Now with even more abstract art.
This commit is contained in:
parent
34def4f660
commit
916aced1be
14 changed files with 600 additions and 388 deletions
|
|
@ -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
291
host/matelight/color.c
Normal 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
21
host/matelight/color.h
Normal 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__
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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__
|
||||
|
|
|
|||
|
|
@ -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
35
host/matelight/genpal.py
Executable 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))
|
||||
|
|
@ -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')
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, :])
|
||||
|
||||
|
|
@ -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)))
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue