Now with even more abstract art.

This commit is contained in:
jaseg 2014-01-03 00:10:20 +01:00
parent 34def4f660
commit 916aced1be
14 changed files with 600 additions and 388 deletions

View file

@ -1,6 +1,6 @@
all: main.c font.c font.h
gcc -std=gnu11 -Wall -lm -o matelight -g -O0 main.c font.c
all: main.c font.c font.h color.c color.h
gcc -std=gnu11 -Wall -lm -o matelight -g -O0 main.c font.c color.c
clean:
rm matelight

291
host/matelight/color.c Normal file
View file

@ -0,0 +1,291 @@
#include "color.h"
#include <stdlib.h>
#include <stdio.h>
int xterm_color_index(color_t c){
int candidate = 0;
int best_distance = 0x7fffffff;
for(int i=0; i<256; i++){
color_t k = colortable[i];
int tmp = abs(c.r - k.r);
tmp *= tmp;
int distance = tmp;
if(distance > best_distance)
continue;
tmp = abs(c.g - k.g);
tmp *= tmp;
distance += tmp;
if(distance > best_distance)
continue;
tmp = abs(c.b - k.b);
tmp *= tmp;
distance += tmp;
if(distance > best_distance)
continue;
best_distance = distance;
candidate = i;
}
return candidate;
}
color_t colortable[256] = {
{0x00, 0x00, 0x00},
{0xa8, 0x00, 0x00},
{0x00, 0xa8, 0x00},
{0xa8, 0x54, 0x00},
{0x00, 0x00, 0xa8},
{0xa8, 0x00, 0xa8},
{0x00, 0xa8, 0xa8},
{0xa8, 0xa8, 0xa8},
{0x54, 0x54, 0x54},
{0xfc, 0x54, 0x54},
{0x54, 0xfc, 0x54},
{0xfc, 0xfc, 0x54},
{0x54, 0x54, 0xfc},
{0xfc, 0x54, 0xfc},
{0x54, 0xfc, 0xfc},
{0xfc, 0xfc, 0xfc},
{0x00, 0x00, 0x00},
{0x00, 0x00, 0x5f},
{0x00, 0x00, 0x87},
{0x00, 0x00, 0xaf},
{0x00, 0x00, 0xd7},
{0x00, 0x00, 0xff},
{0x00, 0x5f, 0x00},
{0x00, 0x5f, 0x5f},
{0x00, 0x5f, 0x87},
{0x00, 0x5f, 0xaf},
{0x00, 0x5f, 0xd7},
{0x00, 0x5f, 0xff},
{0x00, 0x87, 0x00},
{0x00, 0x87, 0x5f},
{0x00, 0x87, 0x87},
{0x00, 0x87, 0xaf},
{0x00, 0x87, 0xd7},
{0x00, 0x87, 0xff},
{0x00, 0xaf, 0x00},
{0x00, 0xaf, 0x5f},
{0x00, 0xaf, 0x87},
{0x00, 0xaf, 0xaf},
{0x00, 0xaf, 0xd7},
{0x00, 0xaf, 0xff},
{0x00, 0xd7, 0x00},
{0x00, 0xd7, 0x5f},
{0x00, 0xd7, 0x87},
{0x00, 0xd7, 0xaf},
{0x00, 0xd7, 0xd7},
{0x00, 0xd7, 0xff},
{0x00, 0xff, 0x00},
{0x00, 0xff, 0x5f},
{0x00, 0xff, 0x87},
{0x00, 0xff, 0xaf},
{0x00, 0xff, 0xd7},
{0x00, 0xff, 0xff},
{0x5f, 0x00, 0x00},
{0x5f, 0x00, 0x5f},
{0x5f, 0x00, 0x87},
{0x5f, 0x00, 0xaf},
{0x5f, 0x00, 0xd7},
{0x5f, 0x00, 0xff},
{0x5f, 0x5f, 0x00},
{0x5f, 0x5f, 0x5f},
{0x5f, 0x5f, 0x87},
{0x5f, 0x5f, 0xaf},
{0x5f, 0x5f, 0xd7},
{0x5f, 0x5f, 0xff},
{0x5f, 0x87, 0x00},
{0x5f, 0x87, 0x5f},
{0x5f, 0x87, 0x87},
{0x5f, 0x87, 0xaf},
{0x5f, 0x87, 0xd7},
{0x5f, 0x87, 0xff},
{0x5f, 0xaf, 0x00},
{0x5f, 0xaf, 0x5f},
{0x5f, 0xaf, 0x87},
{0x5f, 0xaf, 0xaf},
{0x5f, 0xaf, 0xd7},
{0x5f, 0xaf, 0xff},
{0x5f, 0xd7, 0x00},
{0x5f, 0xd7, 0x5f},
{0x5f, 0xd7, 0x87},
{0x5f, 0xd7, 0xaf},
{0x5f, 0xd7, 0xd7},
{0x5f, 0xd7, 0xff},
{0x5f, 0xff, 0x00},
{0x5f, 0xff, 0x5f},
{0x5f, 0xff, 0x87},
{0x5f, 0xff, 0xaf},
{0x5f, 0xff, 0xd7},
{0x5f, 0xff, 0xff},
{0x87, 0x00, 0x00},
{0x87, 0x00, 0x5f},
{0x87, 0x00, 0x87},
{0x87, 0x00, 0xaf},
{0x87, 0x00, 0xd7},
{0x87, 0x00, 0xff},
{0x87, 0x5f, 0x00},
{0x87, 0x5f, 0x5f},
{0x87, 0x5f, 0x87},
{0x87, 0x5f, 0xaf},
{0x87, 0x5f, 0xd7},
{0x87, 0x5f, 0xff},
{0x87, 0x87, 0x00},
{0x87, 0x87, 0x5f},
{0x87, 0x87, 0x87},
{0x87, 0x87, 0xaf},
{0x87, 0x87, 0xd7},
{0x87, 0x87, 0xff},
{0x87, 0xaf, 0x00},
{0x87, 0xaf, 0x5f},
{0x87, 0xaf, 0x87},
{0x87, 0xaf, 0xaf},
{0x87, 0xaf, 0xd7},
{0x87, 0xaf, 0xff},
{0x87, 0xd7, 0x00},
{0x87, 0xd7, 0x5f},
{0x87, 0xd7, 0x87},
{0x87, 0xd7, 0xaf},
{0x87, 0xd7, 0xd7},
{0x87, 0xd7, 0xff},
{0x87, 0xff, 0x00},
{0x87, 0xff, 0x5f},
{0x87, 0xff, 0x87},
{0x87, 0xff, 0xaf},
{0x87, 0xff, 0xd7},
{0x87, 0xff, 0xff},
{0xaf, 0x00, 0x00},
{0xaf, 0x00, 0x5f},
{0xaf, 0x00, 0x87},
{0xaf, 0x00, 0xaf},
{0xaf, 0x00, 0xd7},
{0xaf, 0x00, 0xff},
{0xaf, 0x5f, 0x00},
{0xaf, 0x5f, 0x5f},
{0xaf, 0x5f, 0x87},
{0xaf, 0x5f, 0xaf},
{0xaf, 0x5f, 0xd7},
{0xaf, 0x5f, 0xff},
{0xaf, 0x87, 0x00},
{0xaf, 0x87, 0x5f},
{0xaf, 0x87, 0x87},
{0xaf, 0x87, 0xaf},
{0xaf, 0x87, 0xd7},
{0xaf, 0x87, 0xff},
{0xaf, 0xaf, 0x00},
{0xaf, 0xaf, 0x5f},
{0xaf, 0xaf, 0x87},
{0xaf, 0xaf, 0xaf},
{0xaf, 0xaf, 0xd7},
{0xaf, 0xaf, 0xff},
{0xaf, 0xd7, 0x00},
{0xaf, 0xd7, 0x5f},
{0xaf, 0xd7, 0x87},
{0xaf, 0xd7, 0xaf},
{0xaf, 0xd7, 0xd7},
{0xaf, 0xd7, 0xff},
{0xaf, 0xff, 0x00},
{0xaf, 0xff, 0x5f},
{0xaf, 0xff, 0x87},
{0xaf, 0xff, 0xaf},
{0xaf, 0xff, 0xd7},
{0xaf, 0xff, 0xff},
{0xd7, 0x00, 0x00},
{0xd7, 0x00, 0x5f},
{0xd7, 0x00, 0x87},
{0xd7, 0x00, 0xaf},
{0xd7, 0x00, 0xd7},
{0xd7, 0x00, 0xff},
{0xd7, 0x5f, 0x00},
{0xd7, 0x5f, 0x5f},
{0xd7, 0x5f, 0x87},
{0xd7, 0x5f, 0xaf},
{0xd7, 0x5f, 0xd7},
{0xd7, 0x5f, 0xff},
{0xd7, 0x87, 0x00},
{0xd7, 0x87, 0x5f},
{0xd7, 0x87, 0x87},
{0xd7, 0x87, 0xaf},
{0xd7, 0x87, 0xd7},
{0xd7, 0x87, 0xff},
{0xd7, 0xaf, 0x00},
{0xd7, 0xaf, 0x5f},
{0xd7, 0xaf, 0x87},
{0xd7, 0xaf, 0xaf},
{0xd7, 0xaf, 0xd7},
{0xd7, 0xaf, 0xff},
{0xd7, 0xd7, 0x00},
{0xd7, 0xd7, 0x5f},
{0xd7, 0xd7, 0x87},
{0xd7, 0xd7, 0xaf},
{0xd7, 0xd7, 0xd7},
{0xd7, 0xd7, 0xff},
{0xd7, 0xff, 0x00},
{0xd7, 0xff, 0x5f},
{0xd7, 0xff, 0x87},
{0xd7, 0xff, 0xaf},
{0xd7, 0xff, 0xd7},
{0xd7, 0xff, 0xff},
{0xff, 0x00, 0x00},
{0xff, 0x00, 0x5f},
{0xff, 0x00, 0x87},
{0xff, 0x00, 0xaf},
{0xff, 0x00, 0xd7},
{0xff, 0x00, 0xff},
{0xff, 0x5f, 0x00},
{0xff, 0x5f, 0x5f},
{0xff, 0x5f, 0x87},
{0xff, 0x5f, 0xaf},
{0xff, 0x5f, 0xd7},
{0xff, 0x5f, 0xff},
{0xff, 0x87, 0x00},
{0xff, 0x87, 0x5f},
{0xff, 0x87, 0x87},
{0xff, 0x87, 0xaf},
{0xff, 0x87, 0xd7},
{0xff, 0x87, 0xff},
{0xff, 0xaf, 0x00},
{0xff, 0xaf, 0x5f},
{0xff, 0xaf, 0x87},
{0xff, 0xaf, 0xaf},
{0xff, 0xaf, 0xd7},
{0xff, 0xaf, 0xff},
{0xff, 0xd7, 0x00},
{0xff, 0xd7, 0x5f},
{0xff, 0xd7, 0x87},
{0xff, 0xd7, 0xaf},
{0xff, 0xd7, 0xd7},
{0xff, 0xd7, 0xff},
{0xff, 0xff, 0x00},
{0xff, 0xff, 0x5f},
{0xff, 0xff, 0x87},
{0xff, 0xff, 0xaf},
{0xff, 0xff, 0xd7},
{0xff, 0xff, 0xff},
{0x00, 0x00, 0x00},
{0x12, 0x12, 0x12},
{0x1c, 0x1c, 0x1c},
{0x26, 0x26, 0x26},
{0x30, 0x30, 0x30},
{0x3a, 0x3a, 0x3a},
{0x44, 0x44, 0x44},
{0x4e, 0x4e, 0x4e},
{0x58, 0x58, 0x58},
{0x62, 0x62, 0x62},
{0x6c, 0x6c, 0x6c},
{0x76, 0x76, 0x76},
{0x80, 0x80, 0x80},
{0x8a, 0x8a, 0x8a},
{0x94, 0x94, 0x94},
{0x9e, 0x9e, 0x9e},
{0xa8, 0xa8, 0xa8},
{0xb2, 0xb2, 0xb2},
{0xbc, 0xbc, 0xbc},
{0xc6, 0xc6, 0xc6},
{0xd0, 0xd0, 0xd0},
{0xda, 0xda, 0xda},
{0xe4, 0xe4, 0xe4},
{0xee, 0xee, 0xee}
};

21
host/matelight/color.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef __COLOR_H__
#define __COLOR_H__
#include <stdint.h>
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
} color_t;
int xterm_color_index(color_t c);
// gray
#define DEFAULT_FG_COLOR 7
// black
#define DEFAULT_BG_COLOR 0
extern color_t colortable[256];
#endif//__COLOR_H__

View file

@ -5,7 +5,7 @@
#include <stdlib.h>
#include <string.h>
void render_glyph(glyph_t *g, uint8_t *buf, unsigned int bufwidth, unsigned int offx, unsigned int offy){
void render_glyph(glyph_t *g, uint8_t *buf, unsigned int bufwidth, unsigned int offx, unsigned int offy, color_t fg, color_t bg){
unsigned int bitmap_row_width = g->width/8;
uint8_t *bitmap = ((uint8_t *)g) + sizeof(glyph_t);
for(unsigned int y=0; y < g->height; y++){
@ -16,7 +16,8 @@ void render_glyph(glyph_t *g, uint8_t *buf, unsigned int bufwidth, unsigned int
}
uint8_t *p = buf + (offy+y)*bufwidth + offx;
for(unsigned int x=0; x < g->width; x++){
*p++ = (data&(1<<(g->width-1))) ? 1 : 0;
color_t c = (data&(1<<(g->width-1))) ? fg : bg;
*((color_t *)(p++)) = c;
data <<= 1;
}
}

View file

@ -4,6 +4,7 @@
#include <stdint.h>
#include <stdio.h>
#include "color.h"
// CAUTION: A glyph struct is always followed by the glyph's bitmap.
typedef struct {
@ -16,10 +17,12 @@ typedef struct {
// Size of Unicode's basic multilingual plane
#define BLP_SIZE 65536
#define MAX_CSI_ELEMENTS 8
// We could also use some fancy hashtable here, but unifont includes about 57k glyphs so we would hardly save any memory.
int read_bdf(FILE *f, glyph_t **glyph_table, unsigned int glyph_table_size);
// Requires buf to point to a buffer at least of size glyph->width*glyph->height.
void render_glyph(glyph_t *glyph, uint8_t *buf, unsigned int bufwidth, unsigned int offx, unsigned int offy);
void render_glyph(glyph_t *glyph, uint8_t *buf, unsigned int bufwidth, unsigned int offx, unsigned int offy, color_t fg, color_t bg);
#endif//__FONT_H__

View file

@ -1,5 +0,0 @@
from bdflib import reader as bdfreader # Used to read the bitmap font
FONT = bdfreader.read_bdf(iter(open('fonts/unifont-6.3.20131020.bdf').readlines()))
FONT_HEIGHT = 16

35
host/matelight/genpal.py Executable file
View file

@ -0,0 +1,35 @@
#!/usr/bin/env python3
xterm_colors = [
(0x00, 0x00, 0x00),
(0xa8, 0x00, 0x00),
(0x00, 0xa8, 0x00),
(0xa8, 0x54, 0x00),
(0x00, 0x00, 0xa8),
(0xa8, 0x00, 0xa8),
(0x00, 0xa8, 0xa8),
(0xa8, 0xa8, 0xa8),
(0x54, 0x54, 0x54),
(0xfc, 0x54, 0x54),
(0x54, 0xfc, 0x54),
(0xfc, 0xfc, 0x54),
(0x54, 0x54, 0xfc),
(0xfc, 0x54, 0xfc),
(0x54, 0xfc, 0xfc),
(0xfc, 0xfc, 0xfc)]
# colors 16..232: the 6x6x6 color cube
_valuerange = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff)
for i in range(217):
r = _valuerange[(i // 36) % 6]
g = _valuerange[(i // 6) % 6]
b = _valuerange[i % 6]
xterm_colors.append((r, g, b))
# colors 233..253: grayscale
for i in range(1, 24):
v = 8 + i * 10
xterm_colors.append((v, v, v))
for r,g,b in xterm_colors:
print("\t{{{:#04x}, {:#04x}, {:#04x}}},".format(r,g,b))

View file

@ -1,19 +0,0 @@
import usb
import colorsys
import numpy as np
from config import *
import itertools
dev = usb.core.find(idVendor=0x1cbe, idProduct=0x0003)
def sendframe(framedata):
# not isinstance(framedata, np.array) or
if framedata.shape != (DISPLAY_HEIGHT, DISPLAY_WIDTH, 3) or framedata.dtype != np.uint8:
raise ValueError('framedata must be a ({}, {}, 3)-numpy array of uint8s. Got a {}-numpy array of {}'.format(DISPLAY_HEIGHT, DISPLAY_WIDTH, framedata.shape, framedata.dtype))
for cy, cx in itertools.product(range(CRATES_Y), range(CRATES_X)):
cratedata = framedata[cy*CRATE_HEIGHT:(cy+1)*CRATE_HEIGHT, cx*CRATE_WIDTH:(cx+1)*CRATE_WIDTH]
# Send framebuffer data
dev.write(0x01, bytes([0, cx, cy])+bytes(list(cratedata.flatten())))
# Send latch command
dev.write(0x01, b'\x01')

View file

@ -1,98 +0,0 @@
#!/usr/bin/env python3
from socketserver import *
import socket
import threading
import zlib
import random
import struct
import host
import numpy as np
import time
import sys
import traceback
import renderers
from PIL import Image, ImageSequence
from config import *
# Loading frame (for the big font file)
img = Image.open(open('../nyancat.png', 'rb'))
frame = np.array(img.convert('RGB').getdata(), dtype=np.uint8).reshape((DISPLAY_HEIGHT, DISPLAY_WIDTH, 3))
host.sendframe(frame)
from font import *
UDP_THRES = 1.0
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
default_renderers = [renderers.TextRenderer('\x1B[92mMate Light\x1B[93m@\x1B[92mPlay store or \x1B[94;101mtcp://ml.jaseg.net:1337\x1B[0;91m ♥ '),
renderers.TextRenderer('\x1B[92mMate Light\x1B[0;91m ♥ \x1B[92mUnicode'),
renderers.TextRenderer('\x1B[92mMate Light\x1B[0m powered by \x1B[95mMicrosoft™ \x1B[96mMarquee Manager® Pro')]
global renderer, count
renderer = default_renderers[0]
count = 0
lastudp = 0
class MateLightUDPHandler(BaseRequestHandler):
def handle(self):
try:
global lastudp
data = self.request[0].strip()
if len(data) != FRAME_SIZE+4:
#raise ValueError('Invalid frame size: Expected {}, got {}'.format(FRAME_SIZE+4, len(data)))
return
frame = data[:-4]
crc1, = struct.unpack('!I', data[-4:])
crc2, = zlib.crc32(frame, 0),
#if crc1 != crc2:
# raise ValueError('Invalid frame CRC checksum: Expected {}, got {}'.format(crc2, crc1))
#socket.sendto(b'ACK', self.client_address)
a = np.array(list(frame), dtype=np.uint8)
lastudp = time.time()
host.sendframe(a.reshape((DISPLAY_HEIGHT, DISPLAY_WIDTH, 3)))
except Exception as e:
print('Error receiving UDP frame:', e)
ex_type, ex, tb = sys.exc_info()
traceback.print_tb(tb)
class MateLightTCPTextHandler(BaseRequestHandler):
def handle(self):
try:
data = str(self.request.recv(1024).strip(), 'UTF-8')
if len(data) > 140:
self.request.sendall('TOO MUCH INFORMATION!\n')
return
global renderer, count
print(data+'\x1B[0m')
renderer = renderers.TextRenderer(data)
count = 3
self.request.sendall(b'KTHXBYE!\n')
except:
pass
TCPServer.allow_reuse_address = True
server = TCPServer(('', 1337), MateLightTCPTextHandler)
t = threading.Thread(target=server.serve_forever)
t.daemon = True
t.start()
UDPServer.allow_reuse_address = True
userver = UDPServer(('', 1337), MateLightUDPHandler)
t = threading.Thread(target=userver.serve_forever)
t.daemon = True
t.start()
while True:
global renderer, count, lastudp
foo = renderer
if count == 0:
renderer = random.choice(default_renderers)
else:
count = count - 1
for frame, delay in foo.frames():
#print(list(frame.flatten()))
now = time.time()
if now-lastudp > UDP_THRES:
host.sendframe(np.swapaxes(frame, 0, 1))
else:
time.sleep(0.1)

View file

@ -1,5 +1,6 @@
#include "font.h"
#include "color.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
@ -7,6 +8,7 @@
#include <string.h>
#include <wchar.h>
#include <locale.h>
#include <sys/timeb.h>
/* CAUTION: REQUIRES INPUT TO BE \0-TERMINATED */
int console_render(char *s, glyph_t **glyph_table, unsigned int glyph_table_size){
@ -53,7 +55,8 @@ int console_render(char *s, glyph_t **glyph_table, unsigned int glyph_table_size
// For easier rendering on the terminal, round up to multiples of two
gbufheight += gbufheight&1;
unsigned int gbufsize = gbufwidth*gbufheight;
// gbuf uses 3 bytes per color for r, g and b
unsigned int gbufsize = gbufwidth*3*gbufheight;
gbuf = malloc(gbufsize);
if(gbuf == 0){
fprintf(stderr, "Cannot malloc() %d bytes.\n", gbufsize);
@ -64,31 +67,260 @@ int console_render(char *s, glyph_t **glyph_table, unsigned int glyph_table_size
unsigned int x = 0;
p = s;
memset(&ps, 0, sizeof(mbstate_t));
struct {
color_t fg;
color_t bg;
int blink:4;
int bold:1; // TODO
int underline:1;
int strikethrough:1;
int fraktur:1; // TODO See: Flat10 Fraktur font
int invert:1;
} style = {
colortable[DEFAULT_FG_COLOR], colortable[DEFAULT_BG_COLOR], 0, 0, 0, 0, 0, 0
};
for(;;){
// NOTE: This nested escape sequence parsing does not contain any unicode-awareness whatsoever
if(*p == '\033'){ // Escape sequence YAY
char *sequence_start = p++;
if(*p == '['){ // This was a CSI!
long elems[MAX_CSI_ELEMENTS];
int nelems;
for(nelems = 0; nelems<MAX_CSI_ELEMENTS; nelems++){
p++;
char *endptr;
elems[nelems] = strtol(p, &endptr, 10);
if(p == endptr){
fprintf(stderr, "Invalid ANSI escape code: \"\\e%s\"\n", sequence_start);
goto error;
}
if(*endptr == 'm')
break;
if(*endptr != ';'){
fprintf(stderr, "Invalid ANSI escape code: \"\\e%s\"\n", sequence_start);
goto error;
}
}
// By now we know it's a SGR since we error'ed out on anything else
if(nelems < 1){
fprintf(stderr, "Unsupported escape sequence: \"\\e%s\"\n", sequence_start);
goto error;
}
for(int i=0; i<nelems; i++){
switch(elems[i]){
case 0: // reset style
style.fg = colortable[DEFAULT_FG_COLOR];
style.bg = colortable[DEFAULT_BG_COLOR];
style.bold = 0;
style.underline = 0;
style.blink = 0;
style.strikethrough = 0;
style.fraktur = 0;
style.invert = 0;
break;
case 1: // bold
style.bold = 1;
break;
case 4: // underline
style.underline = 1;
break;
case 5: // slow blink
style.blink = 1;
break;
case 6: // rapid blink
style.blink = 8;
break;
case 7: // color invert on
style.invert = 1;
break;
case 9: // strike-through
style.strikethrough = 1;
break;
case 20:// Fraktur
style.fraktur = 1;
break;
case 22:// Bold off
style.bold = 0;
break;
case 24:// Underline off
style.underline = 0;
break;
case 25:// Blink off
style.blink = 0;
break;
case 27:// color invert off
style.invert = 0;
break;
case 29:// strike-through off
style.strikethrough = 0;
break;
case 30: // Set foreground color, "dim" colors
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
style.fg = colortable[elems[i]-30];
break;
case 38: // Set xterm-256 foreground color
i++;
if(nelems-i < 2 || elems[i] != 5){
fprintf(stderr, "Invalid ANSI escape code: \"\\e%s\"\n", sequence_start);
goto error;
}
style.fg = colortable[elems[i++]];
break;
case 39: // Reset foreground color to default
style.bg = colortable[DEFAULT_FG_COLOR];
break;
case 40: // Set background color, "dim" colors
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
style.bg = colortable[elems[i]-40];
break;
case 48: // Set xterm-256 background color
i++;
if(nelems-i < 2 || elems[i] != 5){
fprintf(stderr, "Invalid ANSI escape code: \"\\e%s\"\n", sequence_start);
goto error;
}
style.bg = colortable[elems[i++]];
break;
case 49: // Reset background color to default
style.bg = colortable[DEFAULT_BG_COLOR];
break;
case 90: // Set foreground color, "bright" colors
case 91:
case 92:
case 93:
case 94:
case 95:
case 96:
case 97:
style.fg = colortable[elems[i]-90+8];
break;
case 100: // Set background color, "bright" colors
case 101:
case 102:
case 103:
case 104:
case 105:
case 106:
case 107:
style.bg = colortable[elems[i]-100+8];
break;
default:
fprintf(stderr, "Unsupported escape sequence: \"\\e%s\"\n", sequence_start);
goto error;
}
}
}else{
fprintf(stderr, "Unsupported escape sequence: \"\\e%s\"\n", sequence_start);
goto error;
}
continue;
}
size_t inc = mbrtowc(&c, p, (s+len+1)-p, NULL);
// If p contained
if(inc == 0) // Reached end of string
break;
p += inc;
struct timeb time = {0};
ftime(&time);
unsigned long int t = time.time*1000 + time.millitm;
int blink = style.blink && (t % (1000/style.blink) < (333/style.blink));
int inv = style.invert ^ blink;
color_t fg = inv ? style.fg : style.bg;
color_t bg = inv ? style.bg : style.fg;
printf("Rendering glyph %lc: Strikethrough %d Underline %d Blink %d Inverted %d Bold %d Fraktur %d FG (%d, %d, %d) BG (%d, %d, %d)\n",
c,
style.strikethrough,
style.underline,
style.blink,
style.invert,
style.bold,
style.fraktur,
style.fg.r,
style.fg.g,
style.fg.b,
style.bg.r,
style.bg.g,
style.bg.b);
glyph_t *g = glyph_table[c];
render_glyph(g, gbuf, gbufwidth, x, 0);
render_glyph(g, gbuf, gbufwidth, x, 0, fg, bg);
if(style.strikethrough || style.underline){
int sty = gbufheight/2;
// g->y usually is a negative index of the glyph's baseline measured from the glyph's bottom
int uly = gbufheight + g->y;
for(int i=0; i<g->width; i++){
if(style.strikethrough)
*((color_t *)(gbuf + (sty*gbufwidth + i)*3)) = fg;
if(style.underline)
*((color_t *)(gbuf + (uly*gbufwidth + i)*3)) = fg;
}
}
x += g->width;
}
for(unsigned int y=0; y < gbufheight; y++){
for(unsigned int x=0; x < gbufwidth; x++){
color_t c = *((color_t *)(gbuf + (y*gbufwidth + x)*3));
//printf("\033[0m%02x,%02x,%02x-%02x\033[38;5;%dm█", c.r, c.g, c.b, xterm_color_index(c), xterm_color_index(c));
printf("\033[38;5;%dm█", xterm_color_index(c));
}
printf("\n");
}
return 0;
color_t lastfg = {0, 0, 0}, lastbg = {0, 0, 0};
printf("\e[38;5;0;48;5;0m");
for(unsigned int y=0; y < gbufheight; y+=2){
for(unsigned int x=0; x < gbufwidth; x++){
//Da magicks: ▀█▄
char c1 = gbuf[y*gbufwidth + x];
char c2 = gbuf[(y+1)*gbufwidth + x];
if(c1 && c2)
printf("");
else if(c1 && !c2)
printf("");
else if(!c1 && c2)
printf("");
else
printf(" ");
color_t ct = *((color_t *)(gbuf + (y*gbufwidth + x)*3)); // Top pixel
color_t cb = *((color_t *)(gbuf + ((y+1)*gbufwidth + x)*3)); // Bottom pixel
if(!memcmp(&ct, &lastfg, sizeof(color_t))){
if(!memcmp(&cb, &lastbg, sizeof(color_t))){
printf("");
}else if(!memcmp(&cb, &lastfg, sizeof(color_t))){
printf("");
}else{
printf("\033[48;5;%dm▀", xterm_color_index(cb));
lastbg = cb;
}
}else if(!memcmp(&ct, &lastbg, sizeof(color_t))){
if(!memcmp(&cb, &lastfg, sizeof(color_t))){
printf("");
}else if(!memcmp(&cb, &lastbg, sizeof(color_t))){
printf(" ");
}else{
printf("\033[38;5;%dm▄", xterm_color_index(cb));
lastfg = cb;
}
}else{ // No matches for the upper pixel
if(!memcmp(&cb, &lastfg, sizeof(color_t))){
printf("\033[48;5;%dm▄", xterm_color_index(ct));
lastbg = ct;
}else if(!memcmp(&cb, &lastbg, sizeof(color_t))){
printf("\033[38;5;%dm▀", xterm_color_index(ct));
lastfg = ct;
}else{
printf("\033[38;5;%d;48;5;%dm▀", xterm_color_index(ct), xterm_color_index(cb));
lastfg = ct;
lastbg = cb;
}
}
}
printf("\n");
}

View file

@ -1,29 +0,0 @@
#!/usr/bin/env python3
import host
import numpy as np
from config import *
from PIL import Image, ImageSequence
import time
img1 = Image.open(open('../nyancat.png', 'rb'))
img2 = Image.open(open('../nyancat2.png', 'rb'))
scroller = Image.open(open('../scroller.png', 'rb'))
datas = []
for img in [img1, img2]:
im = img.convert("RGB")
im.thumbnail((DISPLAY_WIDTH, DISPLAY_HEIGHT), Image.NEAREST)
data = np.array(im.getdata(), dtype=np.uint8)
datas += [data.reshape((DISPLAY_HEIGHT, DISPLAY_WIDTH, 3))]
im = scroller.convert("RGB")
bar = np.array(im.getdata(), dtype=np.uint8)
foo = bar.reshape((DISPLAY_HEIGHT, 300, 3))
while True:
for i in range(60):
for data in datas:
host.sendframe(data)
time.sleep(0.1)
for i in range(260):
host.sendframe(foo[:, i:i+40, :])

View file

@ -1,52 +0,0 @@
#!/usr/bin/env python3
from renderers import TextRenderer, ImageRenderer
import host, config
import time, math
score = lambda now, last, lifetime, priority, item: priority*math.log(now-last)/(10.0+item.duration)
class FuzzyQueue:
def __init__(self, default):
self._default = default
self.put(default, 0.0, 0)
self._l = []
def put(self, item, priority=1.0, lifetime=0):
lifetime += time.time()
self._l.append((0, lifetime, priority, item))
def pop(self):
""" Get an item from the queue
NOTE: This is *not* a regular pop, as it does not necessarily remove the item from the queue.
"""
now = time.time()
# Choose item based on last used and priority
_, index, (_, lifetime, priority, item) = max(sorted([(score(now, *v), i, v) for i, v in self._l]))
# If item's lifetime is exceeded, remove
if lifetime < now and item is not self._default:
del self._l[index]
# Otherwise, set item's last played time
self._l[index] = (now, lifetime, prioity, item)
# Finally, return
return item
q = FuzzyQueue()
def insert_text(text, priority=1.0, lifetime=0, escapes=True):
q.put(TextRenderer(text, escapes), priority, lifetime)
def insert_image(image, priority=1.0, lifetime=0):
q.put(ImageRenderer(image), priority, lifetime)
def render_thread():
while True:
start = time.time()
for frame, delay in q.pop().frames(start):
then = time.time()
if then-start+delay > RENDERER_TIMEOUT:
break
sendframe(frame)
now = time.time()
time.sleep(min(RENDERER_TIMEOUT, delay - (now-then)))

View file

@ -1,154 +0,0 @@
import time
try:
import re2 as re
except ImportError:
import re
import numpy as np
from PIL import Image
from pixelterm import xtermcolors
from config import *
from font import *
default_palette = [
(0x00, 0x00, 0x00), # 0 normal colors
(0xcd, 0x00, 0x00), # 1
(0x00, 0xcd, 0x00), # 2
(0xcd, 0xcd, 0x00), # 3
(0x00, 0x00, 0xee), # 4
(0xcd, 0x00, 0xcd), # 5
(0x00, 0xcd, 0xcd), # 6
(0xe5, 0xe5, 0xe5), # 7
(0x7f, 0x7f, 0x7f), # 8 bright colors
(0xff, 0x00, 0x00), # 9
(0x00, 0xff, 0x00), # 10
(0xff, 0xff, 0x00), # 11
(0x5c, 0x5c, 0xff), # 12
(0xff, 0x00, 0xff), # 13
(0x00, 0xff, 0xff), # 14
(0xff, 0xff, 0xff)] # 15
default_colors = (default_palette[8], default_palette[0])
class CharGenerator:
def __init__(self, seq=None, lg=None, text=''):
settings = False, False, False, default_colors
if lg:
settings = lg.bold, lg.blink, lg.underscore, (lg.fg, lg.bg)
self.bold, self.blink, self.underscore, (self.fg, self.bg) = settings
self.text = text
if seq:
self.parse_escape_sequence(seq)
def parse_escape_sequence(self, seq):
codes = list(map(int, seq[2:-1].split(';')))
fg, bg, reverse, i = self.fg, self.bg, False, 0
while i<len(codes):
a = codes[i]
if a in [38, 48]:
if codes[i+1] == 5:
c = xtermcolors.xterm_colors[codes[i+2]]
fg, bg = (c, bg) if a == 38 else (fg, c)
i += 2
elif a == 39:
fg = (0,0,0)
elif a == 49:
bg = (0,0,0)
elif a == 0:
fg, bg = default_colors
self.bold, self.blink, self.underscore = False, False, False
elif a in range(30, 38):
fg = default_palette[a-30]
elif a in range(90, 98):
fg = default_palette[a-90+8]
elif a in range(40, 48):
bg = default_palette[a-40]
elif a in range(101, 108):
bg = default_palette[a-100+8]
elif a == 7:
reverse = True
elif a == 5:
self.blink = True
elif a == 4:
self.underscore = True
elif a == 1: # Literally "bright", not bold.
self.bold = True
i += 1
fg, bg = (bg, fg) if reverse else (fg, bg)
self.fg, self.bg = fg, bg
def generate_char(self, c, now):
fg, bg = (self.bg, self.fg) if self.blink and now%1.0 < 0.3 else (self.fg, self.bg)
glyph = FONT.glyphs_by_codepoint[ord(c)]
# Please forgive the string manipulation below.
lookup = {'0': bg, '1': fg}
FONT_PADDED_BINARY = ('{:0'+str(glyph.bbW)+'b}').format
FONT_Y_PAD = [[bg]*glyph.bbW]*(DISPLAY_HEIGHT-FONT_HEIGHT)
return np.swapaxes(np.array([ list(map(lookup.get, FONT_PADDED_BINARY(int(row, 16))[:glyph.bbW])) for row in glyph.get_data() ] + FONT_Y_PAD, dtype=np.uint8), 0, 1)
def generate(self, now):
chars = [self.generate_char(c, now) for c in self.text]
# This refers to inter-letter spacing
space = np.zeros((LETTER_SPACING, DISPLAY_HEIGHT, 3), dtype=np.uint8)
spaces = [space]*(len(chars)-1)
everything = chars + spaces
everything[::2] = chars
everything[1::2] = spaces
return np.concatenate(everything)
class TextRenderer:
def __init__(self, text, escapes=True):
"""Renders text into a frame buffer
"escapes" tells the renderer whether to interpret escape sequences (True) or not (False).
"""
generators = []
current_generator = CharGenerator()
for match in re.finditer('(\x1B\[[0-9;]+m)|(.)', text):
esc, char = match.groups()
if esc:
if current_generator.text != '':
generators.append(current_generator)
current_generator = CharGenerator(esc, current_generator)
elif char:
current_generator.text += char
generators = generators + [current_generator]
# Generate the actual frame buffer
zeros = [np.zeros((DISPLAY_WIDTH, DISPLAY_HEIGHT, 3), dtype=np.uint8)]
# Pad the array with one screen's worth of zeros on both sides so the text fully scrolls through.
now = time.time()
self.raw = np.concatenate(zeros+[g.generate(now) for g in generators]+zeros)
def frames(self):
w,h,_ = self.raw.shape
for i in range(0, w-DISPLAY_WIDTH, 2):
frame = self.raw[i:i+DISPLAY_WIDTH, :, :]
yield frame, 1/DEFAULT_SCROLL_SPEED
class ImageRenderer:
def __new__(cls, image_data):
img = Image.open(io.BytesIO(image_data))
self.img = img
def frames(self):
img = self.img
palette = img.getpalette()
last_frame = Image.new("RGB", img.size)
# FIXME set delay to 1/10s if the image is animated, only use DEFAULT_IMAGE_DURATION for static images.
delay = img.info.get('duration', DEFAULT_IMAGE_DURATION*1000.0)/1000.0
for frame in ImageSequence.Iterator(img):
#This works around a known bug in Pillow
#See also: http://stackoverflow.com/questions/4904940/python-converting-gif-frames-to-png
frame.putpalette(palette)
c = frame.convert("RGB")
if img.info['background'] != img.info['transparency']:
last_frame.paste(c, c)
else:
last_frame = c
im = last_frame.copy()
im.thumbnail((DISPLAY_WIDTH, DISPLAY_HEIGHT), Image.NEAREST)
data = np.array(im.getdata(), dtype=np.int8)
data.reshape((DISPLAY_WIDTH, DISPLAY_HEIGHT, 3))
yield data, delay

View file

@ -1,14 +0,0 @@
#!/usr/bin/env python3
import host
import numpy as np
from config import *
import time
import renderers
while True:
renderer = renderers.TextRenderer('\x1B[91mThe \x1B[92;105mquick\x1B[0m brown \x1B[96;5mfox jumps over\x1B[0m the lazy dog.')
for frame, delay in renderer.frames():
#print(list(frame.flatten()))
host.sendframe(np.swapaxes(frame, 0, 1))
#time.sleep(delay)