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
|
all: main.c font.c font.h color.c color.h
|
||||||
gcc -std=gnu11 -Wall -lm -o matelight -g -O0 main.c font.c
|
gcc -std=gnu11 -Wall -lm -o matelight -g -O0 main.c font.c color.c
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm matelight
|
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 <stdlib.h>
|
||||||
#include <string.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;
|
unsigned int bitmap_row_width = g->width/8;
|
||||||
uint8_t *bitmap = ((uint8_t *)g) + sizeof(glyph_t);
|
uint8_t *bitmap = ((uint8_t *)g) + sizeof(glyph_t);
|
||||||
for(unsigned int y=0; y < g->height; y++){
|
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;
|
uint8_t *p = buf + (offy+y)*bufwidth + offx;
|
||||||
for(unsigned int x=0; x < g->width; x++){
|
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;
|
data <<= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include "color.h"
|
||||||
|
|
||||||
// CAUTION: A glyph struct is always followed by the glyph's bitmap.
|
// CAUTION: A glyph struct is always followed by the glyph's bitmap.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -16,10 +17,12 @@ typedef struct {
|
||||||
// Size of Unicode's basic multilingual plane
|
// Size of Unicode's basic multilingual plane
|
||||||
#define BLP_SIZE 65536
|
#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.
|
// 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);
|
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.
|
// 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__
|
#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 "font.h"
|
||||||
|
#include "color.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -7,6 +8,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
|
#include <sys/timeb.h>
|
||||||
|
|
||||||
/* CAUTION: REQUIRES INPUT TO BE \0-TERMINATED */
|
/* CAUTION: REQUIRES INPUT TO BE \0-TERMINATED */
|
||||||
int console_render(char *s, glyph_t **glyph_table, unsigned int glyph_table_size){
|
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
|
// For easier rendering on the terminal, round up to multiples of two
|
||||||
gbufheight += gbufheight&1;
|
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);
|
gbuf = malloc(gbufsize);
|
||||||
if(gbuf == 0){
|
if(gbuf == 0){
|
||||||
fprintf(stderr, "Cannot malloc() %d bytes.\n", gbufsize);
|
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;
|
unsigned int x = 0;
|
||||||
p = s;
|
p = s;
|
||||||
memset(&ps, 0, sizeof(mbstate_t));
|
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(;;){
|
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);
|
size_t inc = mbrtowc(&c, p, (s+len+1)-p, NULL);
|
||||||
// If p contained
|
// If p contained
|
||||||
if(inc == 0) // Reached end of string
|
if(inc == 0) // Reached end of string
|
||||||
break;
|
break;
|
||||||
p += inc;
|
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];
|
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;
|
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 y=0; y < gbufheight; y+=2){
|
||||||
for(unsigned int x=0; x < gbufwidth; x++){
|
for(unsigned int x=0; x < gbufwidth; x++){
|
||||||
//Da magicks: ▀█▄
|
//Da magicks: ▀█▄
|
||||||
char c1 = gbuf[y*gbufwidth + x];
|
color_t ct = *((color_t *)(gbuf + (y*gbufwidth + x)*3)); // Top pixel
|
||||||
char c2 = gbuf[(y+1)*gbufwidth + x];
|
color_t cb = *((color_t *)(gbuf + ((y+1)*gbufwidth + x)*3)); // Bottom pixel
|
||||||
if(c1 && c2)
|
if(!memcmp(&ct, &lastfg, sizeof(color_t))){
|
||||||
printf("█");
|
if(!memcmp(&cb, &lastbg, sizeof(color_t))){
|
||||||
else if(c1 && !c2)
|
printf("▀");
|
||||||
printf("▀");
|
}else if(!memcmp(&cb, &lastfg, sizeof(color_t))){
|
||||||
else if(!c1 && c2)
|
printf("█");
|
||||||
printf("▄");
|
}else{
|
||||||
else
|
printf("\033[48;5;%dm▀", xterm_color_index(cb));
|
||||||
printf(" ");
|
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");
|
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