forked from hkc/cc-stuff
1
0
Fork 0

Compare commits

...

10 Commits

Author SHA1 Message Date
Vftdan a65943a6bd Added Makefile for img2cpi and wsvpn
FIXME: downgrade mongoose submodule or fix wsvpn
2024-10-02 18:47:09 +02:00
Vftdan 2f80e5327d Added mongoose.ws dependency submodule 2024-10-02 18:45:37 +02:00
Vftdan bbbe20f941 Add stb submodule as dependency 2024-10-02 18:23:18 +02:00
Casey 1eccdde3a3
Moved stuff into separate functions, minor cleanup 2024-10-02 15:31:24 +03:00
Casey a0450a7d59
Removed debug stuff 2024-10-02 07:32:28 +03:00
Casey d511cc407e
Fixed it! Now it works fine 2024-10-02 07:22:53 +03:00
Casey 1e364fe3ea
WIP old converter port, gives funky results 2024-10-02 06:51:51 +03:00
Casey 33e3c1ad8c
Minor changes, unfinished palette selection 2024-09-30 14:19:28 +03:00
Casey a665f9498d
One can only hope 2024-09-30 13:58:22 +03:00
Casey 094308ce0b
There's no fucking way it just worked lol 2024-09-30 13:41:31 +03:00
5 changed files with 180 additions and 140 deletions

6
.gitmodules vendored Normal file
View File

@ -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

9
Makefile Normal file
View File

@ -0,0 +1,9 @@
CPPFLAGS += -Idependencies -Idependencies/mongoose
LDLIBS += -lm
all: img2cpi wsvpn
wsvpn: wsvpn.o dependencies/mongoose/mongoose.o
$(CC) $(LDFLAGS) "$<" $(LOADLIBES) $(LDLIBS) -o "$@"
.PHONY: all

1
dependencies/mongoose vendored Submodule

@ -0,0 +1 @@
Subproject commit 3525f044f551816dc1469f445fc16b94d51a1e78

1
dependencies/stb vendored Submodule

@ -0,0 +1 @@
Subproject commit f75e8d1cad7d90d72ef7a4661f1b994ef78b4e31

303
img2cpi.c
View File

@ -76,6 +76,8 @@ struct image {
struct image_pal { struct image_pal {
int w, h; int w, h;
uint8_t *pixels; uint8_t *pixels;
const union color *palette;
size_t palette_size;
}; };
bool parse_cmdline(int argc, char **argv); bool parse_cmdline(int argc, char **argv);
@ -85,9 +87,13 @@ struct image *image_new(int w, int h);
struct image *image_resize(struct image *original, int new_w, int new_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 union color *colors, size_t n_colors);
float get_color_difference(union color a, union color b); float get_color_difference(union color a, union color b);
float get_color_brightness(union color clr);
void image_unload(struct image *img); void image_unload(struct image *img);
void get_size_keep_aspect(int w, int h, int dw, int dh, int *ow, int *oh); 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_8x11(const struct image_pal *img, struct cc_char *characters);
const char *known_file_extensions[] = { const char *known_file_extensions[] = {
".png", ".jpg", ".jpeg", ".jfif", ".jpg", ".gif", ".png", ".jpg", ".jpeg", ".jfif", ".jpg", ".gif",
".tga", ".bmp", ".hdr", ".pnm", 0 ".tga", ".bmp", ".hdr", ".pnm", 0
@ -138,52 +144,6 @@ int main(int argc, char **argv) {
fprintf(stderr, "Fatal error occurred, exiting.\n"); fprintf(stderr, "Fatal error occurred, exiting.\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
printf("fast_mode = %s\n", args.fast_mode ? "true" : "false");
printf("size = %dx%d\n", args.width, args.height);
printf("version = %s\n",
args.cpi_version == CPI_VERSION_AUTO ? "AUTO" : (
args.cpi_version == CPI_VERSION_RAW ? "RAW" : (
args.cpi_version == CPI_VERSION_0 ? "0" : (
args.cpi_version == CPI_VERSION_1 ? "1" : (
args.cpi_version == CPI_VERSION_2 ? "2" : (
"UNKNOWN"
)
)
)
)
));
printf("placement = %s\n",
args.placement == PLACEMENT_CENTER ? "CENTER" : (
args.placement == PLACEMENT_COVER ? "COVER" : (
args.placement == PLACEMENT_TILE ? "TILE" : (
args.placement == PLACEMENT_FULL ? "FULL" : (
args.placement == PLACEMENT_EXTEND ? "EXTEND" : (
args.placement == PLACEMENT_FILL ? "FILL" : (
"UNKNOWN"
)
)
)
)
)
));
printf("palette = %s => %s\n",
args.palette_type == PALETTE_DEFAULT ? "DEFAULT" : (
args.palette_type == PALETTE_DEFAULT_GRAY ? "DEFAULTGRAY" : (
args.palette_type == PALETTE_AUTO ? "AUTO" : (
args.palette_type == PALETTE_PATH ? "PATH" : (
args.palette_type == PALETTE_LIST ? "LIST" : (
"UNKNOWN"
)
)
)
)
),
args.palette
);
printf("input: %s\n", args.input_path);
printf("output: %s\n", args.output_path);
struct image *src_image = image_load(args.input_path); struct image *src_image = image_load(args.input_path);
if (!src_image) { if (!src_image) {
fprintf(stderr, "Error: failed to open the file\n"); fprintf(stderr, "Error: failed to open the file\n");
@ -202,8 +162,16 @@ int main(int argc, char **argv) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// TODO: load palette // TODO: load palette, maybe calculate it too? k-means?
const union color *palette = 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;
default: assert(0 && "Unreachable");
}
// TODO: properly scale // TODO: properly scale
struct image *scaled_image; struct image *scaled_image;
@ -211,8 +179,11 @@ int main(int argc, char **argv) {
int new_w, new_h; int new_w, new_h;
get_size_keep_aspect(src_image->w, src_image->h, canvas->w, canvas->h, &new_w, &new_h); get_size_keep_aspect(src_image->w, src_image->h, canvas->w, canvas->h, &new_w, &new_h);
printf("new size: %dx%d\n", new_w, new_h);
scaled_image = image_resize(src_image, 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");
return EXIT_FAILURE;
}
} }
// TODO: position image properly // TODO: position image properly
@ -224,66 +195,19 @@ int main(int argc, char **argv) {
small_w * sizeof(union color)); small_w * sizeof(union color));
} }
// TODO: actually do stuff // TODO: actually do stuff
const union color *palette = DEFAULT_PALETTE;
struct cc_char *characters = calloc(args.width * args.height, sizeof(struct cc_char)); 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, 16);
if (!quantized_image) {
FILE *tmp = fopen("/tmp/img.raw", "wb"); fprintf(stderr, "Error: failed to open the file\n");
for (int i = 0; i < quantized_image->w * quantized_image->h; i++) { return EXIT_FAILURE;
union color pix = palette[quantized_image->pixels[i]];
fputc(pix.rgba.r, tmp);
fputc(pix.rgba.g, tmp);
fputc(pix.rgba.b, tmp);
} }
fclose(tmp);
if (args.fast_mode) { if (args.fast_mode) {
// use old 2x3 convert_2x3(quantized_image, characters);
} else { } else {
// use new 8x11 character matching convert_8x11(quantized_image, characters);
for (int y = 0; y < args.height; y++) {
for (int x = 0; x < args.width; x++) {
printf("DBG: %d:%d\n", x, y);
// Oh boy...
int 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];
int 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[
x * 8 + ox + (y * 11 + oy) * args.width
]];
difference += get_color_difference(pixel, lit ? cell_fg : cell_bg);
}
}
if (difference <= min_diff) {
min_diff = difference;
closest_sym = sym;
closest_color = color;
}
}
}
if (closest_sym != 0) {
printf("HIT! %d\n", closest_sym);
}
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;
}
}
} }
// TODO: implement something other than CPIv0 // TODO: implement something other than CPIv0
@ -328,23 +252,23 @@ bool parse_cmdline(int argc, char **argv) {
if (c == '?') break; if (c == '?') break;
switch (c) { switch (c) {
case 'h': case 'h': // --help
show_help(argv[0], true, stdout); show_help(argv[0], true, stdout);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
break; break;
case 'f': case 'f': // --fast
args.fast_mode = true; args.fast_mode = true;
if (args.cpi_version != CPI_VERSION_AUTO) { if (args.cpi_version != CPI_VERSION_AUTO) {
fprintf(stderr, "Warning: text mode ignores version\n"); fprintf(stderr, "Warning: text mode ignores version\n");
} }
break; break;
case 'W': case 'W': // --width
args.width = atoi(optarg); args.width = atoi(optarg);
break; break;
case 'H': case 'H': // --height
args.height = atoi(optarg); args.height = atoi(optarg);
break; break;
case 'V': case 'V': // --cpi_version
{ {
if (0 == strcmp(optarg, "auto") || 0 == strcmp(optarg, "-1")) { if (0 == strcmp(optarg, "auto") || 0 == strcmp(optarg, "-1")) {
args.cpi_version = CPI_VERSION_AUTO; args.cpi_version = CPI_VERSION_AUTO;
@ -359,7 +283,7 @@ bool parse_cmdline(int argc, char **argv) {
} }
} }
break; break;
case 'p': case 'p': // --placement
if (0 == strcmp(optarg, "center")) { if (0 == strcmp(optarg, "center")) {
args.placement = PLACEMENT_CENTER; args.placement = PLACEMENT_CENTER;
} else if (0 == strcmp(optarg, "cover")) { } else if (0 == strcmp(optarg, "cover")) {
@ -377,7 +301,7 @@ bool parse_cmdline(int argc, char **argv) {
return false; return false;
} }
break; break;
case 'P': case 'P': // --palette
if (0 == strcmp(optarg, "default")) { if (0 == strcmp(optarg, "default")) {
args.palette_type = PALETTE_DEFAULT; args.palette_type = PALETTE_DEFAULT;
} else if (0 == strcmp(optarg, "defaultgray")) { } else if (0 == strcmp(optarg, "defaultgray")) {
@ -567,6 +491,8 @@ struct image_pal *image_quantize(struct image *original, const union color *colo
out->w = original->w; out->w = original->w;
out->h = original->h; out->h = original->h;
out->pixels = calloc(original->w, original->h); out->pixels = calloc(original->w, original->h);
out->palette = colors;
out->palette_size = n_colors;
for (int i = 0; i < out->w * out->h; i++) { for (int i = 0; i < out->w * out->h; i++) {
int closest_color = 0; int closest_color = 0;
@ -591,40 +517,137 @@ float get_color_difference(union color a, union color b) {
return dr * dr + dg * dg + db * db; return dr * dr + dg * dg + db * db;
} }
float get_color_brightness(union color clr) {
return get_color_difference(clr, (union color){ .v = 0 });
}
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++) {
for (int x = 0; x < w; 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 = img->pixels[ox + (x + (y * 3 + oy) * w) * 2];
float brightness = get_color_brightness(img->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 = img->pixels[ox + (x + (y * 3 + oy) * w) * 2];
float diff_bg = get_color_difference(img->palette[darkest_i], img->palette[pix]);
float diff_fg = get_color_difference(img->palette[brightest_i], img->palette[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[darkest_i], img->palette[pix]);
float diff_fg = get_color_difference(img->palette[brightest_i], img->palette[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;
}
}
}
void convert_8x11(const struct image_pal *img, struct cc_char *characters) {
int w = img->w / 8, h = img->h / 11;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
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 = img->palette[color & 0xF],
cell_fg = img->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 = img->palette[img->pixels[
ox + (x + (y * 11 + oy) * w) * 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 * w].character = closest_sym;
characters[x + y * w].bg = closest_color & 0xF;
characters[x + y * w].fg = closest_color >> 4;
}
}
}
const union color DEFAULT_PALETTE[16] = { const union color DEFAULT_PALETTE[16] = {
{ 0xf0, 0xf0, 0xf0, 0xff }, { { 0xf0, 0xf0, 0xf0, 0xff } },
{ 0xf2, 0xb2, 0x33, 0xff }, { { 0xf2, 0xb2, 0x33, 0xff } },
{ 0xe5, 0x7f, 0xd8, 0xff }, { { 0xe5, 0x7f, 0xd8, 0xff } },
{ 0x99, 0xb2, 0xf2, 0xff }, { { 0x99, 0xb2, 0xf2, 0xff } },
{ 0xde, 0xde, 0x6c, 0xff }, { { 0xde, 0xde, 0x6c, 0xff } },
{ 0x7f, 0xcc, 0x19, 0xff }, { { 0x7f, 0xcc, 0x19, 0xff } },
{ 0xf2, 0xb2, 0xcc, 0xff }, { { 0xf2, 0xb2, 0xcc, 0xff } },
{ 0x4c, 0x4c, 0x4c, 0xff }, { { 0x4c, 0x4c, 0x4c, 0xff } },
{ 0x99, 0x99, 0x99, 0xff }, { { 0x99, 0x99, 0x99, 0xff } },
{ 0x4c, 0x99, 0xb2, 0xff }, { { 0x4c, 0x99, 0xb2, 0xff } },
{ 0xb2, 0x66, 0xe5, 0xff }, { { 0xb2, 0x66, 0xe5, 0xff } },
{ 0x33, 0x66, 0xcc, 0xff }, { { 0x33, 0x66, 0xcc, 0xff } },
{ 0x7f, 0x66, 0x4c, 0xff }, { { 0x7f, 0x66, 0x4c, 0xff } },
{ 0x57, 0xa6, 0x4e, 0xff }, { { 0x57, 0xa6, 0x4e, 0xff } },
{ 0xcc, 0x4c, 0x4c, 0xff }, { { 0xcc, 0x4c, 0x4c, 0xff } },
{ 0x11, 0x11, 0x11, 0xff } { { 0x11, 0x11, 0x11, 0xff } }
}, DEFAULT_GRAY_PALETTE[16] = { }, DEFAULT_GRAY_PALETTE[16] = {
{ 0xf0, 0xf0, 0xf0, 0xff }, { { 0xf0, 0xf0, 0xf0, 0xff } },
{ 0x9d, 0x9d, 0x9d, 0xff }, { { 0x9d, 0x9d, 0x9d, 0xff } },
{ 0xbe, 0xbe, 0xbe, 0xff }, { { 0xbe, 0xbe, 0xbe, 0xff } },
{ 0xbf, 0xbf, 0xbf, 0xff }, { { 0xbf, 0xbf, 0xbf, 0xff } },
{ 0xb8, 0xb8, 0xb8, 0xff }, { { 0xb8, 0xb8, 0xb8, 0xff } },
{ 0x76, 0x76, 0x76, 0xff }, { { 0x76, 0x76, 0x76, 0xff } },
{ 0xd0, 0xd0, 0xd0, 0xff }, { { 0xd0, 0xd0, 0xd0, 0xff } },
{ 0x4c, 0x4c, 0x4c, 0xff }, { { 0x4c, 0x4c, 0x4c, 0xff } },
{ 0x99, 0x99, 0x99, 0xff }, { { 0x99, 0x99, 0x99, 0xff } },
{ 0x87, 0x87, 0x87, 0xff }, { { 0x87, 0x87, 0x87, 0xff } },
{ 0xa9, 0xa9, 0xa9, 0xff }, { { 0xa9, 0xa9, 0xa9, 0xff } },
{ 0x77, 0x77, 0x77, 0xff }, { { 0x77, 0x77, 0x77, 0xff } },
{ 0x65, 0x65, 0x65, 0xff }, { { 0x65, 0x65, 0x65, 0xff } },
{ 0x6e, 0x6e, 0x6e, 0xff }, { { 0x6e, 0x6e, 0x6e, 0xff } },
{ 0x76, 0x76, 0x76, 0xff }, { { 0x76, 0x76, 0x76, 0xff } },
{ 0x11, 0x11, 0x11, 0xff } { { 0x11, 0x11, 0x11, 0xff } }
}; };
const char font_atlas[256][11] = { const char font_atlas[256][11] = {