Compare commits
26 Commits
Author | SHA1 | Date |
---|---|---|
Casey | c3b5d1a198 | |
Casey | 743435200d | |
Casey | c45e9f88bc | |
Casey | d0cf362c1b | |
Casey | 36c19b23dc | |
Casey | d4ca60c9f9 | |
Casey | 8589b2a730 | |
Casey | 3cadd81a6b | |
Casey | e2e5d57e1f | |
Casey | 23447e7ed9 | |
Casey | 022cb0cde8 | |
Casey | 93205ec237 | |
Vftdan | 6028bb419e | |
Vftdan | 5f95f895d2 | |
Vftdan | c91f4d79bd | |
Vftdan | c5af524dac | |
Vftdan | 849563285c | |
Vftdan | d9f4362615 | |
Vftdan | e8c53b1f9b | |
Vftdan | be21d42fa0 | |
Vftdan | d765e88679 | |
Vftdan | 1eb767d0d2 | |
Vftdan | a65943a6bd | |
Vftdan | 2f80e5327d | |
Vftdan | bbbe20f941 | |
Casey | 1eccdde3a3 |
|
@ -0,0 +1,8 @@
|
|||
img2cpi
|
||||
img2cpi.o
|
||||
wsvpn
|
||||
wsvpn.o
|
||||
cpi2png
|
||||
cc-common.o
|
||||
vim.state
|
||||
__pycache__
|
|
@ -0,0 +1,6 @@
|
|||
[submodule "dependencies/stb"]
|
||||
path = dependencies/stb
|
||||
url = https://github.com/nothings/stb
|
||||
[submodule "dependencies/mongoose"]
|
||||
path = dependencies/mongoose
|
||||
url = https://github.com/cesanta/mongoose
|
|
@ -0,0 +1,24 @@
|
|||
CPPFLAGS += -Idependencies -Idependencies/mongoose
|
||||
LDLIBS += -lm
|
||||
|
||||
all: img2cpi cpi2png wsvpn
|
||||
|
||||
test-cpi2png: cpi2png
|
||||
./cpi2png ./cpi-images/rat.cpi /tmp/rat.png
|
||||
|
||||
img2cpi: img2cpi.c cc-common.o dependencies/stb/stb_image.o dependencies/stb/stb_image_resize2.o
|
||||
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o "$@" $(CPPFLAGS)
|
||||
|
||||
cpi2png: cpi2png.c cc-common.o dependencies/stb/stb_image_write.o
|
||||
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o "$@" $(CPPFLAGS)
|
||||
|
||||
dependencies/stb/%.o: dependencies/stb/%.h
|
||||
$(CC) -DSTB_IMAGE_IMPLEMENTATION -DSTB_IMAGE_RESIZE_IMPLEMENTATION -DSTB_IMAGE_WRITE_IMPLEMENTATION -x c $^ -c -o "$@"
|
||||
|
||||
wsvpn: wsvpn.o dependencies/mongoose/mongoose.o
|
||||
$(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o "$@"
|
||||
|
||||
clean:
|
||||
$(RM) -v img2cpi cpi2png wsvpn wsvpn.o cc-common.o dependencies/stb/*.o
|
||||
|
||||
.PHONY: all
|
|
@ -0,0 +1,327 @@
|
|||
#include "cc-common.h"
|
||||
|
||||
int read_varint(FILE *fp, unsigned int *out) {
|
||||
int position = 0;
|
||||
|
||||
while (true) {
|
||||
unsigned char curr = fgetc(fp);
|
||||
*out |= (curr & 0x7F) << position;
|
||||
|
||||
if ((curr & 0x80) == 0) break;
|
||||
|
||||
position += 7;
|
||||
|
||||
if (position >= 32) return -position / 7;
|
||||
}
|
||||
|
||||
return (position + 7) / 7;
|
||||
}
|
||||
|
||||
int write_varint(FILE *fp, unsigned int in) {
|
||||
unsigned mask = 0xFFFFFF80;
|
||||
int written = 0;
|
||||
while (true) {
|
||||
if ((in & mask) == 0) {
|
||||
fputc(in & 0xff, fp);
|
||||
return written + 1;
|
||||
}
|
||||
fputc((in & 0x7F) | 0x80, fp);
|
||||
written++;
|
||||
in >>= 7;
|
||||
}
|
||||
}
|
||||
|
||||
const struct palette cc_default_palette = PALETTE(
|
||||
{ { 0xf0, 0xf0, 0xf0, 0xff } },
|
||||
{ { 0xf2, 0xb2, 0x33, 0xff } },
|
||||
{ { 0xe5, 0x7f, 0xd8, 0xff } },
|
||||
{ { 0x99, 0xb2, 0xf2, 0xff } },
|
||||
{ { 0xde, 0xde, 0x6c, 0xff } },
|
||||
{ { 0x7f, 0xcc, 0x19, 0xff } },
|
||||
{ { 0xf2, 0xb2, 0xcc, 0xff } },
|
||||
{ { 0x4c, 0x4c, 0x4c, 0xff } },
|
||||
{ { 0x99, 0x99, 0x99, 0xff } },
|
||||
{ { 0x4c, 0x99, 0xb2, 0xff } },
|
||||
{ { 0xb2, 0x66, 0xe5, 0xff } },
|
||||
{ { 0x33, 0x66, 0xcc, 0xff } },
|
||||
{ { 0x7f, 0x66, 0x4c, 0xff } },
|
||||
{ { 0x57, 0xa6, 0x4e, 0xff } },
|
||||
{ { 0xcc, 0x4c, 0x4c, 0xff } },
|
||||
{ { 0x11, 0x11, 0x11, 0xff } }
|
||||
), cc_default_gray_palette = PALETTE(
|
||||
{ { 0xf0, 0xf0, 0xf0, 0xff } },
|
||||
{ { 0x9d, 0x9d, 0x9d, 0xff } },
|
||||
{ { 0xbe, 0xbe, 0xbe, 0xff } },
|
||||
{ { 0xbf, 0xbf, 0xbf, 0xff } },
|
||||
{ { 0xb8, 0xb8, 0xb8, 0xff } },
|
||||
{ { 0x76, 0x76, 0x76, 0xff } },
|
||||
{ { 0xd0, 0xd0, 0xd0, 0xff } },
|
||||
{ { 0x4c, 0x4c, 0x4c, 0xff } },
|
||||
{ { 0x99, 0x99, 0x99, 0xff } },
|
||||
{ { 0x87, 0x87, 0x87, 0xff } },
|
||||
{ { 0xa9, 0xa9, 0xa9, 0xff } },
|
||||
{ { 0x77, 0x77, 0x77, 0xff } },
|
||||
{ { 0x65, 0x65, 0x65, 0xff } },
|
||||
{ { 0x6e, 0x6e, 0x6e, 0xff } },
|
||||
{ { 0x76, 0x76, 0x76, 0xff } },
|
||||
{ { 0x11, 0x11, 0x11, 0xff } }
|
||||
);
|
||||
|
||||
const GlyphBitmap cc_font_atlas[256] = {
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x6c, 0x44, 0x54, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x7c, 0x54, 0x7c, 0x44, 0x6c, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x28, 0x7c, 0x7c, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x38, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x38, 0x10, 0x7c, 0x7c, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x38, 0x7c, 0x7c, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7e, 0x7e, 0x66, 0x42, 0x42, 0x66, 0x7e, 0x7e, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x1c, 0x0c, 0x34, 0x48, 0x48, 0x30, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x10, 0x38, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x3c, 0x24, 0x3c, 0x20, 0x60, 0x60, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x3e, 0x22, 0x3e, 0x22, 0x66, 0x66, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x40, 0x70, 0x7c, 0x70, 0x40, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x04, 0x1c, 0x7c, 0x1c, 0x04, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x38, 0x7c, 0x10, 0x10, 0x7c, 0x38, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x24, 0x24, 0x24, 0x24, 0x24, 0x00, 0x24, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x54, 0x54, 0x34, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x60, 0x58, 0x44, 0x34, 0x0c, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x38, 0x7c, 0x10, 0x7c, 0x38, 0x10, 0x7c, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x38, 0x7c, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x10, 0x10, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x18, 0x7c, 0x18, 0x10, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x30, 0x7c, 0x30, 0x10, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x24, 0x7e, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x14, 0x14, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x28, 0x7c, 0x28, 0x7c, 0x28, 0x28, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x3c, 0x40, 0x38, 0x04, 0x78, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x48, 0x08, 0x10, 0x20, 0x24, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x28, 0x10, 0x34, 0x58, 0x48, 0x34, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x10, 0x20, 0x20, 0x20, 0x10, 0x0c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x08, 0x04, 0x04, 0x04, 0x08, 0x30, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x24, 0x18, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x04, 0x08, 0x08, 0x10, 0x20, 0x20, 0x40, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x4c, 0x54, 0x64, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x04, 0x18, 0x20, 0x44, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x04, 0x18, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x14, 0x24, 0x44, 0x7c, 0x04, 0x04, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x40, 0x78, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x18, 0x20, 0x40, 0x78, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x44, 0x04, 0x08, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x44, 0x38, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x44, 0x3c, 0x04, 0x08, 0x30, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x04, 0x08, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x42, 0x5a, 0x5a, 0x5e, 0x40, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x78, 0x44, 0x78, 0x44, 0x44, 0x44, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x40, 0x70, 0x40, 0x40, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x40, 0x70, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x40, 0x4c, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x48, 0x70, 0x48, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x6c, 0x54, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x78, 0x44, 0x78, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x44, 0x44, 0x44, 0x48, 0x34, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x78, 0x44, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x40, 0x38, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x44, 0x44, 0x44, 0x28, 0x28, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x44, 0x44, 0x44, 0x54, 0x6c, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x40, 0x20, 0x20, 0x10, 0x08, 0x08, 0x04, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x40, 0x40, 0x58, 0x64, 0x44, 0x44, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x38, 0x44, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x04, 0x04, 0x34, 0x4c, 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x10, 0x3c, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x3c, 0x44, 0x44, 0x3c, 0x04, 0x78, 0x00, 0x00, },
|
||||
{ 0x00, 0x40, 0x40, 0x58, 0x64, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x04, 0x00, 0x04, 0x04, 0x04, 0x44, 0x44, 0x38, 0x00, 0x00, },
|
||||
{ 0x00, 0x20, 0x20, 0x24, 0x28, 0x30, 0x28, 0x24, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x68, 0x54, 0x54, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x58, 0x64, 0x44, 0x78, 0x40, 0x40, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x34, 0x4c, 0x44, 0x3c, 0x04, 0x04, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x58, 0x64, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x3c, 0x40, 0x38, 0x04, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x44, 0x44, 0x54, 0x54, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x04, 0x78, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x7c, 0x08, 0x10, 0x20, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x10, 0x10, 0x20, 0x10, 0x10, 0x0c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x08, 0x08, 0x04, 0x08, 0x08, 0x30, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x32, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x24, 0x48, 0x12, 0x24, 0x48, 0x12, 0x24, 0x48, 0x12, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x7f, 0x7f, 0x7f, 0x7f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x38, 0x44, 0x40, 0x44, 0x38, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x18, 0x24, 0x20, 0x78, 0x20, 0x20, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x44, 0x38, 0x44, 0x44, 0x44, 0x38, 0x44, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x28, 0x7c, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x10, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x60, 0x58, 0x44, 0x34, 0x0c, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x3c, 0x4a, 0x52, 0x52, 0x4a, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x08, 0x38, 0x48, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x14, 0x28, 0x50, 0x28, 0x14, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x3c, 0x5a, 0x5a, 0x56, 0x42, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x48, 0x48, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x7c, 0x00, 0x00, },
|
||||
{ 0x00, 0x40, 0x20, 0x60, 0x40, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x20, 0x60, 0x20, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x7a, 0x40, 0x40, 0x00, },
|
||||
{ 0x00, 0x3c, 0x54, 0x54, 0x34, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x20, 0x60, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x50, 0x28, 0x14, 0x28, 0x50, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x48, 0x08, 0x10, 0x2c, 0x2c, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x48, 0x08, 0x10, 0x24, 0x28, 0x4c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x64, 0x28, 0x68, 0x10, 0x2c, 0x2c, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x00, 0x10, 0x20, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x50, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x00, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x50, 0x50, 0x78, 0x50, 0x50, 0x5c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x40, 0x40, 0x44, 0x38, 0x08, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x7c, 0x40, 0x78, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x7c, 0x40, 0x78, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x7c, 0x40, 0x78, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x7c, 0x40, 0x78, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x00, 0x38, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x18, 0x00, 0x38, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x28, 0x38, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x38, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x78, 0x44, 0x44, 0x64, 0x44, 0x44, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x14, 0x28, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x38, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x38, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x50, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x38, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x4c, 0x54, 0x64, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x28, 0x00, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x44, 0x28, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x10, 0x18, 0x14, 0x18, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x78, 0x44, 0x58, 0x44, 0x44, 0x44, 0x58, 0x40, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x50, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x2c, 0x52, 0x7c, 0x50, 0x2e, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x38, 0x44, 0x40, 0x44, 0x38, 0x08, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x18, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x08, 0x3c, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x50, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x50, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x00, 0x7c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x38, 0x4c, 0x54, 0x64, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x44, 0x44, 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x44, 0x44, 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x28, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x44, 0x44, 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x04, 0x78, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x10, 0x18, 0x14, 0x18, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x04, 0x78, 0x00, 0x00, },
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef _CC_COMMON_H_
|
||||
#define _CC_COMMON_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef uint8_t GlyphBitmap[11];
|
||||
|
||||
struct rgba { uint8_t r, g, b, a; };
|
||||
union color {
|
||||
struct rgba rgba;
|
||||
uint32_t v;
|
||||
};
|
||||
|
||||
struct palette {
|
||||
const uint8_t count;
|
||||
union color colors[] __attribute__((counted_by(count)));
|
||||
};
|
||||
|
||||
#define LENGTHOF(...) (sizeof(__VA_ARGS__) / sizeof(*(__VA_ARGS__)))
|
||||
#define PALETTE(...) { .count = LENGTHOF((union color[]){__VA_ARGS__}), .colors = {__VA_ARGS__} }
|
||||
|
||||
const extern GlyphBitmap cc_font_atlas[256];
|
||||
const extern struct palette cc_default_palette, cc_default_gray_palette;
|
||||
|
||||
|
||||
int read_varint(FILE *fp, unsigned int *out);
|
||||
int write_varint(FILE *fp, unsigned int in);
|
||||
|
||||
#endif
|
37
cc-pic.py
|
@ -33,22 +33,25 @@ class Converter:
|
|||
]
|
||||
|
||||
DEFAULT_PALETTE = [
|
||||
240, 240, 240,
|
||||
242, 178, 51,
|
||||
229, 127, 216,
|
||||
153, 178, 242,
|
||||
222, 222, 108,
|
||||
127, 204, 25,
|
||||
242, 178, 204,
|
||||
76, 76, 76,
|
||||
153, 153, 153,
|
||||
76, 153, 178,
|
||||
178, 102, 229,
|
||||
51, 102, 204,
|
||||
127, 102, 76,
|
||||
87, 166, 78,
|
||||
204, 76, 76,
|
||||
17, 17, 17
|
||||
0xf0, 0xf0, 0xf0,
|
||||
0xf2, 0xb2, 0x33,
|
||||
0xe5, 0x7f, 0xd8,
|
||||
0x99, 0xb2, 0xf2,
|
||||
|
||||
0xde, 0xde, 0x6c,
|
||||
0x7f, 0xcc, 0x19,
|
||||
0xf2, 0xb2, 0xcc,
|
||||
0x4c, 0x4c, 0x4c,
|
||||
|
||||
0x99, 0x99, 0x99,
|
||||
0x4c, 0x99, 0xb2,
|
||||
0xb2, 0x66, 0xe5,
|
||||
0x33, 0x66, 0xcc,
|
||||
|
||||
0x7f, 0x66, 0x4c,
|
||||
0x57, 0xa6, 0x4e,
|
||||
0xcc, 0x4c, 0x4c,
|
||||
0x11, 0x11, 0x11
|
||||
]
|
||||
|
||||
DEFAULT_GRAYSCALE_PALETTE = [
|
||||
|
@ -399,7 +402,7 @@ def main():
|
|||
raise ValueError(f"invalid palette identifier: {args.palette!r}")
|
||||
|
||||
converter = Converter(canv, palette, dither=not args.nodither)
|
||||
converter._img.save("/tmp/_ccpictmp.png")
|
||||
# converter._img.save("/tmp/_ccpictmp.png")
|
||||
if args.textmode:
|
||||
with open(args.output_path, "w") as fp:
|
||||
converter.export(fp)
|
||||
|
|
22
ccpi.lua
|
@ -64,9 +64,29 @@ decoders[1] = function(image, fp)
|
|||
return true
|
||||
end
|
||||
|
||||
decoders[128] = function(image, fp)
|
||||
image.w = read_varint(fp)
|
||||
image.h = read_varint(fp)
|
||||
image.extras.n_frames = read_varint(fp)
|
||||
read_palette_full(image.palette, fp)
|
||||
local success, err = read_pixeldata_v0(image, fp)
|
||||
if not success then return false, err end
|
||||
image.extras.frames = {}
|
||||
for i = 1, image.extras.n_frames - 1 do
|
||||
local frame = { palette = {}, lines = {} }
|
||||
frame.w = image.w
|
||||
frame.h = image.h
|
||||
frame.scale = image.scale
|
||||
read_palette_full(frame.palette)
|
||||
local success, err = read_pixeldata_v0(frame, fp)
|
||||
if not success then return false, err end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function parse(fp)
|
||||
local res
|
||||
local image = { w = 0, h = 0, scale = 1.0, palette = {}, lines = {} }
|
||||
local image = { w = 0, h = 0, scale = 1.0, palette = {}, lines = {}, extras = {} }
|
||||
|
||||
local magic = fp.read(4)
|
||||
if magic == "CCPI" then
|
||||
|
|
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 94 KiB |
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 52 KiB |
|
@ -0,0 +1,91 @@
|
|||
// x-run: make test-cpi2png
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stb/stb_image.h>
|
||||
#include <stb/stb_image_write.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cc-common.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "Usage: %s [input.cpi] [output.png]\n", argv[0]);
|
||||
}
|
||||
|
||||
FILE *fp_in = fopen(argv[1], "rb");
|
||||
assert(fp_in != NULL && "Failed to open input file");
|
||||
|
||||
unsigned char header[4];
|
||||
|
||||
unsigned char version = 0;
|
||||
assert(fread(header, 1, 4, fp_in) == 4 && "Failed to read header: not enough bytes");
|
||||
if (0 == memcmp(header, "CCPI", 4)) { // Original CCPI (CPIv0)
|
||||
version = 0;
|
||||
} else if (0 == memcmp(header, "CPI", 3)) { // Newer CCPI (CPIvX)
|
||||
version = header[3];
|
||||
} else {
|
||||
assert(false && "Not a CPI/CCPI image: invalid header");
|
||||
}
|
||||
|
||||
if (version & 0x80) {
|
||||
fprintf(stderr, "Draft version: 0x%02x may not be supported properly! Here be dragons!\n", version);
|
||||
}
|
||||
|
||||
unsigned int width = 0, height = 0;
|
||||
|
||||
if (version == 0) {
|
||||
width = fgetc(fp_in);
|
||||
height = fgetc(fp_in);
|
||||
(void)fgetc(fp_in); // XXX: ignore scale
|
||||
} else if (version == 1) {
|
||||
assert(read_varint(fp_in, &width) > 0 && "Failed to read width varint");
|
||||
assert(read_varint(fp_in, &height) > 0 && "Failed to read height varint");
|
||||
} else {
|
||||
assert(false && "Failed to read size: unsupported version");
|
||||
}
|
||||
|
||||
union color *canvas = malloc(width * height * 6 * 9 * sizeof(union color));
|
||||
|
||||
|
||||
// XXX: may change in future when we introduce variable-size palettes
|
||||
// though, it may never change, if I'm being honest. Why would I choose
|
||||
// worse image quality with less colors when I can use all of them?
|
||||
union color colors[16] = { 0 };
|
||||
|
||||
// NOTE: our `union color` type is 4 bytes long, while palette stored in the
|
||||
// file itself uses 3 bytes per color, so we can't just `fread` them at once,
|
||||
// sadly.
|
||||
for (int i = 0; i < 16; i++) {
|
||||
colors[i].rgba.r = fgetc(fp_in);
|
||||
colors[i].rgba.g = fgetc(fp_in);
|
||||
colors[i].rgba.b = fgetc(fp_in);
|
||||
colors[i].rgba.a = 0xff;
|
||||
}
|
||||
|
||||
unsigned char *buffer = calloc(width * height, 2);
|
||||
fread(buffer, 2, width * height, fp_in);
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
unsigned char sym = buffer[(x + y * width) * 2];
|
||||
unsigned char color = buffer[(x + y * width) * 2 + 1];
|
||||
union color background = colors[color & 0xF];
|
||||
union color foreground = colors[color >> 4];
|
||||
for (int oy = 0; oy < 9; oy++) {
|
||||
for (int ox = 0; ox < 6; ox++) {
|
||||
union color pix = ((0x80 >> (ox + 1)) & cc_font_atlas[sym][oy + 1]) ? foreground : background;
|
||||
canvas[ox + (x + (y * 9 + oy) * width) * 6] = pix;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stbi_write_png(argv[2], width * 6, height * 9, 4, canvas, 0);
|
||||
|
||||
free(canvas);
|
||||
fclose(fp_in);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 3525f044f551816dc1469f445fc16b94d51a1e78
|
|
@ -0,0 +1 @@
|
|||
Subproject commit f75e8d1cad7d90d72ef7a4661f1b994ef78b4e31
|
833
img2cpi.c
|
@ -1,7 +1,8 @@
|
|||
// x-run: make img2cpi CC=clang
|
||||
// x-run: ~/scripts/runc.sh % -Wall -Wextra -lm --- ~/images/boykisser.png cpi-images/boykisser.cpi
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <stb/stb_image.h>
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||
#include <stb/stb_image_resize2.h>
|
||||
#include <argp.h>
|
||||
#include <stdio.h>
|
||||
|
@ -11,24 +12,26 @@
|
|||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <stdbool.h>
|
||||
#include "cc-common.h"
|
||||
#ifdef USE_OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
#define MAX_COLOR_DIFFERENCE 768
|
||||
#define K_MEANS_ITERATIONS 4
|
||||
#define PROGRESS_BAR_WIDTH 24
|
||||
|
||||
#define TOSTRNAME(M) #M
|
||||
#define TOSTR(M) TOSTRNAME(M)
|
||||
|
||||
struct rgba { uint8_t r, g, b, a; };
|
||||
union color {
|
||||
struct rgba rgba;
|
||||
uint32_t v;
|
||||
};
|
||||
struct cc_char {
|
||||
unsigned char character;
|
||||
unsigned char bg, fg;
|
||||
};
|
||||
|
||||
const extern char font_atlas[256][11];
|
||||
const extern union color DEFAULT_PALETTE[16], DEFAULT_GRAY_PALETTE[16];
|
||||
|
||||
struct arguments {
|
||||
bool fast_mode;
|
||||
bool verbose;
|
||||
int width, height;
|
||||
enum cpi_version {
|
||||
CPI_VERSION_AUTO,
|
||||
|
@ -57,6 +60,7 @@ struct arguments {
|
|||
char *output_path;
|
||||
} args = {
|
||||
.fast_mode = false,
|
||||
.verbose = false,
|
||||
.width = 4 * 8 - 1, // 4x3 blocks screen
|
||||
.height = 3 * 6 - 2,
|
||||
.cpi_version = CPI_VERSION_AUTO,
|
||||
|
@ -64,8 +68,7 @@ struct arguments {
|
|||
.input_path = NULL,
|
||||
.output_path = NULL,
|
||||
.palette = NULL,
|
||||
.palette_type = PALETTE_DEFAULT // TODO(kc): change to PALETTE_AUTO when
|
||||
// k-means is implemented
|
||||
.palette_type = PALETTE_AUTO
|
||||
};
|
||||
|
||||
struct image {
|
||||
|
@ -76,19 +79,53 @@ struct image {
|
|||
struct image_pal {
|
||||
int w, h;
|
||||
uint8_t *pixels;
|
||||
const struct palette *palette;
|
||||
};
|
||||
|
||||
struct k_means_state {
|
||||
const struct image *items;
|
||||
struct palette *clusters;
|
||||
uint8_t *predicted_cluster;
|
||||
struct k_means_centroid_intermediate {
|
||||
struct {
|
||||
float r, g, b;
|
||||
} sums;
|
||||
size_t count;
|
||||
union color closest_present_item;
|
||||
float closest_present_distance;
|
||||
} *centroid_intermediate;
|
||||
size_t item_count;
|
||||
};
|
||||
|
||||
bool parse_cmdline(int argc, char **argv);
|
||||
void show_help(const char *progname, bool show_all, FILE *fp);
|
||||
|
||||
struct image *image_load(const char *fp);
|
||||
struct image *image_new(int w, int h);
|
||||
struct image *image_resize(struct image *original, int new_w, int new_h);
|
||||
struct image_pal *image_quantize(struct image *original, const union color *colors, size_t n_colors);
|
||||
struct image_pal *image_quantize(struct image *original, const struct palette *palette);
|
||||
void image_unload(struct image *img);
|
||||
|
||||
float get_color_difference(union color a, union color b);
|
||||
float get_color_brightness(union color clr);
|
||||
void image_unload(struct image *img);
|
||||
|
||||
void get_size_keep_aspect(int w, int h, int dw, int dh, int *ow, int *oh);
|
||||
|
||||
void convert_2x3(const struct image_pal *img, struct cc_char *characters);
|
||||
void convert_6x9(const struct image_pal *img, struct cc_char *characters);
|
||||
|
||||
int save_cpi_0(FILE *fp, const struct palette *pal, const struct cc_char *chars, int w, int h);
|
||||
int save_cpi_1(FILE *fp, const struct palette *pal, const struct cc_char *chars, int w, int h);
|
||||
|
||||
// Only one global custom palette is maintained
|
||||
struct palette *custom_palette_resize(uint8_t size);
|
||||
struct palette *custom_palette_from(const struct palette *orig);
|
||||
|
||||
struct k_means_state k_means_init(const struct image *image, struct palette *starting_palette);
|
||||
bool k_means_iteration(struct k_means_state *state);
|
||||
void k_means_end(struct k_means_state *state);
|
||||
struct palette *palette_k_means(const struct image *image, const struct palette *prototype);
|
||||
|
||||
const char *known_file_extensions[] = {
|
||||
".png", ".jpg", ".jpeg", ".jfif", ".jpg", ".gif",
|
||||
".tga", ".bmp", ".hdr", ".pnm", 0
|
||||
|
@ -103,6 +140,7 @@ static const struct optiondocs {
|
|||
} optiondocs[] = {
|
||||
{ 'h', "help", 0, "Show help", 0 },
|
||||
{ 'f', "fast", 0, "Use fast (old) method for picking characters and colors", 0 },
|
||||
{ 'v', "verbose", 0, "Increase verbosity", 0 },
|
||||
{ 'W', "width", "width", "Width in characters", 0 },
|
||||
{ 'h', "height", "height", "Height in characters", 0 },
|
||||
{ 'P', "palette", "palette", "Use specific palette.\n"
|
||||
|
@ -133,6 +171,8 @@ static const struct optiondocs {
|
|||
{ 0 }
|
||||
};
|
||||
|
||||
static char progress_bar[PROGRESS_BAR_WIDTH];
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (!parse_cmdline(argc, argv)) {
|
||||
show_help(argv[0], false, stderr);
|
||||
|
@ -145,11 +185,16 @@ int main(int argc, char **argv) {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (args.verbose) {
|
||||
memset(progress_bar, '#', PROGRESS_BAR_WIDTH);
|
||||
printf("Input image: %dx%d\n", src_image->w, src_image->h);
|
||||
}
|
||||
|
||||
struct image *canvas;
|
||||
if (args.fast_mode) {
|
||||
canvas = image_new(args.width * 2, args.height * 3);
|
||||
} else {
|
||||
canvas = image_new(args.width * 8, args.height * 11);
|
||||
canvas = image_new(args.width * 6, args.height * 9);
|
||||
}
|
||||
|
||||
if (!canvas) {
|
||||
|
@ -157,14 +202,18 @@ int main(int argc, char **argv) {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (args.verbose) {
|
||||
printf("Output image canvas: %dx%d\n", canvas->w, canvas->h);
|
||||
}
|
||||
|
||||
// TODO: load palette, maybe calculate it too? k-means?
|
||||
const union color *palette = DEFAULT_PALETTE;
|
||||
const struct palette *palette = &cc_default_palette;
|
||||
switch (args.palette_type) {
|
||||
case PALETTE_DEFAULT: palette = DEFAULT_PALETTE; break;
|
||||
case PALETTE_DEFAULT_GRAY: palette = DEFAULT_GRAY_PALETTE; break;
|
||||
case PALETTE_AUTO: assert(0 && "Not implemented"); break;
|
||||
case PALETTE_LIST: assert(0 && "Not implemented"); break;
|
||||
case PALETTE_PATH: assert(0 && "Not implemented"); break;
|
||||
case PALETTE_DEFAULT: palette = &cc_default_palette; break;
|
||||
case PALETTE_DEFAULT_GRAY: palette = &cc_default_gray_palette; break;
|
||||
case PALETTE_AUTO: palette = palette_k_means(src_image, &cc_default_palette); break;
|
||||
case PALETTE_LIST: assert(0 && "Not implemented"); break; // TODO
|
||||
case PALETTE_PATH: assert(0 && "Not implemented"); break; // TODO
|
||||
default: assert(0 && "Unreachable");
|
||||
}
|
||||
|
||||
|
@ -174,6 +223,10 @@ int main(int argc, char **argv) {
|
|||
int new_w, new_h;
|
||||
get_size_keep_aspect(src_image->w, src_image->h, canvas->w, canvas->h, &new_w, &new_h);
|
||||
|
||||
if (args.verbose) {
|
||||
printf("Scaling down to: %dx%d\n", new_w, new_h);
|
||||
}
|
||||
|
||||
scaled_image = image_resize(src_image, new_w, new_h);
|
||||
if (!scaled_image) {
|
||||
fprintf(stderr, "Error: failed to open the file\n");
|
||||
|
@ -190,131 +243,42 @@ int main(int argc, char **argv) {
|
|||
small_w * sizeof(union color));
|
||||
}
|
||||
|
||||
|
||||
// TODO: actually do stuff
|
||||
struct cc_char *characters = calloc(args.width * args.height, sizeof(struct cc_char));
|
||||
|
||||
struct image_pal *quantized_image = image_quantize(canvas, palette, 16);
|
||||
struct image_pal *quantized_image = image_quantize(canvas, palette);
|
||||
if (!quantized_image) {
|
||||
fprintf(stderr, "Error: failed to open the file\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
FILE *tmp = fopen("/tmp/img.raw", "wb");
|
||||
for (int i = 0; i < quantized_image->w * quantized_image->h; i++) {
|
||||
union color pix = palette[quantized_image->pixels[i]];
|
||||
fputc(pix.rgba.r, tmp);
|
||||
fputc(pix.rgba.g, tmp);
|
||||
fputc(pix.rgba.b, tmp);
|
||||
if (args.verbose) {
|
||||
printf("Converting image ");
|
||||
}
|
||||
fclose(tmp);
|
||||
|
||||
if (args.fast_mode) {
|
||||
// use old 2x3
|
||||
for (int y = 0; y < args.height; y++) {
|
||||
for (int x = 0; x < args.width; x++) {
|
||||
unsigned char darkest_i = 0, brightest_i = 0;
|
||||
float darkest_diff = 0xffffff, brightest_diff = 0;
|
||||
|
||||
for (int oy = 0; oy < 3; oy++) {
|
||||
for (int ox = 0; ox < 2; ox++) {
|
||||
unsigned char pix = quantized_image->pixels[ox + (x + (y * 3 + oy) * args.width) * 2];
|
||||
float brightness = get_color_brightness(palette[pix]);
|
||||
if (brightness >= brightest_diff) {
|
||||
brightest_i = pix;
|
||||
brightest_diff = brightness;
|
||||
}
|
||||
if (brightness <= darkest_diff) {
|
||||
darkest_i = pix;
|
||||
darkest_diff = brightness;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char bitmap = 0;
|
||||
const static unsigned char pixel_bits[3][2] = { { 1, 2}, { 4, 8 }, { 16, 0 } };
|
||||
for (int oy = 0; oy < 3; oy++) {
|
||||
for (int ox = 0; ox < 2; ox++) {
|
||||
if (ox == 1 && oy == 2) continue;
|
||||
unsigned char pix = quantized_image->pixels[ox + (x + (y * 3 + oy) * args.width) * 2];
|
||||
float diff_bg = get_color_difference(palette[darkest_i], palette[pix]);
|
||||
float diff_fg = get_color_difference(palette[brightest_i], palette[pix]);
|
||||
if (diff_fg < diff_bg) {
|
||||
bitmap |= pixel_bits[oy][ox];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
unsigned char pix = quantized_image->pixels[1 + (x + (y * 3 + 2) * args.width) * 2];
|
||||
float diff_bg = get_color_difference(palette[darkest_i], palette[pix]);
|
||||
float diff_fg = get_color_difference(palette[brightest_i], palette[pix]);
|
||||
if (diff_fg < diff_bg) {
|
||||
bitmap ^= 31;
|
||||
unsigned char tmp = darkest_i;
|
||||
darkest_i = brightest_i;
|
||||
brightest_i = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
characters[x + y * args.width].character = 0x80 + bitmap;
|
||||
characters[x + y * args.width].bg = darkest_i;
|
||||
characters[x + y * args.width].fg = brightest_i;
|
||||
}
|
||||
if (args.verbose) {
|
||||
printf(" using fast method\n");
|
||||
}
|
||||
convert_2x3(quantized_image, characters);
|
||||
} else {
|
||||
// use new 8x11 character matching
|
||||
for (int y = 0; y < args.height; y++) {
|
||||
for (int x = 0; x < args.width; x++) {
|
||||
// Oh boy...
|
||||
float min_diff = 0xffffff;
|
||||
char closest_sym = 0x00, closest_color = 0xae;
|
||||
for (int sym = 0x01; sym <= 0xFF; sym++) {
|
||||
if (sym == '\t' || sym == '\n' || sym == '\r' || sym == '\x0e') {
|
||||
continue;
|
||||
}
|
||||
for (int color = 0x00; color <= 0xff; color++) {
|
||||
union color cell_bg = palette[color & 0xF],
|
||||
cell_fg = palette[color >> 4];
|
||||
float difference = 0;
|
||||
for (int oy = 0; oy < 11; oy++) {
|
||||
unsigned char sym_line = font_atlas[sym][oy];
|
||||
for (int ox = 0; ox < 8; ox++) {
|
||||
bool lit = sym_line & (0x80 >> ox);
|
||||
union color pixel = palette[quantized_image->pixels[
|
||||
ox + (x + (y * 11 + oy) * args.width) * 8
|
||||
]];
|
||||
difference += get_color_difference(pixel, lit ? cell_fg : cell_bg);
|
||||
}
|
||||
}
|
||||
if (difference <= min_diff) {
|
||||
min_diff = difference;
|
||||
closest_sym = sym;
|
||||
closest_color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
characters[x + y * args.width].character = closest_sym;
|
||||
characters[x + y * args.width].bg = closest_color & 0xF;
|
||||
characters[x + y * args.width].fg = closest_color >> 4;
|
||||
}
|
||||
if (args.verbose) {
|
||||
printf(" using slow method\n");
|
||||
}
|
||||
convert_6x9(quantized_image, characters);
|
||||
}
|
||||
|
||||
// TODO: implement something other than CPIv0
|
||||
FILE *fp = fopen(args.output_path, "wb");
|
||||
fwrite("CCPI", 1, 4, fp);
|
||||
fputc(args.width, fp);
|
||||
fputc(args.height, fp);
|
||||
fputc(0x00, fp);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
fputc(palette[i].rgba.r, fp);
|
||||
fputc(palette[i].rgba.g, fp);
|
||||
fputc(palette[i].rgba.b, fp);
|
||||
if (args.verbose) {
|
||||
printf("Conversion done, saving image ");
|
||||
}
|
||||
for (int i = 0; i < args.width * args.height; i++) {
|
||||
fputc(characters[i].character, fp);
|
||||
fputc(characters[i].bg | (characters[i].fg << 4), fp);
|
||||
|
||||
FILE *fp = fopen(args.output_path, "wb");
|
||||
if (args.width < 256 && args.height < 256) {
|
||||
printf(" using cpiv0\n");
|
||||
save_cpi_0(fp, palette, characters, args.width, args.height);
|
||||
} else {
|
||||
printf(" using cpiv1\n");
|
||||
save_cpi_1(fp, palette, characters, args.width, args.height);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
|
@ -323,6 +287,47 @@ int main(int argc, char **argv) {
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int _write_palette_full(FILE *fp, const struct palette *pal) {
|
||||
int written = 0;
|
||||
assert(pal->count == 16 && "Invalid palette size");
|
||||
for (int i = 0; i < 16; i++) {
|
||||
written += fputc(pal->colors[i].rgba.r, fp);
|
||||
written += fputc(pal->colors[i].rgba.g, fp);
|
||||
written += fputc(pal->colors[i].rgba.b, fp);
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
int _write_pixeldata_v0(FILE *fp, const struct cc_char *chars, int w, int h) {
|
||||
int written = 0;
|
||||
for (int i = 0; i < w * h; i++) {
|
||||
written += fputc(chars[i].character, fp);
|
||||
written += fputc(chars[i].bg | (chars[i].fg << 4), fp);
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
int save_cpi_0(FILE *fp, const struct palette *pal, const struct cc_char *chars, int w, int h) {
|
||||
int written = 0;
|
||||
written += fwrite("CCPI", 1, 4, fp);
|
||||
written += fputc(w, fp);
|
||||
written += fputc(h, fp);
|
||||
written += fputc(0x00, fp);
|
||||
written += _write_palette_full(fp, pal);
|
||||
written += _write_pixeldata_v0(fp, chars, w, h);
|
||||
return written;
|
||||
}
|
||||
|
||||
int save_cpi_1(FILE *fp, const struct palette *pal, const struct cc_char *chars, int w, int h) {
|
||||
int written = 0;
|
||||
written += fwrite("CPI\x01", 1, 4, fp);
|
||||
written += write_varint(fp, w);
|
||||
written += write_varint(fp, h);
|
||||
written += _write_palette_full(fp, pal);
|
||||
written += _write_pixeldata_v0(fp, chars, w, h);
|
||||
return written;
|
||||
}
|
||||
|
||||
bool parse_cmdline(int argc, char **argv) {
|
||||
static struct option options[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
|
@ -337,7 +342,7 @@ bool parse_cmdline(int argc, char **argv) {
|
|||
|
||||
while (true) {
|
||||
int option_index = 0;
|
||||
int c = getopt_long(argc, argv, "hfW:H:V:p:P:", options, &option_index);
|
||||
int c = getopt_long(argc, argv, "hvfW:H:V:p:P:", options, &option_index);
|
||||
if (c == -1) break;
|
||||
if (c == 0) c = options[option_index].val;
|
||||
if (c == '?') break;
|
||||
|
@ -353,6 +358,9 @@ bool parse_cmdline(int argc, char **argv) {
|
|||
fprintf(stderr, "Warning: text mode ignores version\n");
|
||||
}
|
||||
break;
|
||||
case 'v': // --verbose
|
||||
args.verbose = true;
|
||||
break;
|
||||
case 'W': // --width
|
||||
args.width = atoi(optarg);
|
||||
break;
|
||||
|
@ -577,17 +585,18 @@ void get_size_keep_aspect(int w, int h, int dw, int dh, int *ow, int *oh)
|
|||
}
|
||||
}
|
||||
|
||||
struct image_pal *image_quantize(struct image *original, const union color *colors, size_t n_colors) {
|
||||
struct image_pal *image_quantize(struct image *original, const struct palette *palette) {
|
||||
struct image_pal *out = calloc(1, sizeof(struct image_pal));
|
||||
out->w = original->w;
|
||||
out->h = original->h;
|
||||
out->pixels = calloc(original->w, original->h);
|
||||
out->palette = palette;
|
||||
|
||||
for (int i = 0; i < out->w * out->h; i++) {
|
||||
int closest_color = 0;
|
||||
float closest_distance = 1e20;
|
||||
for (int color = 0; color < n_colors; color++) {
|
||||
float dist = get_color_difference(colors[color], original->pixels[i]);
|
||||
for (int color = 0; color < palette->count; color++) {
|
||||
float dist = get_color_difference(palette->colors[color], original->pixels[i]);
|
||||
if (dist <= closest_distance) {
|
||||
closest_distance = dist;
|
||||
closest_color = color;
|
||||
|
@ -610,297 +619,269 @@ float get_color_brightness(union color clr) {
|
|||
return get_color_difference(clr, (union color){ .v = 0 });
|
||||
}
|
||||
|
||||
const union color DEFAULT_PALETTE[16] = {
|
||||
{ 0xf0, 0xf0, 0xf0, 0xff },
|
||||
{ 0xf2, 0xb2, 0x33, 0xff },
|
||||
{ 0xe5, 0x7f, 0xd8, 0xff },
|
||||
{ 0x99, 0xb2, 0xf2, 0xff },
|
||||
{ 0xde, 0xde, 0x6c, 0xff },
|
||||
{ 0x7f, 0xcc, 0x19, 0xff },
|
||||
{ 0xf2, 0xb2, 0xcc, 0xff },
|
||||
{ 0x4c, 0x4c, 0x4c, 0xff },
|
||||
{ 0x99, 0x99, 0x99, 0xff },
|
||||
{ 0x4c, 0x99, 0xb2, 0xff },
|
||||
{ 0xb2, 0x66, 0xe5, 0xff },
|
||||
{ 0x33, 0x66, 0xcc, 0xff },
|
||||
{ 0x7f, 0x66, 0x4c, 0xff },
|
||||
{ 0x57, 0xa6, 0x4e, 0xff },
|
||||
{ 0xcc, 0x4c, 0x4c, 0xff },
|
||||
{ 0x11, 0x11, 0x11, 0xff }
|
||||
}, DEFAULT_GRAY_PALETTE[16] = {
|
||||
{ 0xf0, 0xf0, 0xf0, 0xff },
|
||||
{ 0x9d, 0x9d, 0x9d, 0xff },
|
||||
{ 0xbe, 0xbe, 0xbe, 0xff },
|
||||
{ 0xbf, 0xbf, 0xbf, 0xff },
|
||||
{ 0xb8, 0xb8, 0xb8, 0xff },
|
||||
{ 0x76, 0x76, 0x76, 0xff },
|
||||
{ 0xd0, 0xd0, 0xd0, 0xff },
|
||||
{ 0x4c, 0x4c, 0x4c, 0xff },
|
||||
{ 0x99, 0x99, 0x99, 0xff },
|
||||
{ 0x87, 0x87, 0x87, 0xff },
|
||||
{ 0xa9, 0xa9, 0xa9, 0xff },
|
||||
{ 0x77, 0x77, 0x77, 0xff },
|
||||
{ 0x65, 0x65, 0x65, 0xff },
|
||||
{ 0x6e, 0x6e, 0x6e, 0xff },
|
||||
{ 0x76, 0x76, 0x76, 0xff },
|
||||
{ 0x11, 0x11, 0x11, 0xff }
|
||||
};
|
||||
void convert_2x3(const struct image_pal *img, struct cc_char *characters) {
|
||||
int w = img->w / 2, h = img->h / 3;
|
||||
for (int y = 0; y < h; y++) {
|
||||
if (args.verbose) {
|
||||
int sz = PROGRESS_BAR_WIDTH - (y * PROGRESS_BAR_WIDTH / h);
|
||||
printf("\r[%-" TOSTR(PROGRESS_BAR_WIDTH) ".*s|%7.3f%%|%4d/%4d]",
|
||||
PROGRESS_BAR_WIDTH - sz, progress_bar + sz,
|
||||
100.0 * (y + 1) / h,
|
||||
y + 1, h);
|
||||
fflush(stdout);
|
||||
}
|
||||
for (int x = 0; x < w; x++) {
|
||||
unsigned char darkest_i = 0, brightest_i = 0;
|
||||
float darkest_diff = 0xffffff, brightest_diff = 0;
|
||||
|
||||
const char font_atlas[256][11] = {
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x6c, 0x44, 0x54, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x7c, 0x54, 0x7c, 0x44, 0x6c, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x28, 0x7c, 0x7c, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x38, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x38, 0x10, 0x7c, 0x7c, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x38, 0x7c, 0x7c, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7e, 0x7e, 0x66, 0x42, 0x42, 0x66, 0x7e, 0x7e, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x1c, 0x0c, 0x34, 0x48, 0x48, 0x30, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x10, 0x38, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x3c, 0x24, 0x3c, 0x20, 0x60, 0x60, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x3e, 0x22, 0x3e, 0x22, 0x66, 0x66, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x40, 0x70, 0x7c, 0x70, 0x40, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x04, 0x1c, 0x7c, 0x1c, 0x04, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x38, 0x7c, 0x10, 0x10, 0x7c, 0x38, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x24, 0x24, 0x24, 0x24, 0x24, 0x00, 0x24, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x54, 0x54, 0x34, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x60, 0x58, 0x44, 0x34, 0x0c, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x38, 0x7c, 0x10, 0x7c, 0x38, 0x10, 0x7c, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x38, 0x7c, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x10, 0x10, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x18, 0x7c, 0x18, 0x10, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x30, 0x7c, 0x30, 0x10, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x24, 0x7e, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x10, 0x38, 0x38, 0x7c, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x7c, 0x38, 0x38, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x14, 0x14, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x28, 0x7c, 0x28, 0x7c, 0x28, 0x28, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x3c, 0x40, 0x38, 0x04, 0x78, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x48, 0x08, 0x10, 0x20, 0x24, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x28, 0x10, 0x34, 0x58, 0x48, 0x34, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x10, 0x20, 0x20, 0x20, 0x10, 0x0c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x08, 0x04, 0x04, 0x04, 0x08, 0x30, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x24, 0x18, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x04, 0x08, 0x08, 0x10, 0x20, 0x20, 0x40, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x4c, 0x54, 0x64, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x30, 0x10, 0x10, 0x10, 0x10, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x04, 0x18, 0x20, 0x44, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x04, 0x18, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x14, 0x24, 0x44, 0x7c, 0x04, 0x04, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x40, 0x78, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x18, 0x20, 0x40, 0x78, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x44, 0x04, 0x08, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x44, 0x38, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x44, 0x3c, 0x04, 0x08, 0x30, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x20, 0x10, 0x08, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x04, 0x08, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x42, 0x5a, 0x5a, 0x5e, 0x40, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x78, 0x44, 0x78, 0x44, 0x44, 0x44, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x40, 0x40, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x78, 0x44, 0x44, 0x44, 0x44, 0x44, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x40, 0x70, 0x40, 0x40, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x40, 0x70, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x40, 0x4c, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x44, 0x7c, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x10, 0x10, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x48, 0x70, 0x48, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x6c, 0x54, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x78, 0x44, 0x78, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x44, 0x44, 0x44, 0x48, 0x34, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x78, 0x44, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x40, 0x38, 0x04, 0x04, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x44, 0x44, 0x44, 0x28, 0x28, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x44, 0x44, 0x44, 0x54, 0x6c, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x04, 0x08, 0x10, 0x20, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x20, 0x20, 0x20, 0x20, 0x20, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x40, 0x20, 0x20, 0x10, 0x08, 0x08, 0x04, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x40, 0x40, 0x58, 0x64, 0x44, 0x44, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x38, 0x44, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x04, 0x04, 0x34, 0x4c, 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x10, 0x3c, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x3c, 0x44, 0x44, 0x3c, 0x04, 0x78, 0x00, 0x00, },
|
||||
{ 0x00, 0x40, 0x40, 0x58, 0x64, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x04, 0x00, 0x04, 0x04, 0x04, 0x44, 0x44, 0x38, 0x00, 0x00, },
|
||||
{ 0x00, 0x20, 0x20, 0x24, 0x28, 0x30, 0x28, 0x24, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x68, 0x54, 0x54, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x58, 0x64, 0x44, 0x78, 0x40, 0x40, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x34, 0x4c, 0x44, 0x3c, 0x04, 0x04, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x58, 0x64, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x3c, 0x40, 0x38, 0x04, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x38, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x28, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x44, 0x44, 0x54, 0x54, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x04, 0x78, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x7c, 0x08, 0x10, 0x20, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x10, 0x10, 0x20, 0x10, 0x10, 0x0c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x08, 0x08, 0x04, 0x08, 0x08, 0x30, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x32, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x24, 0x48, 0x12, 0x24, 0x48, 0x12, 0x24, 0x48, 0x12, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x7f, 0x7f, 0x7f, 0x7f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x38, 0x44, 0x40, 0x44, 0x38, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x18, 0x24, 0x20, 0x78, 0x20, 0x20, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x44, 0x38, 0x44, 0x44, 0x44, 0x38, 0x44, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x28, 0x7c, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x10, 0x10, 0x00, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x60, 0x58, 0x44, 0x34, 0x0c, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x3c, 0x4a, 0x52, 0x52, 0x4a, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x08, 0x38, 0x48, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x14, 0x28, 0x50, 0x28, 0x14, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x3c, 0x5a, 0x5a, 0x56, 0x42, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x48, 0x48, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10, 0x00, 0x7c, 0x00, 0x00, },
|
||||
{ 0x00, 0x40, 0x20, 0x60, 0x40, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x20, 0x60, 0x20, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x7a, 0x40, 0x40, 0x00, },
|
||||
{ 0x00, 0x3c, 0x54, 0x54, 0x34, 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x20, 0x60, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x50, 0x28, 0x14, 0x28, 0x50, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x48, 0x08, 0x10, 0x2c, 0x2c, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x44, 0x48, 0x08, 0x10, 0x24, 0x28, 0x4c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x64, 0x28, 0x68, 0x10, 0x2c, 0x2c, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x00, 0x10, 0x20, 0x40, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x50, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x00, 0x38, 0x44, 0x7c, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x3c, 0x50, 0x50, 0x78, 0x50, 0x50, 0x5c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x40, 0x40, 0x44, 0x38, 0x08, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x7c, 0x40, 0x78, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x7c, 0x40, 0x78, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x7c, 0x40, 0x78, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x7c, 0x40, 0x78, 0x40, 0x7c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x00, 0x38, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x18, 0x00, 0x38, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x28, 0x38, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x38, 0x10, 0x10, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x78, 0x44, 0x44, 0x64, 0x44, 0x44, 0x78, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x14, 0x28, 0x44, 0x64, 0x54, 0x4c, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x38, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x38, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x50, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x38, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x4c, 0x54, 0x64, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x28, 0x00, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x44, 0x28, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x10, 0x18, 0x14, 0x18, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x78, 0x44, 0x58, 0x44, 0x44, 0x44, 0x58, 0x40, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x50, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x00, 0x38, 0x04, 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x2c, 0x52, 0x7c, 0x50, 0x2e, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x38, 0x44, 0x40, 0x44, 0x38, 0x08, 0x10, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x38, 0x44, 0x7c, 0x40, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x18, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x28, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x08, 0x3c, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x50, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x38, 0x44, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x50, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x10, 0x00, 0x7c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x00, 0x00, 0x38, 0x4c, 0x54, 0x64, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x60, 0x00, 0x44, 0x44, 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x44, 0x44, 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x10, 0x28, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x44, 0x44, 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x0c, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x04, 0x78, 0x00, 0x00, },
|
||||
{ 0x00, 0x30, 0x10, 0x18, 0x14, 0x18, 0x10, 0x38, 0x00, 0x00, 0x00, },
|
||||
{ 0x00, 0x28, 0x00, 0x44, 0x44, 0x44, 0x3c, 0x04, 0x78, 0x00, 0x00, },
|
||||
};
|
||||
for (int oy = 0; oy < 3; oy++) {
|
||||
for (int ox = 0; ox < 2; ox++) {
|
||||
unsigned char pix = img->pixels[ox + (x + (y * 3 + oy) * w) * 2];
|
||||
float brightness = get_color_brightness(img->palette->colors[pix]);
|
||||
if (brightness >= brightest_diff) {
|
||||
brightest_i = pix;
|
||||
brightest_diff = brightness;
|
||||
}
|
||||
if (brightness <= darkest_diff) {
|
||||
darkest_i = pix;
|
||||
darkest_diff = brightness;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char bitmap = 0;
|
||||
const static unsigned char pixel_bits[3][2] = { { 1, 2}, { 4, 8 }, { 16, 0 } };
|
||||
for (int oy = 0; oy < 3; oy++) {
|
||||
for (int ox = 0; ox < 2; ox++) {
|
||||
if (ox == 1 && oy == 2) continue;
|
||||
unsigned char pix = img->pixels[ox + (x + (y * 3 + oy) * w) * 2];
|
||||
float diff_bg = get_color_difference(img->palette->colors[darkest_i], img->palette->colors[pix]);
|
||||
float diff_fg = get_color_difference(img->palette->colors[brightest_i], img->palette->colors[pix]);
|
||||
if (diff_fg < diff_bg) {
|
||||
bitmap |= pixel_bits[oy][ox];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
unsigned char pix = img->pixels[1 + (x + (y * 3 + 2) * w) * 2];
|
||||
float diff_bg = get_color_difference(img->palette->colors[darkest_i], img->palette->colors[pix]);
|
||||
float diff_fg = get_color_difference(img->palette->colors[brightest_i], img->palette->colors[pix]);
|
||||
if (diff_fg < diff_bg) {
|
||||
bitmap ^= 31;
|
||||
unsigned char tmp = darkest_i;
|
||||
darkest_i = brightest_i;
|
||||
brightest_i = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
characters[x + y * w].character = 0x80 + bitmap;
|
||||
characters[x + y * w].bg = darkest_i;
|
||||
characters[x + y * w].fg = brightest_i;
|
||||
}
|
||||
}
|
||||
if (args.verbose) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void convert_6x9(const struct image_pal *img, struct cc_char *characters) {
|
||||
int w = img->w / 6, h = img->h / 9;
|
||||
float palette_self_diffs[0x100][0x10] = {{(float) 0xffffff}};
|
||||
for (int input_color = 0x0; input_color < 0x100 && input_color < img->palette->count; input_color++) {
|
||||
for (int output_color = 0x0; output_color < 0x10 && output_color < img->palette->count; output_color++) {
|
||||
palette_self_diffs[input_color][output_color] = get_color_difference(img->palette->colors[input_color], img->palette->colors[output_color]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 0; y < h; y++) {
|
||||
if (args.verbose) {
|
||||
int sz = PROGRESS_BAR_WIDTH - (y * PROGRESS_BAR_WIDTH / h);
|
||||
printf("\r[%-" TOSTR(PROGRESS_BAR_WIDTH) ".*s|%7.3f%%|%4d/%4d]",
|
||||
PROGRESS_BAR_WIDTH - sz, progress_bar + sz,
|
||||
100.0 * (y + 1) / h,
|
||||
y + 1, h);
|
||||
fflush(stdout);
|
||||
}
|
||||
#ifdef USE_OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int x = 0; x < w; x++) {
|
||||
float chunk_palette_diffs[6][9][0x10] = {{{(float) 0xffffff}}};
|
||||
for (int ox = 0; ox < 6; ox++) {
|
||||
for (int oy = 0; oy < 9; oy++) {
|
||||
uint8_t pixel_unresolved = img->pixels[
|
||||
ox + (x + (y * 9 + oy) * w) * 6
|
||||
];
|
||||
for (int color = 0x0; color < 0x10 && color < img->palette->count; color++) {
|
||||
chunk_palette_diffs[ox][oy][color] = palette_self_diffs[pixel_unresolved][color];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float min_diff = 0xffffff;
|
||||
char closest_sym = 0x00, closest_color = 0xae;
|
||||
for (int sym = 0x01; sym <= 0xFF; sym++) {
|
||||
if (sym == '\t' || sym == '\n' || sym == '\r' || sym == '\x0e') {
|
||||
continue;
|
||||
}
|
||||
for (int color = 0x00; color <= 0xff; color++) {
|
||||
float difference = 0;
|
||||
for (int oy = 0; oy < 9; oy++) {
|
||||
unsigned char sym_line = cc_font_atlas[sym][oy];
|
||||
for (int ox = 0; ox < 6; ox++) {
|
||||
bool lit = sym_line & (0x80 >> ox);
|
||||
difference += chunk_palette_diffs[ox][oy][lit ? color >> 4 : color & 0xF];
|
||||
}
|
||||
}
|
||||
if (difference <= min_diff) {
|
||||
min_diff = difference;
|
||||
closest_sym = sym;
|
||||
closest_color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
characters[x + y * w].character = closest_sym;
|
||||
characters[x + y * w].bg = closest_color & 0xF;
|
||||
characters[x + y * w].fg = closest_color >> 4;
|
||||
}
|
||||
}
|
||||
if (args.verbose) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
struct {
|
||||
uint8_t count;
|
||||
union color colors[256];
|
||||
} custom_palette_data;
|
||||
|
||||
struct palette *custom_palette_resize(uint8_t size) {
|
||||
custom_palette_data.count = size;
|
||||
return (struct palette*)&custom_palette_data;
|
||||
}
|
||||
|
||||
struct palette *custom_palette_from(const struct palette *orig) {
|
||||
custom_palette_data.count = orig->count;
|
||||
for (int i = 0; i < custom_palette_data.count; i++) {
|
||||
custom_palette_data.colors[i] = orig->colors[i];
|
||||
}
|
||||
return (struct palette*)&custom_palette_data;
|
||||
}
|
||||
|
||||
struct k_means_state k_means_init(const struct image *image, struct palette *starting_palette) {
|
||||
size_t item_count = image->w * image->h;
|
||||
uint8_t cluster_count = starting_palette->count;
|
||||
struct k_means_state state = {
|
||||
.items = image,
|
||||
.clusters = starting_palette,
|
||||
.predicted_cluster = calloc(image->w, image->h),
|
||||
.centroid_intermediate = calloc(cluster_count, sizeof(struct k_means_centroid_intermediate)),
|
||||
.item_count = item_count,
|
||||
};
|
||||
if (state.centroid_intermediate) {
|
||||
for (size_t i = 0; i < cluster_count; i++) {
|
||||
state.centroid_intermediate[i].closest_present_item = starting_palette->colors[i];
|
||||
state.centroid_intermediate[i].closest_present_distance = 1e20;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
bool k_means_iteration(struct k_means_state *state) {
|
||||
if (!state->predicted_cluster || !state->centroid_intermediate) {
|
||||
return false;
|
||||
}
|
||||
bool changed = false;
|
||||
|
||||
// Find closest cluster
|
||||
for (int i = 0; i < state->item_count; i++) {
|
||||
int closest_cluster = 0;
|
||||
float closest_distance = 1e20;
|
||||
for (int cluster = 0; cluster < state->clusters->count; cluster++) {
|
||||
union color item = state->items->pixels[i];
|
||||
float dist = get_color_difference(state->clusters->colors[cluster], item);
|
||||
if (dist <= closest_distance) {
|
||||
closest_distance = dist;
|
||||
closest_cluster = cluster;
|
||||
}
|
||||
if (dist < state->centroid_intermediate[cluster].closest_present_distance) {
|
||||
bool can_update = true;
|
||||
for (int other_cluster = 0; other_cluster < state->clusters->count; other_cluster++) {
|
||||
if (other_cluster == cluster) {
|
||||
continue;
|
||||
}
|
||||
if (state->centroid_intermediate[other_cluster].closest_present_item.v == item.v) {
|
||||
can_update = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (can_update) {
|
||||
state->centroid_intermediate[cluster].closest_present_item = item;
|
||||
state->centroid_intermediate[cluster].closest_present_distance = dist;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!changed) {
|
||||
changed = state->predicted_cluster[i] != closest_cluster;
|
||||
}
|
||||
state->predicted_cluster[i] = closest_cluster;
|
||||
state->centroid_intermediate[closest_cluster].count += 1;
|
||||
state->centroid_intermediate[closest_cluster].sums.r += state->items->pixels[i].rgba.r;
|
||||
state->centroid_intermediate[closest_cluster].sums.g += state->items->pixels[i].rgba.g;
|
||||
state->centroid_intermediate[closest_cluster].sums.b += state->items->pixels[i].rgba.b;
|
||||
}
|
||||
|
||||
// Update centroids
|
||||
for (int i = 0; i < state->clusters->count; ++i) {
|
||||
struct k_means_centroid_intermediate intermediate = state->centroid_intermediate[i];
|
||||
if (intermediate.count) {
|
||||
union color centroid = {
|
||||
.rgba = {
|
||||
.r = intermediate.sums.r / intermediate.count,
|
||||
.g = intermediate.sums.g / intermediate.count,
|
||||
.b = intermediate.sums.b / intermediate.count,
|
||||
.a = 0xff,
|
||||
}
|
||||
};
|
||||
if (!changed) {
|
||||
changed = state->clusters->colors[i].v != centroid.v;
|
||||
}
|
||||
state->clusters->colors[i] = centroid;
|
||||
} else {
|
||||
// No pixels are closest to this color
|
||||
// Warp the centroid onto the closest item
|
||||
state->clusters->colors[i] = intermediate.closest_present_item;
|
||||
}
|
||||
state->centroid_intermediate[i] = (struct k_means_centroid_intermediate) { .sums = {0, 0, 0}, .count = 0, .closest_present_item = state->clusters->colors[i], .closest_present_distance = 1e20 };
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void k_means_end(struct k_means_state *state) {
|
||||
if (state->predicted_cluster) {
|
||||
free(state->predicted_cluster);
|
||||
}
|
||||
if (state->centroid_intermediate) {
|
||||
free(state->centroid_intermediate);
|
||||
}
|
||||
}
|
||||
|
||||
struct palette *palette_k_means(const struct image *image, const struct palette *prototype) {
|
||||
if (!prototype) {
|
||||
prototype = &cc_default_palette;
|
||||
}
|
||||
struct palette *palette = custom_palette_from(prototype);
|
||||
|
||||
struct k_means_state state = k_means_init(image, palette);
|
||||
for (int i = 0; i < K_MEANS_ITERATIONS; i++) {
|
||||
if (!k_means_iteration(&state)) {
|
||||
fprintf(stderr, "early k-means stop at iteration %d\n", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
k_means_end(&state);
|
||||
|
||||
return palette;
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 131 B |
|
@ -0,0 +1,82 @@
|
|||
local args = { ... }
|
||||
local ccpi = require("ccpi")
|
||||
|
||||
local bit = {
|
||||
band = function(a, b) return a & b end,
|
||||
bor = function(a, b) return a | b end,
|
||||
blshift = function(a, b) return a << b end,
|
||||
brshift = function(a, b) return a >> b end,
|
||||
}
|
||||
|
||||
local function write_varint(fp, value)
|
||||
value = bit.band(value, 0xFFFFFFFF)
|
||||
mask = 0xFFFFFF80
|
||||
while true do
|
||||
if bit.band(value, mask) == 0 then
|
||||
fp:write(string.char(bit.band(value, 0xff)))
|
||||
return
|
||||
end
|
||||
|
||||
fp:write(string.char(bit.bor(0x80, bit.band(value, 0x7f))))
|
||||
value = bit.brshift(value, 7)
|
||||
end
|
||||
end
|
||||
|
||||
local function write_palette(fp, pal)
|
||||
for i = 1, 16 do
|
||||
fp:write(string.char(
|
||||
bit.brshift(pal[i], 16),
|
||||
bit.band(bit.brshift(pal[i], 8), 0xff),
|
||||
bit.band(pal[i], 0xff)
|
||||
))
|
||||
end
|
||||
end
|
||||
|
||||
local function write_pixeldata_v0(img, fp)
|
||||
for y = 1, img.h do
|
||||
for x = 1, img.w do
|
||||
fp:write(img.lines[y].s:sub(x, x))
|
||||
local bg = tonumber(img.lines[y].bg:sub(x, x), 16)
|
||||
local fg = tonumber(img.lines[y].fg:sub(x, x), 16)
|
||||
fp:write(string.char(bit.bor(bg, bit.blshift(fg, 4))))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local fp_out = io.open(table.remove(args, 1), "wb")
|
||||
fp_out:write("CPI" .. string.char(0x80))
|
||||
|
||||
local fp = io.open(table.remove(args, 1), "rb")
|
||||
print(fp, fp.close)
|
||||
local img, err = ccpi.parse({
|
||||
read = function(sz) return fp:read(sz) end,
|
||||
})
|
||||
fp:close()
|
||||
if err ~= nil then
|
||||
printError(err)
|
||||
return
|
||||
end
|
||||
|
||||
write_varint(fp_out, img.w)
|
||||
write_varint(fp_out, img.h)
|
||||
write_varint(fp_out, #args)
|
||||
|
||||
write_palette(fp_out, img.palette)
|
||||
write_pixeldata_v0(img, fp_out)
|
||||
|
||||
for i, arg in ipairs(args) do
|
||||
print(arg)
|
||||
local fp = io.open(arg, "rb")
|
||||
img, err = ccpi.parse({
|
||||
read = function(sz) return fp:read(sz) end,
|
||||
})
|
||||
fp:close()
|
||||
if err ~= nil then
|
||||
printError(err)
|
||||
return
|
||||
end
|
||||
write_palette(fp_out, img.palette)
|
||||
write_pixeldata_v0(img, fp_out)
|
||||
end
|
||||
|
||||
fp_out:close()
|
After Width: | Height: | Size: 2.8 KiB |
|
@ -0,0 +1,25 @@
|
|||
from PIL import Image
|
||||
from collections import Counter
|
||||
|
||||
with Image.open("./cc_font.png") as im:
|
||||
pixels = im.load()
|
||||
weights = [0 for _ in range(6 * 9)]
|
||||
for char in range(256):
|
||||
ctx, cty = (char % 16) * 8, (char // 16) * 11
|
||||
for oy in range(9):
|
||||
for ox in range(6):
|
||||
pix = int(pixels[ctx + ox + 1, cty + oy + 1][0]) # type: ignore
|
||||
weights[ox + 6 * oy] += 1 if pix else 0
|
||||
|
||||
with Image.new("L", (6, 9), 0) as im_out:
|
||||
for y in range(9):
|
||||
for x in range(6):
|
||||
print("%3d" % weights[x + 6 * y], end="\t")
|
||||
im_out.putpixel((x, y), weights[x + 6 * y])
|
||||
print()
|
||||
|
||||
im_out.save("avg.png")
|
||||
|
||||
print(dict(enumerate([
|
||||
iv[0] for iv in sorted(enumerate(weights), key=lambda iv: iv[1])
|
||||
])))
|
|
@ -0,0 +1,65 @@
|
|||
|
||||
from typing import Any, Literal
|
||||
from websockets import connect
|
||||
|
||||
HexDigit = Literal[
|
||||
"0", "1", "2", "3", "4", "5", "6", "7",
|
||||
"8", "9", "a", "b", "c", "d", "e", "f"
|
||||
]
|
||||
|
||||
class Packet:
|
||||
packet_id: int
|
||||
side: Literal["client", "server"]
|
||||
|
||||
def __init_subclass__(cls, packet_id: int, side: Literal["client", "server"]) -> None:
|
||||
cls.packet_id = packet_id
|
||||
cls.side = side
|
||||
|
||||
class PacketServerPing(Packet, packet_id=2, side="server"):
|
||||
pass
|
||||
|
||||
class PacketClientPong(Packet, packet_id=2, side="client"):
|
||||
pass
|
||||
|
||||
class PacketServerCapabilities(Packet, packet_id=0, side="server"):
|
||||
clients: int
|
||||
capabilities: list[str]
|
||||
|
||||
class PacketServerComputer(Packet, packet_id=18, side="server"):
|
||||
id: int
|
||||
label: str
|
||||
|
||||
class PacketServerScreen(Packet, packet_id=16, side="server"):
|
||||
cursorBlink: bool
|
||||
width: int
|
||||
height: int
|
||||
cursorX: int
|
||||
cursorY: int
|
||||
curFore: HexDigit
|
||||
curBack: HexDigit
|
||||
text: list[str]
|
||||
back: list[str]
|
||||
fore: list[str]
|
||||
palette: list[int]
|
||||
|
||||
class CCEvent:
|
||||
name: str
|
||||
args: list[Any]
|
||||
|
||||
class PacketClientEvent(Packet, packet_id=17, side="client"):
|
||||
events: list[CCEvent]
|
||||
|
||||
class CCAction:
|
||||
action: int
|
||||
|
||||
def __init_subclass__(cls, action: int) -> None:
|
||||
cls.action = action
|
||||
|
||||
class CCActionFile(CCAction, action=0):
|
||||
file: str
|
||||
contents: str
|
||||
checksum: int
|
||||
|
||||
class PacketServerFile(Packet, packet_id=34, side="server"):
|
||||
actions: list[CCAction]
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
# x-run: python3 % ~/downloads/moZtoMP7HAA.mp4 /tmp/video.cani
|
||||
from typing import Literal
|
||||
from dataclasses import dataclass
|
||||
from struct import pack
|
||||
from sys import argv
|
||||
from subprocess import Popen, PIPE, run
|
||||
from tqdm import tqdm
|
||||
from tempfile import TemporaryDirectory
|
||||
from glob import glob
|
||||
from PIL import Image
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
PIX_BITS = [[1, 2], [4, 8], [16, 0]]
|
||||
|
||||
|
||||
@dataclass
|
||||
class VideoMetadata:
|
||||
framerate: Literal[20, 10, 5] = 10
|
||||
audio_channels: Literal[1, 2] = 2
|
||||
sample_rate: Literal[12000, 24000, 48000] = 48000
|
||||
screen_width: int = 164
|
||||
screen_height: int = 81
|
||||
|
||||
@property
|
||||
def audio_samples_per_frame(self) -> int:
|
||||
return self.sample_rate // self.framerate
|
||||
|
||||
def serialize(self) -> bytes:
|
||||
return bytes([ self.framerate, self.audio_channels ]) \
|
||||
+ pack("<HHH", self.sample_rate, self.screen_width, self.screen_height)
|
||||
|
||||
@dataclass
|
||||
class VideoFrame:
|
||||
audio: list[bytes]
|
||||
video: list[bytes]
|
||||
palette: list[int]
|
||||
|
||||
def serialize(self) -> bytes:
|
||||
return bytes.join(b"", self.audio) \
|
||||
+ bytes.join(b"", self.video) \
|
||||
+ bytes.join(b"", [ bytes.fromhex("%06x" % color) for color in self.palette ])
|
||||
|
||||
@lru_cache
|
||||
def _brightness(palette: tuple, i: int) -> float:
|
||||
r, g, b = palette[i * 3 : (i + 1) * 3]
|
||||
return (r + g + b) / 768
|
||||
|
||||
@lru_cache
|
||||
def _distance(palette: tuple, a: int, b: int) -> float:
|
||||
r1, g1, b1 = palette[a * 3 : (a + 1) * 3]
|
||||
r2, g2, b2 = palette[b * 3 : (b + 1) * 3]
|
||||
rd, gd, bd = r1 - r2, g1 - g2, b1 - b2
|
||||
return (rd * rd + gd * gd + bd * bd) / 1966608
|
||||
|
||||
@lru_cache
|
||||
def _get_colors(imgdata, palette: tuple, x: int, y: int) -> tuple[int, int]:
|
||||
brightest_i, brightest_l = 0, 0
|
||||
darkest_i, darkest_l = 0, 768
|
||||
for oy, line in enumerate(PIX_BITS):
|
||||
for ox in range(len(line)):
|
||||
pix = imgdata[x + ox, y + oy]
|
||||
assert pix < 16, f"{pix} is too big at {x+ox}:{y+oy}"
|
||||
brightness = _brightness(palette, pix)
|
||||
if brightness > brightest_l:
|
||||
brightest_l, brightest_i = brightness, pix
|
||||
if brightness < darkest_l:
|
||||
darkest_l, darkest_i = brightness, pix
|
||||
return darkest_i, brightest_i
|
||||
|
||||
@lru_cache()
|
||||
def _is_darker(palette: tuple, bg: int, fg: int, c: int) -> bool:
|
||||
return _distance(palette, bg, c) < _distance(palette, fg, c)
|
||||
|
||||
def _get_block(imgdata, palette: tuple, x: int, y: int) -> tuple[int, int, int]:
|
||||
dark_i, bri_i = _get_colors(imgdata, palette, x, y)
|
||||
assert dark_i < 16, f"{dark_i} is too big"
|
||||
assert bri_i < 16, f"{bri_i} is too big"
|
||||
out: int = 0
|
||||
for oy, line in enumerate(PIX_BITS):
|
||||
for ox, bit in enumerate(line):
|
||||
if not _is_darker(
|
||||
palette, dark_i, bri_i, imgdata[x + ox, y + oy]
|
||||
):
|
||||
out |= bit
|
||||
# bottom right pixel fix?
|
||||
if not _is_darker(palette, dark_i, bri_i, imgdata[x + 1, y + 2]):
|
||||
out ^= 31
|
||||
dark_i, bri_i = bri_i, dark_i
|
||||
return out, dark_i, bri_i
|
||||
|
||||
metadata = VideoMetadata(
|
||||
framerate=20,
|
||||
audio_channels=2,
|
||||
sample_rate=24000,
|
||||
screen_width=164,
|
||||
screen_height=81
|
||||
)
|
||||
|
||||
input_video = argv[1]
|
||||
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
run([
|
||||
"ffmpeg",
|
||||
"-i", input_video,
|
||||
"-f", "s8",
|
||||
"-ac", str(metadata.audio_channels),
|
||||
"-ar", str(metadata.sample_rate),
|
||||
f"{tmpdir}/audio.s8"
|
||||
])
|
||||
|
||||
run([
|
||||
"ffmpeg",
|
||||
"-i", input_video,
|
||||
"-an",
|
||||
"-r", str(metadata.framerate),
|
||||
"-vf", f"scale={metadata.screen_width * 2}:{metadata.screen_height * 3}",
|
||||
f"{tmpdir}/video%06d.jpg"
|
||||
])
|
||||
|
||||
with open(argv[2], "w") as fp_out, open(f"{tmpdir}/audio.s8", "rb") as fp_audio:
|
||||
print(metadata.serialize().hex(), file=fp_out)
|
||||
for i, frame_path in tqdm(enumerate(glob(f"{tmpdir}/video*.jpg"))):
|
||||
with Image.open(frame_path) as img_in:
|
||||
img_in = img_in.convert("P", palette=Image.Palette.ADAPTIVE, colors=16)
|
||||
img_data = img_in.load()
|
||||
img_palette = tuple(img_in.getpalette()) # type: ignore
|
||||
|
||||
audio_samples = fp_audio.read(metadata.audio_samples_per_frame * metadata.audio_channels)
|
||||
|
||||
frame = VideoFrame(
|
||||
[
|
||||
audio_samples[i::metadata.audio_channels]
|
||||
for i in range(metadata.audio_channels)
|
||||
],
|
||||
[],
|
||||
[
|
||||
(r << 16) | (g << 8) | b
|
||||
for r, g, b
|
||||
in zip(img_palette[0::3], img_palette[1::3], img_palette[2::3]) # type: ignore
|
||||
])
|
||||
|
||||
for y in range(0, img_in.height - 2, 3):
|
||||
line = bytearray()
|
||||
for x in range(0, img_in.width - 1, 2):
|
||||
ch, bg, fg = _get_block(img_data, img_palette, x, y)
|
||||
line.extend([(ch + 0x80) & 0xFF, fg << 4 | bg])
|
||||
frame.video.append(line)
|
||||
|
||||
print(frame.serialize().hex(), file=fp_out)
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
local config = {
|
||||
{ filter = ".*charged.*", from = "ae2:charger", to = "create:basin" },
|
||||
{ filter = "!.*charged.*", from = "create:basin", to = "ae2:charger", limit = 1 }
|
||||
}
|
||||
|
||||
local function is_matching(pattern, value)
|
||||
if pattern:sub(1, 1) == "!" then
|
||||
return not is_matching(pattern:sub(2))
|
||||
end
|
||||
return value:match(pattern) ~= nil
|
||||
end
|
||||
|
||||
|
||||
local items = {}
|
||||
|
||||
parallel.waitForAll(function() -- Inventory listener
|
||||
while true do
|
||||
local new_items = {}
|
||||
peripheral.find("inventory", function(inv)
|
||||
local inv_items = peripheral.call(inv, "list")
|
||||
for slot, item in pairs(inv_items) do
|
||||
new_items[string.format("%s#%d", inv, slot)] = item
|
||||
end
|
||||
end)
|
||||
items = new_items
|
||||
os.sleep(0)
|
||||
end
|
||||
end, function() -- Executor
|
||||
while true do
|
||||
for i, rule in ipairs(config) do
|
||||
|
||||
end
|
||||
os.sleep(0)
|
||||
end
|
||||
end)
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
peripheral.find("modem", function(name)
|
||||
rednet.open(name)
|
||||
print("Opened modem " .. name .. " for rednet connections")
|
||||
end)
|
||||
|
||||
print("Hostname: " .. os.getComputerLabel())
|
||||
print("ID: " .. os.getComputerID())
|
||||
rednet.host("ramfs", os.getComputerLabel())
|
||||
|
||||
parallel.waitForAll(function()
|
||||
while true do
|
||||
local ev = { os.pullEvent() }
|
||||
if ev[1] == "ramfs:shutdown" then
|
||||
break
|
||||
end
|
||||
print(table.unpack(ev))
|
||||
end
|
||||
end, function() -- Shutdown routine
|
||||
os.pullEvent("ramfs:shutdown")
|
||||
end)
|
|
@ -0,0 +1,84 @@
|
|||
{
|
||||
"junk": [
|
||||
"metalbarrels:gold_tile_4"
|
||||
],
|
||||
"storage": [
|
||||
"create:item_vault_2",
|
||||
"create:item_vault_1"
|
||||
],
|
||||
"pull": [
|
||||
"minecraft:barrel_1",
|
||||
"NOT IMPLEMENTED"
|
||||
],
|
||||
"push": [
|
||||
{ "name": "*_nugget", "to": "metalbarrels:gold_tile_8" },
|
||||
{ "name": "*_ingot", "to": "metalbarrels:gold_tile_8" },
|
||||
{ "name": "minecraft:*coal", "to": "metalbarrels:gold_tile_9" },
|
||||
{ "name": "NOT IMPLEMENTED", "to": "NOT IMPLEMENTED" }
|
||||
],
|
||||
"stock": {
|
||||
"metalbarrels:gold_tile_13#1": { "name": "minecraft:spruce_log", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#2": { "name": "minecraft:spruce_log", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#3": { "name": "minecraft:spruce_planks", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#10": { "name": "minecraft:oak_log", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#11": { "name": "minecraft:oak_log", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#12": { "name": "minecraft:oak_planks", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#19": { "name": "minecraft:dark_oak_log", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#20": { "name": "minecraft:dark_oak_log", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#21": { "name": "minecraft:dark_oak_planks", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#4": { "name": "minecraft:cobblestone", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#5": { "name": "minecraft:cobblestone", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#6": { "name": "minecraft:stone", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#13": { "name": "minecraft:cobbled_deepslate", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#14": { "name": "minecraft:cobbled_deepslate", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#15": { "name": "minecraft:deepslate", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#7": { "name": "minecraft:dirt", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#16": { "name": "minecraft:sand", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#25": { "name": "minecraft:gravel", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#8": { "name": "minecraft:netherrack", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#17": { "name": "minecraft:soul_sand", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#22": { "name": "minecraft:andesite", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#23": { "name": "minecraft:diorite", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#24": { "name": "minecraft:granite", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#31": { "name": "minecraft:tuff", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#32": { "name": "expcaves:sediment_stone", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#33": { "name": "expcaves:lavastone", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#34": { "name": "expcaves:dirtstone", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#42": { "name": "minecraft:calcite", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#43": { "name": "minecraft:clay_ball", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#40": { "name": "create:limestone", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#49": { "name": "minecraft:stone_bricks", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#50": { "name": "minecraft:cracked_stone_bricks", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#51": { "name": "minecraft:moss_block", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#41": { "name": "forbidden_arcanus:darkstone", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#73": { "name": "minecraft:apple", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#74": { "name": "minecraft:kelp", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#75": { "name": "minecraft:oak_sapling", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#76": { "name": "minecraft:spruce_sapling", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#77": { "name": "minecraft:dark_oak_sapling", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#81": { "name": "minecraft:stick", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#26": { "name": "minecraft:nether_bricks", "count": 64 },
|
||||
"metalbarrels:gold_tile_13#35": { "name": "minecraft:magma_block", "count": 64 },
|
||||
|
||||
"metalbarrels:gold_tile_6#1": { "name": "kubejs:kinetic_mechanism", "count": 64 },
|
||||
"metalbarrels:gold_tile_6#2": { "name": "kubejs:kinetic_mechanism", "count": 64 },
|
||||
"metalbarrels:gold_tile_6#10": { "name": "create:andesite_alloy", "count": 64 },
|
||||
"metalbarrels:gold_tile_6#11": { "name": "create:andesite_alloy", "count": 64 },
|
||||
"metalbarrels:gold_tile_6#12": { "name": "thermal:cured_rubber", "count": 64 },
|
||||
"metalbarrels:gold_tile_6#19": { "name": "thermal:silver_coin", "count": 64 },
|
||||
"metalbarrels:gold_tile_6#20": { "name": "thermal:silver_coin", "count": 64 },
|
||||
"metalbarrels:gold_tile_6#21": { "name": "thermal:silver_coin", "count": 64 },
|
||||
|
||||
"metalbarrels:gold_tile_7#1": { "name": "minecraft:lapis_lazuli", "count": 64 },
|
||||
"metalbarrels:gold_tile_7#2": { "name": "minecraft:redstone", "count": 64 },
|
||||
"metalbarrels:gold_tile_7#3": { "name": "minecraft:coal", "count": 15 },
|
||||
"metalbarrels:gold_tile_7#4": { "name": "thermal:apatite", "count": 64 },
|
||||
"metalbarrels:gold_tile_7#5": { "name": "thermal:sulfur", "count": 64 },
|
||||
"metalbarrels:gold_tile_7#6": { "name": "thermal:niter", "count": 64 },
|
||||
"metalbarrels:gold_tile_7#7": { "name": "thermal:cinnabar", "count": 64 },
|
||||
"metalbarrels:gold_tile_7#10": { "name": "ae2:certus_quartz_dust", "count": 64 },
|
||||
"metalbarrels:gold_tile_7#11": { "name": "ae2:certus_crystal_seed", "count": 64 },
|
||||
"metalbarrels:gold_tile_7#12": { "name": "forbidden_arcanus:xpetrified_orb", "count": 16 }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,434 @@
|
|||
|
||||
local function draw_bar(y, c1, c2, p, fmt, ...)
|
||||
local str = string.format(fmt, ...)
|
||||
local tw = term.getSize()
|
||||
local w1 = math.ceil(p * tw)
|
||||
local w2 = tw - w1
|
||||
|
||||
local old_bg = term.getBackgroundColor()
|
||||
term.setCursorPos(1, y)
|
||||
term.setBackgroundColor(c1)
|
||||
term.write(str:sub(1, w1))
|
||||
local rem = w1 - #str
|
||||
if rem > 0 then
|
||||
term.write(string.rep(" ", rem))
|
||||
end
|
||||
|
||||
term.setBackgroundColor(c2)
|
||||
term.write(str:sub(w1 + 1, w1 + w2))
|
||||
|
||||
rem = math.min(tw - #str, w2)
|
||||
if rem > 0 then
|
||||
term.write(string.rep(" ", rem))
|
||||
end
|
||||
|
||||
term.setBackgroundColor(old_bg)
|
||||
end
|
||||
|
||||
function table.deepcopy(obj)
|
||||
if type(obj) ~= 'table' then return obj end
|
||||
local res = {}
|
||||
for k, v in pairs(obj) do res[table.deepcopy(k)] = table.deepcopy(v) end
|
||||
return res
|
||||
end
|
||||
|
||||
function spairs(t, order)
|
||||
-- collect the tbl_keys
|
||||
local tbl_keys = {}
|
||||
for k in pairs(t) do tbl_keys[#tbl_keys+1] = k end
|
||||
|
||||
-- if order function given, sort by it by passing the table and tbl_keys a, b,
|
||||
-- otherwise just sort the tbl_keys
|
||||
if order then
|
||||
table.sort(tbl_keys, function(a,b) return order(t, a, b) end)
|
||||
else
|
||||
table.sort(tbl_keys)
|
||||
end
|
||||
|
||||
-- return the iterator function
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
if tbl_keys[i] then
|
||||
return tbl_keys[i], t[tbl_keys[i]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function string.split(inputstr, sep)
|
||||
sep = sep or "%s"
|
||||
local t = {}
|
||||
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
|
||||
table.insert(t, str)
|
||||
end
|
||||
return table.unpack(t)
|
||||
end
|
||||
|
||||
function map(tbl, fn)
|
||||
local out = {}
|
||||
for k, v in pairs(tbl) do
|
||||
out[k] = fn(k, v, tbl)
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
function filter(tbl, fil)
|
||||
local out = {}
|
||||
for k, v in pairs(tbl) do
|
||||
if fil(k, v, tbl) then
|
||||
out[k] = v
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
function pop(tbl)
|
||||
local out = { k = nil, v = nil }
|
||||
for k, v in pairs(tbl) do
|
||||
out.k = k
|
||||
out.v = v
|
||||
table.remove(tbl, k)
|
||||
break
|
||||
end
|
||||
return out.k, out.v
|
||||
end
|
||||
|
||||
function groupby(tbl, fn)
|
||||
local out = {}
|
||||
for k, v in pairs(tbl) do
|
||||
local group = fn(k, v, tbl)
|
||||
out[group] = out[group] or {}
|
||||
table.insert(out[group], { k = k, v = v })
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
function reduce(tbl, operator, initial)
|
||||
local accumulator = initial
|
||||
for k, v in pairs(tbl) do
|
||||
accumulator = operator(k, v, accumulator, tbl)
|
||||
end
|
||||
return accumulator
|
||||
end
|
||||
|
||||
function pipe(input)
|
||||
local function chain(fn)
|
||||
return function(self, ...)
|
||||
fn(self, ...)
|
||||
return self
|
||||
end
|
||||
end
|
||||
return {
|
||||
_value = table.deepcopy(input),
|
||||
groupby = chain(function(self, fn) self._value = groupby(self._value, fn) end),
|
||||
filter = chain(function(self, fn) self._value = filter(self._value, fn) end),
|
||||
map = chain(function(self, fn) self._value = map(self._value, fn) end),
|
||||
reduce = chain(function(self, operator, initial) self._value = reduce(self._value, operator, initial) end),
|
||||
sort = chain(function(self, key)
|
||||
local out = {}
|
||||
for k, v in spairs(self._value, key) do
|
||||
table.insert(out, { k = k, v = v })
|
||||
end
|
||||
self._value = out
|
||||
end),
|
||||
get = function(self) return self._value end
|
||||
}
|
||||
end
|
||||
|
||||
local keyboard = {
|
||||
"qwertyuiop",
|
||||
"asdfghjkl\x1b",
|
||||
"zxcvbnm \xd7",
|
||||
}
|
||||
|
||||
local config = { stock = {}, trash = {}, take = {}, storage = {} }
|
||||
local item_state = { stock = {}, storage = {}, junk = {} }
|
||||
local storage_sizes = { stock = {}, storage = {}, junk = {} }
|
||||
local sort_mode, sort_inverse = "count", false
|
||||
local search_open, search_query = false, ""
|
||||
|
||||
local mon = peripheral.find("monitor")
|
||||
mon.setTextScale(0.5)
|
||||
mon.clear()
|
||||
mon.setPaletteColor(colors.blue, 0x131326) -- odd lines
|
||||
mon.setPaletteColor(colors.purple, 0x261326) -- even lines
|
||||
mon.setPaletteColor(colors.magenta, 0xaa55ff) -- search query
|
||||
mon.setPaletteColor(colors.cyan, 0x55aaff) -- keyboard
|
||||
mon.setPaletteColor(colors.brown, 0x262626) -- keyboard bg
|
||||
|
||||
parallel.waitForAll(function()
|
||||
while true do
|
||||
local fp = assert(io.open("/restock.json", "r"))
|
||||
config = textutils.unserializeJSON(fp:read("a"))
|
||||
fp:close()
|
||||
os.sleep(1)
|
||||
end
|
||||
end, function()
|
||||
while true do
|
||||
local new_state = {}
|
||||
local new_sizes = {}
|
||||
map(config.storage, function(i, inv)
|
||||
new_sizes[inv] = peripheral.call(inv, "size")
|
||||
for slot, item in pairs(peripheral.call(inv, "list")) do
|
||||
new_state[string.format("%s#%d", inv, slot)] = item
|
||||
end
|
||||
end)
|
||||
item_state.storage = new_state
|
||||
storage_sizes.storage = new_sizes
|
||||
os.sleep(0)
|
||||
end
|
||||
end, function()
|
||||
while true do
|
||||
local new_state = {}
|
||||
local new_sizes = {}
|
||||
map(
|
||||
groupby(config.stock, function(location)
|
||||
local storage = string.split(location, "#")
|
||||
return storage
|
||||
end
|
||||
), function(inv)
|
||||
new_sizes[inv] = peripheral.call(inv, "size")
|
||||
for slot, item in pairs(peripheral.call(inv, "list")) do
|
||||
new_state[string.format("%s#%d", inv, slot)] = item
|
||||
end
|
||||
end)
|
||||
item_state.stock = new_state
|
||||
storage_sizes.stock = new_sizes
|
||||
os.sleep(0)
|
||||
end
|
||||
end, function() -- IMPORT
|
||||
while true do
|
||||
map(
|
||||
groupby(config.stock, function(location) return ({ string.split(location, "#") })[1] end),
|
||||
function(stock_inv)
|
||||
local size = storage_sizes.stock[stock_inv] or 0
|
||||
for i = 1, size do
|
||||
local slot = string.format("%s#%d", stock_inv, i)
|
||||
local slot_stocked, slot_expected = item_state.stock[slot], config.stock[slot]
|
||||
if (slot_stocked and slot_expected and slot_stocked.name ~= slot_expected.name) or (slot_stocked ~= nil and slot_expected == nil) then
|
||||
for _, output in ipairs(config.storage) do
|
||||
if peripheral.call(stock_inv, "pushItems", output, i) ~= 0 then
|
||||
print("MOVE", slot, slot_stocked.name)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
os.sleep(0)
|
||||
end
|
||||
end, function() -- STOCK
|
||||
while true do
|
||||
local new_junk = {}
|
||||
local item_locations = groupby(item_state.storage, function(location, item)
|
||||
new_junk[item.name] = (new_junk[item.name] or 0) + item.count
|
||||
return item.name
|
||||
end)
|
||||
|
||||
local tw, th = term.getSize()
|
||||
map(config.stock, function(destination, item)
|
||||
local dst_container, dst_slot = string.split(destination, "#")
|
||||
new_junk[item.name] = nil
|
||||
if item_locations[item.name] == nil then return end
|
||||
local dst_item = item_state.stock[destination]
|
||||
local dst_count = dst_item and dst_item.count or 0
|
||||
if dst_count >= item.count then return end
|
||||
local missing = item.count - dst_count
|
||||
|
||||
local _, take_from = pop(filter(item_locations[item.name], function(_, src_item)
|
||||
local src_container, src_slot = string.split(src_item.k, "#")
|
||||
return src_container ~= dst_container
|
||||
end))
|
||||
|
||||
if not take_from then return end
|
||||
local src_container, src_slot = string.split(take_from.k, "#")
|
||||
local t = os.clock()
|
||||
local out = peripheral.call(dst_container, "pullItems", src_container, tonumber(src_slot), missing, tonumber(dst_slot))
|
||||
print(string.format("PUT %s*%d/%d to %s", item.name, out, missing, destination))
|
||||
-- print(string.format("%s -> %s (%s*%d) took %.2f", take_from.k, destination, item.name, item.count, os.clock() - t))
|
||||
end)
|
||||
item_state.junk = new_junk
|
||||
os.sleep(0)
|
||||
end
|
||||
end, function() -- JUNK
|
||||
while true do
|
||||
local y = 2
|
||||
for name, count in pairs(item_state.junk) do
|
||||
map(filter(item_state.storage, function(location, item)
|
||||
return item.name == name
|
||||
end), function(location, item)
|
||||
local junk_container, junk_slot = string.split(location, "#")
|
||||
for _, junk_output in ipairs(config.junk) do
|
||||
if peripheral.call(junk_container, "pushItems", junk_output, tonumber(junk_slot)) ~= 0 then
|
||||
print("JUNK OUT", item.name, "->", junk_container)
|
||||
break
|
||||
end
|
||||
end
|
||||
end)
|
||||
y = y + 1
|
||||
end
|
||||
os.sleep(0)
|
||||
end
|
||||
end, function() -- USER INPUT
|
||||
while true do
|
||||
local ev = { os.pullEvent() }
|
||||
if ev[1] == "char" and search_open then
|
||||
search_query = search_query .. ev[2]
|
||||
mon.setBackgroundColor(colors.gray)
|
||||
mon.setTextColor(colors.magenta)
|
||||
mon.setCursorPos(tw - 14, th - 20)
|
||||
mon.write("> " .. search_query)
|
||||
for i, line in ipairs(keyboard) do
|
||||
mon.setCursorPos(tw - 14, th - 20 + i)
|
||||
mon.setBackgroundColor(colors.brown)
|
||||
mon.setTextColor(colors.cyan)
|
||||
mon.write(line)
|
||||
end
|
||||
elseif ev[1] == "key" then
|
||||
if ev[2] == keys.enter then
|
||||
search_open = not search_open
|
||||
elseif ev[2] == keys.backspace and search_open then
|
||||
search_query = search_query:sub(1, #search_query - 1)
|
||||
mon.setBackgroundColor(colors.gray)
|
||||
mon.setTextColor(colors.magenta)
|
||||
mon.setCursorPos(tw - 14, th - 20)
|
||||
mon.write("> " .. search_query)
|
||||
for i, line in ipairs(keyboard) do
|
||||
mon.setCursorPos(tw - 14, th - 20 + i)
|
||||
mon.setBackgroundColor(colors.brown)
|
||||
mon.setTextColor(colors.cyan)
|
||||
mon.write(line)
|
||||
end
|
||||
end
|
||||
elseif ev[1] == "monitor_touch" then
|
||||
local tw, th = mon.getSize()
|
||||
local _, _, x, y = table.unpack(ev)
|
||||
if y == 3 or y == th then
|
||||
local new_mode
|
||||
if x >= 1 and x <= 8 then new_mode = "count"
|
||||
elseif x >= 10 and x <= (tw - 1) then new_mode = "name"
|
||||
elseif x == tw then
|
||||
search_open = not search_open
|
||||
end
|
||||
|
||||
if new_mode ~= nil and new_mode == sort_mode then
|
||||
sort_inverse = not sort_inverse
|
||||
elseif new_mode ~= nil then
|
||||
sort_mode = new_mode
|
||||
sort_inverse = false
|
||||
end
|
||||
elseif search_open and x >= (tw - 14) and x < (tw - 4) and y > (th - 20) and y <= (th - 17) then
|
||||
mon.setBackgroundColor(colors.gray)
|
||||
mon.setTextColor(colors.magenta)
|
||||
mon.setCursorPos(tw - 14, th - 20)
|
||||
mon.write("> " .. search_query)
|
||||
for i, line in ipairs(keyboard) do
|
||||
mon.setCursorPos(tw - 14, th - 20 + i)
|
||||
mon.setBackgroundColor(colors.brown)
|
||||
mon.setTextColor(colors.cyan)
|
||||
mon.write(line)
|
||||
end
|
||||
|
||||
local char = keyboard[20 - th + y]:sub(15 - tw + x, 15 - tw + x)
|
||||
if char >= "a" and char <= "z" then
|
||||
os.queueEvent("key", keys[char], false)
|
||||
os.queueEvent("char", char)
|
||||
os.queueEvent("key_up", keys[char])
|
||||
else
|
||||
-- 27 backspace (ev=259)
|
||||
-- 215 enter (ev=257)
|
||||
local code = nil
|
||||
|
||||
if char == "\x1b" then code = keys.backspace
|
||||
elseif char == "\xd7" then code = keys.enter
|
||||
end
|
||||
|
||||
if code then
|
||||
os.queueEvent("key", code, false)
|
||||
os.queueEvent("key_up", code)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end, function() -- STATUS
|
||||
|
||||
while true do
|
||||
local back_term = term.redirect(mon)
|
||||
local tw, th = term.getSize()
|
||||
term.setBackgroundColor(colors.black)
|
||||
term.setTextColor(colors.white)
|
||||
term.clear()
|
||||
|
||||
local usage = pipe(config.storage)
|
||||
:map(function(_, storage)
|
||||
local used, total = 0, storage_sizes.storage[storage] or 0
|
||||
for slot = 1, total do
|
||||
if item_state.storage[string.format("%s#%d", storage, slot)] then
|
||||
used = used + 1
|
||||
end
|
||||
end
|
||||
return { used = used, total = total }
|
||||
end)
|
||||
:reduce(function(_, info, acc)
|
||||
return { used = info.used + acc.used, total = info.total + acc.total }
|
||||
end, { used = 0, total = 0 }):get()
|
||||
|
||||
draw_bar(1, colors.lightGray, colors.gray, usage.used / usage.total, "Storage utilization: %7.3f%%", 100 * usage.used / usage.total)
|
||||
|
||||
term.setCursorPos(1, 3)
|
||||
term.setBackgroundColor(colors.gray)
|
||||
term.clearLine()
|
||||
local infoline = string.format("Count %s Name %s",
|
||||
(sort_mode == "count" and (sort_inverse and "\x1e" or "\x1f") or " "),
|
||||
(sort_mode == "name" and (sort_inverse and "\x1e" or "\x1f") or " "))
|
||||
local fg = sort_mode == "count" and "55555551f88888881" or "88888881f55555551"
|
||||
term.blit(infoline, fg, "77777777777777777")
|
||||
term.setCursorPos(tw, 3)
|
||||
term.write("\x0c")
|
||||
term.setCursorPos(1, th)
|
||||
term.clearLine()
|
||||
term.blit(infoline, fg, "77777777777777777")
|
||||
term.setCursorPos(tw, th)
|
||||
term.write("\x0c")
|
||||
|
||||
|
||||
pipe(item_state.storage)
|
||||
:groupby(function(slot, item) return item.name end)
|
||||
:filter(function(name, slots)
|
||||
if not search_open then return true end
|
||||
return string.match(name, search_query)
|
||||
end)
|
||||
:map(function(name, slots) return reduce(slots, function(_, slot, acc) return slot.v.count + acc end, 0) end)
|
||||
:sort(function(t, a, b)
|
||||
local swap = false
|
||||
if sort_mode == "count" then swap = t[a] > t[b] end
|
||||
if sort_mode == "name" then swap = a > b end
|
||||
return swap ~= sort_inverse
|
||||
end)
|
||||
:map(function(i, kv)
|
||||
if i >= (th - 4) then return end
|
||||
term.setCursorPos(1, 3 + i)
|
||||
term.setBackgroundColor((i % 2) == 0 and colors.blue or colors.purple)
|
||||
term.clearLine()
|
||||
term.write(string.format("%8d %s", kv.v, kv.k))
|
||||
end)
|
||||
|
||||
if search_open then
|
||||
mon.setBackgroundColor(colors.gray)
|
||||
mon.setTextColor(colors.magenta)
|
||||
term.setCursorPos(tw - 14, th - 20)
|
||||
term.write("> " .. search_query)
|
||||
for i, line in ipairs(keyboard) do
|
||||
term.setCursorPos(tw - 14, th - 20 + i)
|
||||
term.setBackgroundColor(colors.brown)
|
||||
term.setTextColor(colors.cyan)
|
||||
term.write(line)
|
||||
end
|
||||
end
|
||||
|
||||
term.redirect(back_term)
|
||||
os.sleep(1)
|
||||
end
|
||||
end)
|
|
@ -0,0 +1,11 @@
|
|||
# x-run: sanic stream_video:app
|
||||
from sanic import Request, Sanic, Websocket
|
||||
|
||||
app = Sanic("CCVideoStreamer")
|
||||
|
||||
@app.websocket("/stream")
|
||||
async def ws_stream(req: Request, ws: Websocket):
|
||||
with open("./video.cani", "r") as fp:
|
||||
for line in fp:
|
||||
await ws.send(bytes.fromhex(line.strip()))
|
||||
await ws.close()
|
|
@ -0,0 +1,83 @@
|
|||
|
||||
local url = "wss://kc.is.being.pet/ws"
|
||||
|
||||
local screen = peripheral.wrap("monitor_1")
|
||||
local speakers = {
|
||||
l = peripheral.wrap("speaker_1"),
|
||||
r = peripheral.wrap("speaker_0"),
|
||||
}
|
||||
|
||||
local ws = assert(http.websocket(url))
|
||||
|
||||
local function parse_u16(data)
|
||||
local v = table.remove(data, 1)
|
||||
v = bit.bor(v, bit.blshift(table.remove(data, 1), 8))
|
||||
return v, data
|
||||
end
|
||||
|
||||
local metadata_pkt = ws.receive()
|
||||
metadata_pkt = { string.byte(metadata_pkt, 1, #metadata_pkt) }
|
||||
local framerate = table.remove(metadata_pkt, 1)
|
||||
local n_channels = table.remove(metadata_pkt, 1)
|
||||
local sample_rate, data = parse_u16(metadata_pkt)
|
||||
local screen_w, data = parse_u16(data)
|
||||
local screen_h, data = parse_u16(data)
|
||||
local samples_per_frame = math.floor(sample_rate / framerate)
|
||||
|
||||
print(string.format("FPS: %d", framerate))
|
||||
print(string.format("Audio: %d channels, %d Hz", n_channels, sample_rate))
|
||||
print(string.format("Video: %dx%d", screen_w, screen_h))
|
||||
print(string.format("S/F: %d", samples_per_frame))
|
||||
|
||||
local function decode_s8(buf)
|
||||
local buffer = {}
|
||||
for i = 1, #buf do
|
||||
local v = buf[i]
|
||||
if bit32.band(v, 0x80) then
|
||||
v = bit32.bxor(v, 0x7F) - 128
|
||||
end
|
||||
table.insert(buffer, v)
|
||||
table.insert(buffer, v)
|
||||
end
|
||||
return buffer
|
||||
end
|
||||
|
||||
local function mkSpeakerCoro(p, b)
|
||||
return function()
|
||||
while not p.playAudio(b) do
|
||||
os.pullEvent("speaker_audio_empty")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
while true do
|
||||
local video_pkt = ws.receive()
|
||||
local channels = { l = {}, r = {} }
|
||||
local offset = 1
|
||||
channels.l = decode_s8({ string.byte(video_pkt, offset, offset + samples_per_frame - 1) })
|
||||
offset = offset + samples_per_frame
|
||||
channels.r = decode_s8({ string.byte(video_pkt, offset, offset + samples_per_frame - 1) })
|
||||
offset = offset + samples_per_frame
|
||||
for y = 1, screen_h do
|
||||
local tx, bg, fg = {}, {}, {}
|
||||
for x = 1, screen_w do
|
||||
table.insert(tx, string.sub(video_pkt, offset, offset))
|
||||
local color = string.byte(video_pkt, offset + 1, offset + 1)
|
||||
table.insert(bg, string.format("%x", bit.brshift(color, 4)))
|
||||
table.insert(fg, string.format("%x", bit.band(color, 0xF)))
|
||||
offset = offset + 2
|
||||
end
|
||||
screen.setCursorPos(1, y)
|
||||
screen.blit(table.concat(tx), table.concat(bg), table.concat(fg))
|
||||
end
|
||||
|
||||
for i = 1, 16 do
|
||||
local r, g, b = string.byte(video_pkt, offset, offset + 3)
|
||||
screen.setPaletteColor(bit.blshift(1, i - 1), bit.bor(bit.bor(bit.blshift(r, 16), bit.blshift(g, 8)), b))
|
||||
offset = offset + 3
|
||||
end
|
||||
|
||||
parallel.waitForAll(
|
||||
mkSpeakerCoro(speakers.l, channels.l),
|
||||
mkSpeakerCoro(speakers.r, channels.r))
|
||||
end
|
13
obcb-cc.lua
|
@ -26,9 +26,12 @@ local function send_chunk_subscribe_request(chunk)
|
|||
ws.send(string.char(0x14, bit.band(chunk, 0xFF), bit.band(bit.brshift(chunk, 8), 0xFF)), true)
|
||||
end
|
||||
|
||||
local shutdown = false
|
||||
|
||||
parallel.waitForAll(function()
|
||||
while true do
|
||||
while not shutdown do
|
||||
local data, is_binary = ws.receive()
|
||||
if data == nil then return end
|
||||
data = { string.byte(data, 1, #data) }
|
||||
local opcode = table.remove(data, 1)
|
||||
if opcode == 0x00 then -- hello
|
||||
|
@ -63,20 +66,25 @@ function()
|
|||
mon.setTextScale(0.5)
|
||||
mon.clear()
|
||||
local tw, th = term.getSize()
|
||||
term.clear()
|
||||
send_chunk_request(chunk_id)
|
||||
send_chunk_subscribe_request(chunk_id)
|
||||
term.setCursorPos(1, 3)
|
||||
print("Showing chunk " .. chunk_id)
|
||||
print(string.format("Screen: %dx%d", mon.getSize()))
|
||||
while true do
|
||||
while not shutdown do
|
||||
local ev = { os.pullEvent() }
|
||||
if ev[1] == "char" and ev[2] == "q" then
|
||||
shutdown = true
|
||||
ws.close()
|
||||
break
|
||||
elseif ev[1] == "obcb:hello" then
|
||||
term.setCursorPos(1, 1)
|
||||
term.clearLine()
|
||||
print(string.format("Hello: obcb v%d.%d", ev[2], ev[3]))
|
||||
elseif ev[1] == "obcb:clients" then
|
||||
term.setCursorPos(1, 2)
|
||||
term.clearLine()
|
||||
print("Clients: " .. ev[2])
|
||||
elseif ev[1] == "obcb:update" then
|
||||
for y = 1, 81 do
|
||||
|
@ -96,4 +104,5 @@ function()
|
|||
end
|
||||
end)
|
||||
|
||||
shutdown = true
|
||||
ws.close()
|
||||
|
|
50
wsvpn.c
|
@ -65,10 +65,10 @@ struct client {
|
|||
|
||||
struct client clients[MAX_CLIENTS] = { 0 };
|
||||
|
||||
static void handle_client(struct mg_connection *connection, int event_type, void *ev_data, void *fn_data);
|
||||
static void on_ws_connect(struct mg_connection *connection, struct mg_http_message *message, void *data);
|
||||
static void on_ws_message(struct mg_connection *connection, struct mg_ws_message *message, void *data);
|
||||
static void on_ws_disconnect(struct mg_connection *connection, void *data);
|
||||
static void handle_client(struct mg_connection *connection, int event_type, void *ev_data);
|
||||
static void on_ws_connect(struct mg_connection *connection, struct mg_http_message *message);
|
||||
static void on_ws_message(struct mg_connection *connection, struct mg_ws_message *message);
|
||||
static void on_ws_disconnect(struct mg_connection *connection);
|
||||
|
||||
bool client_is_open(struct client *client, uint16_t channel);
|
||||
static void modem_open(struct client *client, uint16_t request_id, uint16_t channel);
|
||||
|
@ -101,15 +101,15 @@ int main(void) {
|
|||
mg_mgr_free(&manager);
|
||||
}
|
||||
|
||||
static void handle_client(struct mg_connection *connection, int event_type, void *event_data, void *fn_data) {
|
||||
static void handle_client(struct mg_connection *connection, int event_type, void *event_data) {
|
||||
if (event_type == MG_EV_OPEN) {
|
||||
if (connection->rem.port == 0) return;
|
||||
memset(connection->data, 0, 32);
|
||||
} else if (event_type == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *http_message = (struct mg_http_message *) event_data;
|
||||
if (mg_http_match_uri(http_message, "/open")) {
|
||||
if (mg_match(http_message->uri, mg_str_s("/open"), 0)) {
|
||||
mg_ws_upgrade(connection, http_message, NULL);
|
||||
} else if (mg_http_match_uri(http_message, "/metrics")) {
|
||||
} else if (mg_match(http_message->uri, mg_str_s("/metrics"), 0)) {
|
||||
mg_printf(connection, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
|
||||
mg_http_printf_chunk(connection, "# HELP ws_bytes_sent_total Number of bytes sent to clients\n");
|
||||
mg_http_printf_chunk(connection, "# TYPE ws_bytes_sent_total counter\n");
|
||||
|
@ -147,13 +147,13 @@ static void handle_client(struct mg_connection *connection, int event_type, void
|
|||
}
|
||||
} else if (event_type == MG_EV_WS_OPEN) {
|
||||
struct mg_http_message *http_message = (struct mg_http_message *) event_data;
|
||||
on_ws_connect(connection, http_message, fn_data);
|
||||
on_ws_connect(connection, http_message);
|
||||
} else if (event_type == MG_EV_WS_MSG) {
|
||||
struct mg_ws_message *ws_message = (struct mg_ws_message *)event_data;
|
||||
on_ws_message(connection, ws_message, fn_data);
|
||||
on_ws_message(connection, ws_message);
|
||||
} else if (event_type == MG_EV_CLOSE) {
|
||||
if (connection->is_websocket) {
|
||||
on_ws_disconnect(connection, fn_data);
|
||||
on_ws_disconnect(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,9 +211,8 @@ void ws_respond(struct client *client, uint16_t request_id, void *data, uint32_t
|
|||
mg_ws_send(client->connection, buffer, size + 3, WEBSOCKET_OP_BINARY);
|
||||
}
|
||||
|
||||
static void on_ws_connect(struct mg_connection *connection, struct mg_http_message *message, void *data) {
|
||||
static void on_ws_connect(struct mg_connection *connection, struct mg_http_message *message) {
|
||||
(void)message;
|
||||
(void)data;
|
||||
struct client *client = malloc(sizeof(struct client));
|
||||
memcpy(&connection->data[0], &client, sizeof(struct client *));
|
||||
client->connection = connection;
|
||||
|
@ -226,9 +225,7 @@ static void on_ws_connect(struct mg_connection *connection, struct mg_http_messa
|
|||
mg_ws_send(connection, buffer, 2 + buffer[1], WEBSOCKET_OP_BINARY);
|
||||
}
|
||||
|
||||
static void on_ws_message(struct mg_connection *connection, struct mg_ws_message *message, void *data) {
|
||||
(void)data;
|
||||
|
||||
static void on_ws_message(struct mg_connection *connection, struct mg_ws_message *message) {
|
||||
if ((message->flags & 15) != WEBSOCKET_OP_BINARY) {
|
||||
const char *err_str = "This server only works in binary mode. Sorry!";
|
||||
mg_ws_send(connection, err_str, strlen(err_str), WEBSOCKET_OP_TEXT);
|
||||
|
@ -244,15 +241,15 @@ static void on_ws_message(struct mg_connection *connection, struct mg_ws_message
|
|||
|
||||
if (message->data.len == 0) return;
|
||||
|
||||
uint16_t request_id = ntohs(*(uint16_t*)&message->data.ptr[1]);
|
||||
uint16_t request_id = ntohs(*(uint16_t*)&message->data.buf[1]);
|
||||
|
||||
switch (message->data.ptr[0]) {
|
||||
switch (message->data.buf[0]) {
|
||||
case 'I': // info. We can safely ignore that message
|
||||
break;
|
||||
case 'O': // open
|
||||
{
|
||||
metrics.method_calls[0]++;
|
||||
uint16_t channel = ntohs(*(uint16_t*)&message->data.ptr[3]);
|
||||
uint16_t channel = ntohs(*(uint16_t*)&message->data.buf[3]);
|
||||
printf("%p[%04x] modem.open(%d)\n", (void*)client, request_id, channel);
|
||||
modem_open(client, request_id, channel);
|
||||
}
|
||||
|
@ -260,7 +257,7 @@ static void on_ws_message(struct mg_connection *connection, struct mg_ws_message
|
|||
case 'o': // isOpen
|
||||
{
|
||||
metrics.method_calls[1]++;
|
||||
uint16_t channel = ntohs(*(uint16_t*)&message->data.ptr[3]);
|
||||
uint16_t channel = ntohs(*(uint16_t*)&message->data.buf[3]);
|
||||
printf("%p[%04x] modem.isOpen(%d)\n", (void*)client, request_id, channel);
|
||||
modem_isOpen(client, request_id, channel);
|
||||
}
|
||||
|
@ -268,7 +265,7 @@ static void on_ws_message(struct mg_connection *connection, struct mg_ws_message
|
|||
case 'c': // close
|
||||
{
|
||||
metrics.method_calls[2]++;
|
||||
uint16_t channel = ntohs(*(uint16_t*)&message->data.ptr[3]);
|
||||
uint16_t channel = ntohs(*(uint16_t*)&message->data.buf[3]);
|
||||
printf("%p[%04x] modem.close(%d)\n", (void*)client, request_id, channel);
|
||||
modem_close(client, request_id, channel);
|
||||
}
|
||||
|
@ -283,21 +280,20 @@ static void on_ws_message(struct mg_connection *connection, struct mg_ws_message
|
|||
case 'T': // transmit
|
||||
{
|
||||
metrics.method_calls[4]++;
|
||||
uint16_t channel = ntohs(*(uint16_t*)&message->data.ptr[3]);
|
||||
uint16_t reply_channel = ntohs(*(uint16_t*)&message->data.ptr[5]);
|
||||
uint16_t data_length = ntohs(*(uint16_t*)&message->data.ptr[7]);
|
||||
modem_transmit(client, request_id, channel, reply_channel, (void*)&message->data.ptr[9], data_length);
|
||||
uint16_t channel = ntohs(*(uint16_t*)&message->data.buf[3]);
|
||||
uint16_t reply_channel = ntohs(*(uint16_t*)&message->data.buf[5]);
|
||||
uint16_t data_length = ntohs(*(uint16_t*)&message->data.buf[7]);
|
||||
modem_transmit(client, request_id, channel, reply_channel, (void*)&message->data.buf[9], data_length);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
ws_send_error(client, request_id, "Unknown opcode: 0x%02x", message->data.ptr[0]);
|
||||
ws_send_error(client, request_id, "Unknown opcode: 0x%02x", message->data.buf[0]);
|
||||
connection->is_draining = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void on_ws_disconnect(struct mg_connection *connection, void *data) {
|
||||
(void)data;
|
||||
static void on_ws_disconnect(struct mg_connection *connection) {
|
||||
|
||||
struct client *client = *(struct client **)&connection->data[0];
|
||||
if (client->connection == connection) {
|
||||
|
|