frob
This commit is contained in:
parent
b4bc8afe0a
commit
16f41a5298
3 changed files with 146 additions and 16 deletions
11
Makefile
Normal file
11
Makefile
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
all: pixelflut.so
|
||||||
|
|
||||||
|
|
||||||
|
pixelflut.so: pixelflut.c
|
||||||
|
gcc -o $@ -shared -fPIC -Wall -Wpedantic -Wstrict-aliasing $<
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -f pixelflut.so
|
||||||
79
clippy.py
79
clippy.py
|
|
@ -5,12 +5,13 @@ import random
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
import numpy
|
import numpy as np
|
||||||
import bz2
|
import bz2
|
||||||
import os
|
import os
|
||||||
import functools
|
import functools
|
||||||
import contextlib
|
import contextlib
|
||||||
import math
|
import math
|
||||||
|
import ctypes
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
|
@ -19,7 +20,7 @@ from pixelterm import pixelterm
|
||||||
HOST, PORT = "172.23.42.29",2342
|
HOST, PORT = "172.23.42.29",2342
|
||||||
CMD_LED_DRAW = 18
|
CMD_LED_DRAW = 18
|
||||||
|
|
||||||
def resize_image(img, size):
|
def resize_image(img, size, blackbg=True):
|
||||||
tw, th = size
|
tw, th = size
|
||||||
w, h = img.size
|
w, h = img.size
|
||||||
a, b = w/tw, h/th
|
a, b = w/tw, h/th
|
||||||
|
|
@ -27,8 +28,11 @@ def resize_image(img, size):
|
||||||
pos = int((tw-w*f)/2), int((th-h*f)/2)
|
pos = int((tw-w*f)/2), int((th-h*f)/2)
|
||||||
buf = Image.new('RGBA', (tw, th))
|
buf = Image.new('RGBA', (tw, th))
|
||||||
buf.paste(img.resize((int(w*f), int(h*f))).convert('RGBA'), pos)
|
buf.paste(img.resize((int(w*f), int(h*f))).convert('RGBA'), pos)
|
||||||
buf2 = Image.new('RGBA', (tw, th), (0, 0, 0, 255))
|
if blackbg:
|
||||||
return Image.alpha_composite(buf2, buf)
|
buf2 = Image.new('RGBA', (tw, th), (0, 0, 0, 255))
|
||||||
|
return Image.alpha_composite(buf2, buf)
|
||||||
|
else:
|
||||||
|
return buf
|
||||||
|
|
||||||
class Display:
|
class Display:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
@ -53,7 +57,29 @@ class Display:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def encode_image(img, displaysize):
|
def encode_image(img, displaysize):
|
||||||
return numpy.frombuffer(Display.do_gamma(resize_image(img, displaysize), 0.5).convert('1').tobytes(), dtype='1b')
|
return np.frombuffer(Display.do_gamma(resize_image(img, displaysize), 0.5).convert('1').tobytes(), dtype='1b')
|
||||||
|
|
||||||
|
class Pixelflut:
|
||||||
|
def __init__(self, host, port, x, y, w, h):
|
||||||
|
self.host, self.port = host.encode(), port
|
||||||
|
self.x, self.y = x, y
|
||||||
|
self.w, self.h = w, h
|
||||||
|
self.dbuf = np.zeros(w*h*4, dtype=np.uint8)
|
||||||
|
self.so = ctypes.CDLL('./pixelflut.so')
|
||||||
|
self.sock = None
|
||||||
|
|
||||||
|
def sendframe(self, frame):
|
||||||
|
np.copyto(self.dbuf, frame)
|
||||||
|
cptr = self.dbuf.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8))
|
||||||
|
if self.sock is None:
|
||||||
|
while self.sock is None or self.sock < 0:
|
||||||
|
time.sleep(1)
|
||||||
|
self.sock = self.so.cct(self.host, self.port)
|
||||||
|
if self.so.sendframe(self.sock, cptr, self.w, self.h, self.x, self.y):
|
||||||
|
self.so.discct(self.sock)
|
||||||
|
|
||||||
|
def encode_image(self, img):
|
||||||
|
return np.array(resize_image(img, (self.w, self.h), blackbg=False)).reshape(self.w*self.h*4)
|
||||||
|
|
||||||
def weightedChoice(choices, default=None):
|
def weightedChoice(choices, default=None):
|
||||||
acc = 0
|
acc = 0
|
||||||
|
|
@ -81,14 +107,15 @@ class Agent:
|
||||||
self.picmap = Image.open(path / 'map.png')
|
self.picmap = Image.open(path / 'map.png')
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
def __call__(self, action):
|
def __call__(self, action, sleep=True):
|
||||||
for frame in self._animate(action):
|
for frame in self._animate(action):
|
||||||
# print('frame:', frame)
|
# print('frame:', frame)
|
||||||
if 'images_encoded' in frame: # some frames contain branch info and sound, but no images
|
if 'images_encoded' in frame: # some frames contain branch info and sound, but no images
|
||||||
yield frame['images_encoded']
|
yield frame['images_encoded']
|
||||||
time.sleep(frame['duration']/1000)
|
if sleep:
|
||||||
|
time.sleep(frame['duration']/1000)
|
||||||
|
|
||||||
def precalculate_images(self, dsp, termsize):
|
def precalculate_images(self, pf, dsp, termsize):
|
||||||
print('\033[93mPrecalculating images\033[0m')
|
print('\033[93mPrecalculating images\033[0m')
|
||||||
total = sum(1 for ani in self.config['animations'].values() for f in ani['frames'] if 'images' in f)
|
total = sum(1 for ani in self.config['animations'].values() for f in ani['frames'] if 'images' in f)
|
||||||
i = 0
|
i = 0
|
||||||
|
|
@ -97,15 +124,16 @@ class Agent:
|
||||||
if 'images' in f:
|
if 'images' in f:
|
||||||
print(('(\033[38;5;245m{: '+str(1+int(math.log10(total)))+'}/{}\033[0m) ').format(i, total), end='')
|
print(('(\033[38;5;245m{: '+str(1+int(math.log10(total)))+'}/{}\033[0m) ').format(i, total), end='')
|
||||||
i += 1
|
i += 1
|
||||||
f['images_encoded'] = self._precalculate_one_image(tuple(f['images'][0]), dsp, termsize)
|
f['images_encoded'] = self._precalculate_one_image(tuple(f['images'][0]), pf, dsp, termsize)
|
||||||
print()
|
print()
|
||||||
print('\033[93mdone.\033[0m')
|
print('\033[93mdone.\033[0m')
|
||||||
self._precalculate_one_image.cache_clear()
|
self._precalculate_one_image.cache_clear()
|
||||||
|
|
||||||
@functools.lru_cache(maxsize=None)
|
@functools.lru_cache(maxsize=None)
|
||||||
def _precalculate_one_image(self, coords, dsp, termsize):
|
def _precalculate_one_image(self, coords, pf, dsp, termsize):
|
||||||
img = self._get_image(*coords)
|
img = self._get_image(*coords)
|
||||||
return ( dsp.encode_image(img, dsp.size) if dsp else None,
|
return ( pf.encode_image(img) if pf else None,
|
||||||
|
dsp.encode_image(img, dsp.size) if dsp else None,
|
||||||
pixelterm.termify_pixels(resize_image(img, termsize)) if termsize else None )
|
pixelterm.termify_pixels(resize_image(img, termsize)) if termsize else None )
|
||||||
|
|
||||||
def _animate(self, action):
|
def _animate(self, action):
|
||||||
|
|
@ -132,9 +160,11 @@ if __name__ == '__main__':
|
||||||
parser.add_argument('-a', '--agent', default='Clippy')
|
parser.add_argument('-a', '--agent', default='Clippy')
|
||||||
parser.add_argument('-e', '--endless', action='store_true')
|
parser.add_argument('-e', '--endless', action='store_true')
|
||||||
parser.add_argument('-d', '--display', action='store_true')
|
parser.add_argument('-d', '--display', action='store_true')
|
||||||
|
parser.add_argument('-p', '--pixelflut', type=str, default='94.45.232.225:1234')
|
||||||
parser.add_argument('-t', '--terminal', action='store_true')
|
parser.add_argument('-t', '--terminal', action='store_true')
|
||||||
parser.add_argument('-x', '--termsize', type=str)
|
parser.add_argument('-x', '--termsize', type=str)
|
||||||
parser.add_argument('-s', '--socket', action='store_true')
|
parser.add_argument('-s', '--socket', action='store_true')
|
||||||
|
parser.add_argument('-n', '--nosleep', action='store_true')
|
||||||
parser.add_argument('-b', '--bind', type=str, default='0.0.0.0:2342')
|
parser.add_argument('-b', '--bind', type=str, default='0.0.0.0:2342')
|
||||||
parser.add_argument('action', default='Greeting', nargs='?')
|
parser.add_argument('action', default='Greeting', nargs='?')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
@ -149,6 +179,17 @@ if __name__ == '__main__':
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
dsp = Display() if args.display else None
|
dsp = Display() if args.display else None
|
||||||
|
|
||||||
|
if args.pixelflut:
|
||||||
|
target, *params = args.pixelflut.split('@')
|
||||||
|
host, port = target.split(':')
|
||||||
|
port = int(port)
|
||||||
|
x, y, *_r = params[0].split(',') if params else (0, 0, None)
|
||||||
|
w, h = _r if _r else (320, 240)
|
||||||
|
x, y, w, h = map(int, (x, y, w, h))
|
||||||
|
pf = Pixelflut(host, port, x, y, w, h) if args.pixelflut else None
|
||||||
|
else:
|
||||||
|
pf = None
|
||||||
agent = Agent(agent_path)
|
agent = Agent(agent_path)
|
||||||
if args.socket:
|
if args.socket:
|
||||||
tx, ty = (args.termsize or '60x30').split('x')
|
tx, ty = (args.termsize or '60x30').split('x')
|
||||||
|
|
@ -157,7 +198,7 @@ if __name__ == '__main__':
|
||||||
tx, ty = args.termsize.split('x') or os.get_terminal_size()
|
tx, ty = args.termsize.split('x') or os.get_terminal_size()
|
||||||
tx, ty = int(tx), int(ty)
|
tx, ty = int(tx), int(ty)
|
||||||
termsize = (tx, ty*2) if args.terminal or args.socket else None
|
termsize = (tx, ty*2) if args.terminal or args.socket else None
|
||||||
agent.precalculate_images(dsp, termsize)
|
agent.precalculate_images(pf, dsp, termsize)
|
||||||
|
|
||||||
if args.socket:
|
if args.socket:
|
||||||
import socketserver
|
import socketserver
|
||||||
|
|
@ -169,7 +210,7 @@ if __name__ == '__main__':
|
||||||
while True:
|
while True:
|
||||||
action = random.choice(agent.animations)
|
action = random.choice(agent.animations)
|
||||||
print('[\033[38;5;245m{}\033[0m] Playing: {}'.format(self.client_address[0], action))
|
print('[\033[38;5;245m{}\033[0m] Playing: {}'.format(self.client_address[0], action))
|
||||||
for _img_dsp, img_term in agent(action):
|
for _img_pf, _img_dsp, img_term in agent(action):
|
||||||
self.request.sendall(b'\033[H'+img_term.encode())
|
self.request.sendall(b'\033[H'+img_term.encode())
|
||||||
host, port = args.bind.split(':')
|
host, port = args.bind.split(':')
|
||||||
port = int(port)
|
port = int(port)
|
||||||
|
|
@ -180,16 +221,22 @@ if __name__ == '__main__':
|
||||||
if random.random() > 0.2:
|
if random.random() > 0.2:
|
||||||
action = random.choice(agent.animations)
|
action = random.choice(agent.animations)
|
||||||
print('Playing:', action)
|
print('Playing:', action)
|
||||||
for img_dsp, img_term in agent(action):
|
for img_pf, img_dsp, img_term in agent(action, not args.nosleep):
|
||||||
if args.terminal:
|
if args.terminal:
|
||||||
print('\033[H'+img_term)
|
print('\033[H'+img_term)
|
||||||
if args.display:
|
if args.display:
|
||||||
dsp.sendframe(img_dsp)
|
dsp.sendframe(img_dsp)
|
||||||
time.sleep(1)
|
if args.pixelflut:
|
||||||
|
pf.sendframe(img_pf)
|
||||||
|
if not args.nosleep:
|
||||||
|
time.sleep(1)
|
||||||
else:
|
else:
|
||||||
for img_dsp, img_term in agent(args.action):
|
for img_pf, img_dsp, img_term in agent(args.action, not args.nosleep):
|
||||||
if args.terminal:
|
if args.terminal:
|
||||||
print(pixelterm.termify_pixels(
|
print(pixelterm.termify_pixels(
|
||||||
resize_image(img, termsize)))
|
resize_image(img, termsize)))
|
||||||
if args.display:
|
if args.display:
|
||||||
dsp.sendframe(img_dsp)
|
dsp.sendframe(img_dsp)
|
||||||
|
if args.pixelflut:
|
||||||
|
pf.sendframe(img_pf)
|
||||||
|
|
||||||
|
|
|
||||||
72
pixelflut.c
Normal file
72
pixelflut.c
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#define PIXEL_FORMAT "PX %zd %zd %02x%02x%02x\n"
|
||||||
|
int cct(const char *target, int port) {
|
||||||
|
printf("Reconnecting %s:%d\n", target, port);
|
||||||
|
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (!sockfd) {
|
||||||
|
fprintf(stderr, "No sockfd.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in serv_addr;
|
||||||
|
memset(&serv_addr, 0, sizeof(serv_addr));
|
||||||
|
serv_addr.sin_family = AF_INET;
|
||||||
|
serv_addr.sin_port = htons(port);
|
||||||
|
if (inet_pton(AF_INET, target, &serv_addr.sin_addr) != 1) {
|
||||||
|
fprintf(stderr, "Address error. \"%s\"\n", target);
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) {
|
||||||
|
fprintf(stderr, "Connect error.\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sockfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sendframe(int fd, uint8_t *img, int w, int h, int ox, int oy) {
|
||||||
|
static unsigned long fcnt=0;
|
||||||
|
printf("frame %lu %dx%d @pos %dx%d\n", fcnt++, w, h, ox, oy);
|
||||||
|
int fmtlen = snprintf(NULL, 0, PIXEL_FORMAT, (size_t)1000, (size_t)1000, 0xff, 0xff, 0xff);
|
||||||
|
char *out = malloc(1400);
|
||||||
|
if (!out) {
|
||||||
|
fprintf(stderr, "Malloc error.\n");
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
char *p = out;
|
||||||
|
for (size_t x=0; x<w; x++) {
|
||||||
|
for (size_t y=0; y<h; y++) {
|
||||||
|
uint8_t *px = img + (y*w + x)*4;
|
||||||
|
uint8_t r = px[0], g = px[1], b = px[2], a = px[3];
|
||||||
|
if (a != 255)
|
||||||
|
continue;
|
||||||
|
size_t cx = ox+x, cy = oy+y;
|
||||||
|
p += snprintf(p, fmtlen+1, PIXEL_FORMAT, cx, cy, r, g, b);
|
||||||
|
if (p-out > 1400-fmtlen-1) {
|
||||||
|
if (send(fd, out, p-out, 0) < 0) {
|
||||||
|
fprintf(stderr, "Send error. %d %s\n", errno, strerror(errno));
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
p = out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(out);
|
||||||
|
usleep(1000);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void discct(int fd) {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue