From c45e9f88bca21fce2fd7eea0351cfbd8a0960aec Mon Sep 17 00:00:00 2001 From: hkc Date: Sat, 5 Oct 2024 12:44:16 +0300 Subject: [PATCH] OpenMP, CPIv1 writer and proper size for converter Also moved *_varint into commons, writers are now separate functions, maybe I could move them into commons sometime and add readers in case I would use them at some point. --- Makefile | 3 ++ cc-common.c | 31 ++++++++++++++++++ cc-common.h | 6 ++++ cpi2png.c | 33 +++++-------------- img2cpi.c | 94 +++++++++++++++++++++++++++++++++++++++-------------- 5 files changed, 117 insertions(+), 50 deletions(-) diff --git a/Makefile b/Makefile index 189f517..21bdb98 100644 --- a/Makefile +++ b/Makefile @@ -18,4 +18,7 @@ dependencies/stb/%.o: dependencies/stb/%.h 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 diff --git a/cc-common.c b/cc-common.c index 46e19e8..6f97269 100644 --- a/cc-common.c +++ b/cc-common.c @@ -1,5 +1,36 @@ #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 } }, diff --git a/cc-common.h b/cc-common.h index 7abd47d..ebac596 100644 --- a/cc-common.h +++ b/cc-common.h @@ -2,6 +2,8 @@ #define _CC_COMMON_H_ #include +#include +#include typedef uint8_t GlyphBitmap[11]; @@ -22,4 +24,8 @@ struct palette { 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 diff --git a/cpi2png.c b/cpi2png.c index a321d82..dbef5c6 100644 --- a/cpi2png.c +++ b/cpi2png.c @@ -10,8 +10,6 @@ #include "cc-common.h" -bool read_varint(FILE *fp, unsigned int *out); - int main(int argc, char **argv) { if (argc < 3) { fprintf(stderr, "Usage: %s [input.cpi] [output.png]\n", argv[0]); @@ -43,8 +41,8 @@ int main(int argc, char **argv) { height = fgetc(fp_in); (void)fgetc(fp_in); // XXX: ignore scale } else if (version == 1) { - assert(read_varint(fp_in, &width) && "Failed to read width varint"); - assert(read_varint(fp_in, &height) && "Failed to read height varint"); + 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"); } @@ -55,7 +53,7 @@ int main(int argc, char **argv) { // 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 = calloc(16, sizeof(union color)); + 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, @@ -67,10 +65,13 @@ int main(int argc, char **argv) { 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 = fgetc(fp_in); - unsigned char color = fgetc(fp_in); + 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++) { @@ -84,25 +85,7 @@ int main(int argc, char **argv) { stbi_write_png(argv[2], width * 6, height * 9, 4, canvas, 0); - free(colors); free(canvas); fclose(fp_in); return EXIT_SUCCESS; } - -bool 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 false; - } - - return true; -} diff --git a/img2cpi.c b/img2cpi.c index 743bc0a..0eb74d5 100644 --- a/img2cpi.c +++ b/img2cpi.c @@ -13,6 +13,9 @@ #include #include #include "cc-common.h" +#ifdef USE_OPENMP +#include +#endif #define MAX_COLOR_DIFFERENCE 768 #define K_MEANS_ITERATIONS 4 @@ -91,17 +94,23 @@ struct k_means_state { 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 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_8x11(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); @@ -172,7 +181,7 @@ int main(int argc, char **argv) { 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) { @@ -225,23 +234,14 @@ int main(int argc, char **argv) { if (args.fast_mode) { convert_2x3(quantized_image, characters); } else { - convert_8x11(quantized_image, characters); + 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->colors[i].rgba.r, fp); - fputc(palette->colors[i].rgba.g, fp); - fputc(palette->colors[i].rgba.b, fp); - } - for (int i = 0; i < args.width * args.height; i++) { - fputc(characters[i].character, fp); - fputc(characters[i].bg | (characters[i].fg << 4), fp); + if (args.width < 256) { + save_cpi_0(fp, palette, characters, args.width, args.height); + } else { + save_cpi_1(fp, palette, characters, args.width, args.height); } fclose(fp); @@ -250,6 +250,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' }, @@ -593,8 +634,8 @@ void convert_2x3(const struct image_pal *img, struct cc_char *characters) { } } -void convert_8x11(const struct image_pal *img, struct cc_char *characters) { - int w = img->w / 8, h = img->h / 11; +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++) { @@ -603,12 +644,15 @@ void convert_8x11(const struct image_pal *img, struct cc_char *characters) { } for (int y = 0; y < h; y++) { +#ifdef USE_OPENMP +#pragma omp parallel for +#endif for (int x = 0; x < w; x++) { - float chunk_palette_diffs[8][11][0x10] = {{{(float) 0xffffff}}}; - for (int ox = 0; ox < 8; ox++) { - for (int oy = 0; oy < 11; oy++) { + 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 * 11 + oy) * w) * 8 + 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]; @@ -624,9 +668,9 @@ void convert_8x11(const struct image_pal *img, struct cc_char *characters) { } for (int color = 0x00; color <= 0xff; color++) { float difference = 0; - for (int oy = 0; oy < 11; oy++) { + for (int oy = 0; oy < 9; oy++) { unsigned char sym_line = cc_font_atlas[sym][oy]; - for (int ox = 0; ox < 8; ox++) { + for (int ox = 0; ox < 6; ox++) { bool lit = sym_line & (0x80 >> ox); difference += chunk_palette_diffs[ox][oy][lit ? color >> 4 : color & 0xF]; }