This commit is contained in:
jaseg 2016-05-27 00:48:52 +02:00
parent b4bc8afe0a
commit 16f41a5298
3 changed files with 146 additions and 16 deletions

11
Makefile Normal file
View 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

View file

@ -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
View 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);
}