123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704 |
- #include <assert.h>
- #include <stdio.h>
- #include <math.h>
- #define STB_IMAGE_IMPLEMENTATION
- #include "stb_image.h"
- #define STB_IMAGE_RESIZE_IMPLEMENTATION
- #include "stb_image_resize.h"
- #define DEFAULT_WIDTH 32
- int hsl256[][3] = {
- {0, 0, 0},
- {0, 100, 25},
- {120, 100, 25},
- {60, 100, 25},
- {240, 100, 25},
- {300, 100, 25},
- {180, 100, 25},
- {0, 0, 75},
- {0, 0, 50},
- {0, 100, 50},
- {120, 100, 50},
- {60, 100, 50},
- {240, 100, 50},
- {300, 100, 50},
- {180, 100, 50},
- {0, 0, 100},
- {0, 0, 0},
- {240, 99, 18},
- {240, 100, 26},
- {240, 100, 34},
- {240, 100, 42},
- {240, 100, 50},
- {120, 99, 18},
- {180, 99, 18},
- {197, 100, 26},
- {207, 100, 34},
- {213, 100, 42},
- {217, 100, 50},
- {120, 100, 26},
- {162, 100, 26},
- {180, 100, 26},
- {193, 100, 34},
- {202, 100, 42},
- {208, 100, 50},
- {120, 100, 34},
- {152, 100, 34},
- {166, 100, 34},
- {180, 100, 34},
- {191, 100, 42},
- {198, 100, 50},
- {120, 100, 42},
- {146, 100, 42},
- {157, 100, 42},
- {168, 100, 42},
- {180, 100, 42},
- {189, 100, 50},
- {120, 100, 50},
- {142, 100, 50},
- {151, 100, 50},
- {161, 100, 50},
- {170, 100, 50},
- {180, 100, 50},
- {0, 99, 18},
- {300, 99, 18},
- {282, 100, 26},
- {272, 100, 34},
- {266, 100, 42},
- {262, 100, 50},
- {60, 99, 18},
- {0, 0, 37},
- {240, 17, 45},
- {240, 33, 52},
- {240, 60, 60},
- {240, 100, 68},
- {77, 100, 26},
- {120, 17, 45},
- {180, 17, 45},
- {210, 33, 52},
- {220, 60, 60},
- {225, 100, 68},
- {87, 100, 34},
- {120, 33, 52},
- {150, 33, 52},
- {180, 33, 52},
- {200, 60, 60},
- {210, 100, 68},
- {93, 100, 42},
- {120, 60, 60},
- {140, 60, 60},
- {160, 60, 60},
- {180, 60, 60},
- {195, 100, 68},
- {97, 100, 50},
- {120, 100, 68},
- {135, 100, 68},
- {150, 100, 68},
- {165, 100, 68},
- {180, 100, 68},
- {0, 100, 26},
- {317, 100, 26},
- {300, 100, 26},
- {286, 100, 34},
- {277, 100, 42},
- {271, 100, 50},
- {42, 100, 26},
- {0, 17, 45},
- {300, 17, 45},
- {270, 33, 52},
- {260, 60, 60},
- {255, 100, 68},
- {60, 100, 26},
- {60, 17, 45},
- {0, 0, 52},
- {240, 20, 60},
- {240, 50, 68},
- {240, 100, 76},
- {73, 100, 34},
- {90, 33, 52},
- {120, 20, 60},
- {180, 20, 60},
- {210, 50, 68},
- {220, 100, 76},
- {82, 100, 42},
- {100, 60, 60},
- {120, 50, 68},
- {150, 50, 68},
- {180, 50, 68},
- {200, 100, 76},
- {88, 100, 50},
- {105, 100, 68},
- {120, 100, 76},
- {140, 100, 76},
- {160, 100, 76},
- {180, 100, 76},
- {0, 100, 34},
- {327, 100, 34},
- {313, 100, 34},
- {300, 100, 34},
- {288, 100, 42},
- {281, 100, 50},
- {32, 100, 34},
- {0, 33, 52},
- {330, 33, 52},
- {300, 33, 52},
- {280, 60, 60},
- {270, 100, 68},
- {46, 100, 34},
- {30, 33, 52},
- {0, 20, 60},
- {300, 20, 60},
- {270, 50, 68},
- {260, 100, 76},
- {60, 100, 34},
- {60, 33, 52},
- {60, 20, 60},
- {0, 0, 68},
- {240, 33, 76},
- {240, 100, 84},
- {71, 100, 42},
- {80, 60, 60},
- {90, 50, 68},
- {120, 33, 76},
- {180, 33, 76},
- {210, 100, 84},
- {78, 100, 50},
- {90, 100, 68},
- {100, 100, 76},
- {120, 100, 84},
- {150, 100, 84},
- {180, 100, 84},
- {0, 100, 42},
- {333, 100, 42},
- {322, 100, 42},
- {311, 100, 42},
- {300, 100, 42},
- {290, 100, 50},
- {26, 100, 42},
- {0, 60, 60},
- {340, 60, 60},
- {320, 60, 60},
- {300, 60, 60},
- {285, 100, 68},
- {37, 100, 42},
- {20, 60, 60},
- {0, 50, 68},
- {330, 50, 68},
- {300, 50, 68},
- {280, 100, 76},
- {48, 100, 42},
- {40, 60, 60},
- {30, 50, 68},
- {0, 33, 76},
- {300, 33, 76},
- {270, 100, 84},
- {60, 100, 42},
- {60, 60, 60},
- {60, 50, 68},
- {60, 33, 76},
- {0, 0, 84},
- {240, 100, 92},
- {69, 100, 50},
- {75, 100, 68},
- {80, 100, 76},
- {90, 100, 84},
- {120, 100, 92},
- {180, 100, 92},
- {0, 100, 50},
- {337, 100, 50},
- {328, 100, 50},
- {318, 100, 50},
- {309, 100, 50},
- {300, 100, 50},
- {22, 100, 50},
- {0, 100, 68},
- {345, 100, 68},
- {330, 100, 68},
- {315, 100, 68},
- {300, 100, 68},
- {31, 100, 50},
- {15, 100, 68},
- {0, 100, 76},
- {340, 100, 76},
- {320, 100, 76},
- {300, 100, 76},
- {41, 100, 50},
- {30, 100, 68},
- {20, 100, 76},
- {0, 100, 84},
- {330, 100, 84},
- {300, 100, 84},
- {50, 100, 50},
- {45, 100, 68},
- {40, 100, 76},
- {30, 100, 84},
- {0, 100, 92},
- {300, 100, 92},
- {60, 100, 50},
- {60, 100, 68},
- {60, 100, 76},
- {60, 100, 84},
- {60, 100, 92},
- {0, 0, 100},
- {0, 0, 3},
- {0, 0, 7},
- {0, 0, 10},
- {0, 0, 14},
- {0, 0, 18},
- {0, 0, 22},
- {0, 0, 26},
- {0, 0, 30},
- {0, 0, 34},
- {0, 0, 38},
- {0, 0, 42},
- {0, 0, 46},
- {0, 0, 50},
- {0, 0, 54},
- {0, 0, 58},
- {0, 0, 61},
- {0, 0, 65},
- {0, 0, 69},
- {0, 0, 73},
- {0, 0, 77},
- {0, 0, 81},
- {0, 0, 85},
- {0, 0, 89},
- {0, 0, 93},
- };
- int rgb256[][3] = {
- {0,0,0},
- {128,0,0},
- {0,128,0},
- {128,128,0},
- {0,0,128},
- {128,0,128},
- {0,128,128},
- {192,192,192},
- {128,128,128},
- {255,0,0},
- {0,255,0},
- {255,255,0},
- {0,0,255},
- {255,0,255},
- {0,255,255},
- {255,255,255},
- {0,0,0},
- {0,0,95},
- {0,0,135},
- {0,0,175},
- {0,0,215},
- {0,0,255},
- {0,95,0},
- {0,95,95},
- {0,95,135},
- {0,95,175},
- {0,95,215},
- {0,95,255},
- {0,135,0},
- {0,135,95},
- {0,135,135},
- {0,135,175},
- {0,135,215},
- {0,135,255},
- {0,175,0},
- {0,175,95},
- {0,175,135},
- {0,175,175},
- {0,175,215},
- {0,175,255},
- {0,215,0},
- {0,215,95},
- {0,215,135},
- {0,215,175},
- {0,215,215},
- {0,215,255},
- {0,255,0},
- {0,255,95},
- {0,255,135},
- {0,255,175},
- {0,255,215},
- {0,255,255},
- {95,0,0},
- {95,0,95},
- {95,0,135},
- {95,0,175},
- {95,0,215},
- {95,0,255},
- {95,95,0},
- {95,95,95},
- {95,95,135},
- {95,95,175},
- {95,95,215},
- {95,95,255},
- {95,135,0},
- {95,135,95},
- {95,135,135},
- {95,135,175},
- {95,135,215},
- {95,135,255},
- {95,175,0},
- {95,175,95},
- {95,175,135},
- {95,175,175},
- {95,175,215},
- {95,175,255},
- {95,215,0},
- {95,215,95},
- {95,215,135},
- {95,215,175},
- {95,215,215},
- {95,215,255},
- {95,255,0},
- {95,255,95},
- {95,255,135},
- {95,255,175},
- {95,255,215},
- {95,255,255},
- {135,0,0},
- {135,0,95},
- {135,0,135},
- {135,0,175},
- {135,0,215},
- {135,0,255},
- {135,95,0},
- {135,95,95},
- {135,95,135},
- {135,95,175},
- {135,95,215},
- {135,95,255},
- {135,135,0},
- {135,135,95},
- {135,135,135},
- {135,135,175},
- {135,135,215},
- {135,135,255},
- {135,175,0},
- {135,175,95},
- {135,175,135},
- {135,175,175},
- {135,175,215},
- {135,175,255},
- {135,215,0},
- {135,215,95},
- {135,215,135},
- {135,215,175},
- {135,215,215},
- {135,215,255},
- {135,255,0},
- {135,255,95},
- {135,255,135},
- {135,255,175},
- {135,255,215},
- {135,255,255},
- {175,0,0},
- {175,0,95},
- {175,0,135},
- {175,0,175},
- {175,0,215},
- {175,0,255},
- {175,95,0},
- {175,95,95},
- {175,95,135},
- {175,95,175},
- {175,95,215},
- {175,95,255},
- {175,135,0},
- {175,135,95},
- {175,135,135},
- {175,135,175},
- {175,135,215},
- {175,135,255},
- {175,175,0},
- {175,175,95},
- {175,175,135},
- {175,175,175},
- {175,175,215},
- {175,175,255},
- {175,215,0},
- {175,215,95},
- {175,215,135},
- {175,215,175},
- {175,215,215},
- {175,215,255},
- {175,255,0},
- {175,255,95},
- {175,255,135},
- {175,255,175},
- {175,255,215},
- {175,255,255},
- {215,0,0},
- {215,0,95},
- {215,0,135},
- {215,0,175},
- {215,0,215},
- {215,0,255},
- {215,95,0},
- {215,95,95},
- {215,95,135},
- {215,95,175},
- {215,95,215},
- {215,95,255},
- {215,135,0},
- {215,135,95},
- {215,135,135},
- {215,135,175},
- {215,135,215},
- {215,135,255},
- {215,175,0},
- {215,175,95},
- {215,175,135},
- {215,175,175},
- {215,175,215},
- {215,175,255},
- {215,215,0},
- {215,215,95},
- {215,215,135},
- {215,215,175},
- {215,215,215},
- {215,215,255},
- {215,255,0},
- {215,255,95},
- {215,255,135},
- {215,255,175},
- {215,255,215},
- {215,255,255},
- {255,0,0},
- {255,0,95},
- {255,0,135},
- {255,0,175},
- {255,0,215},
- {255,0,255},
- {255,95,0},
- {255,95,95},
- {255,95,135},
- {255,95,175},
- {255,95,215},
- {255,95,255},
- {255,135,0},
- {255,135,95},
- {255,135,135},
- {255,135,175},
- {255,135,215},
- {255,135,255},
- {255,175,0},
- {255,175,95},
- {255,175,135},
- {255,175,175},
- {255,175,215},
- {255,175,255},
- {255,215,0},
- {255,215,95},
- {255,215,135},
- {255,215,175},
- {255,215,215},
- {255,215,255},
- {255,255,0},
- {255,255,95},
- {255,255,135},
- {255,255,175},
- {255,255,215},
- {255,255,255},
- {8,8,8},
- {18,18,18},
- {28,28,28},
- {38,38,38},
- {48,48,48},
- {58,58,58},
- {68,68,68},
- {78,78,78},
- {88,88,88},
- {98,98,98},
- {108,108,108},
- {118,118,118},
- {128,128,128},
- {138,138,138},
- {148,148,148},
- {158,158,158},
- {168,168,168},
- {178,178,178},
- {188,188,188},
- {198,198,198},
- {208,208,208},
- {218,218,218},
- {228,228,228},
- {238,238,238},
- };
- static void rgb_to_hsl(int r, int g, int b, int *h, int *s, int *l)
- {
- float r01 = r/255.0f;
- float g01 = g/255.0f;
- float b01 = b/255.0f;
- float cmax = r01;
- if (g01 > cmax) cmax = g01;
- if (b01 > cmax) cmax = b01;
- float cmin = r01;
- if (g01 < cmin) cmin = g01;
- if (b01 < cmin) cmin = b01;
- float delta = cmax - cmin;
- float epsilon = 1e-6;
- float hf = 0;
- if (delta < epsilon) hf = 0;
- else if (cmax == r01) hf = 60.0f*fmod((g01 - b01)/delta, 6.0f);
- else if (cmax == g01) hf = 60.0f*((b01 - r01)/delta + 2);
- else if (cmax == b01) hf = 60.0f*((r01 - g01)/delta + 4);
- else assert(0 && "unreachable");
- float lf = (cmax + cmin)/2;
- float sf = 0;
- if (delta < epsilon) sf = 0;
- else sf = delta/(1 - fabsf(2*lf - 1));
- *h = fmodf(fmodf(hf, 360.0f) + 360.0f, 360.0f);
- *s = sf*100.0f;
- *l = lf*100.0f;
- }
- static int distance256(int table256[256][3], int i, int a, int b, int c)
- {
- int da = a - table256[i][0];
- int db = b - table256[i][1];
- int dc = c - table256[i][2];
- return da*da + db*db + dc*dc;
- }
- static int find_ansi_index(int table256[256][3], int a, int b, int c)
- {
- int index = 0;
- for (int i = 0; i < 256; ++i) {
- if (distance256(table256, i, a, b, c) < distance256(table256, index, a, b, c)) {
- index = i;
- }
- }
- return index;
- }
- static char *shift_args(int *argc, char ***argv)
- {
- assert(*argc > 0);
- char *result = **argv;
- *argc -= 1;
- *argv += 1;
- return result;
- }
- typedef enum {
- DIST_HSL,
- DIST_RGB,
- } Distance;
- void usage(const char *program)
- {
- fprintf(stderr, "Usage: %s [OPTIONS...] [FILES...]\n", program);
- fprintf(stderr, "Options:\n");
- fprintf(stderr, " -w width of the image default is %d\n", DEFAULT_WIDTH);
- fprintf(stderr, " -rgb search nearest color in RGB space\n");
- fprintf(stderr, " -hsl search nearest color in HSL space (default)\n");
- fprintf(stderr, " -h print this help and exit\n");
- fprintf(stderr, "Example:\n");
- fprintf(stderr, " $ %s -w 16 image1.png -rgb -w 32 image2.png\n", program);
- fprintf(stderr, " Print image1.png with width 16 and HSL search space then \n");
- fprintf(stderr, " print image2.png with widht 32 and RGB search space.\n");
- fprintf(stderr, " Each flags override the previous one.\n");
- }
- int main(int argc, char **argv)
- {
- // TODO: Add 16 colors support
- // TODO: Add TrueColor support
- assert(argc > 0);
- const char *program = shift_args(&argc, &argv);
- int resized_width = DEFAULT_WIDTH;
- Distance distance = DIST_HSL;
- // TODO: throw an error if not a single file was provided
- while (argc > 0) {
- const char *flag = shift_args(&argc, &argv);
- if (strcmp(flag, "-w") == 0) {
- if (argc <= 0) {
- fprintf(stderr, "ERROR: no value is provided for %s\n", flag);
- exit(1);
- }
- resized_width = atoi(shift_args(&argc, &argv));
- if (resized_width < 0) {
- fprintf(stderr, "ERROR: the value of %s can't be negative\n", flag);
- exit(1);
- }
- } else if (strcmp(flag, "-rgb") == 0) {
- distance = DIST_RGB;
- } else if (strcmp(flag, "-hsl") == 0) {
- distance = DIST_HSL;
- } else if (strcmp(flag, "-h") == 0) {
- usage(program);
- exit(0);
- } else {
- const char *file_path = flag;
- int width, height;
- uint32_t *pixels = (uint32_t*)stbi_load(file_path, &width, &height, NULL, 4);
- if (pixels == NULL) {
- fprintf(stderr, "ERROR: could not read file %s\n", file_path);
- continue;
- }
- int resized_height = height*resized_width/width;
- // TODO: maybe use a custom resize algorithm that does not require any memory allocation?
- // Similar to how olive.c resize the sprites.
- // (Though stb_image_resize supports a lot of fancy filters and stuff which we may try
- // to utilize to improve the results)
- uint32_t *resized_pixels = malloc(sizeof(uint32_t)*resized_width*resized_height);
- if (resized_pixels == NULL) {
- fprintf(stderr, "ERROR: could not allocate memory for resized image\n");
- exit(1);
- }
- // TODO: stbir_resize_uint8 returns int, which means it can fail. We should check for that.
- stbir_resize_uint8(
- (const unsigned char*)pixels, width, height, sizeof(uint32_t)*width,
- (unsigned char*)resized_pixels, resized_width, resized_height, sizeof(uint32_t)*resized_width,
- 4);
- for (int y = 0; y < resized_height; ++y) {
- for (int x = 0; x < resized_width; ++x) {
- uint32_t pixel = resized_pixels[y*resized_width + x];
- int r = (pixel>>8*0)&0xFF;
- int g = (pixel>>8*1)&0xFF;
- int b = (pixel>>8*2)&0xFF;
- int a = (pixel>>8*3)&0xFF;
- r = a*r/255;
- g = a*g/255;
- b = a*b/255;
- switch (distance) {
- case DIST_HSL: {
- int h, s, l;
- rgb_to_hsl(r, g, b, &h, &s, &l);
- printf("\e[48;5;%dm ", find_ansi_index(hsl256, h, s, l));
- } break;
- case DIST_RGB: {
- printf("\e[48;5;%dm ", find_ansi_index(rgb256, r, g, b));
- } break;
- default: assert(0 && "unreachable");
- }
- }
- printf("\e[0m\n");
- }
- printf("\e[0m\n");
- free(resized_pixels);
- stbi_image_free(pixels);
- }
- }
- return 0;
- }
- // TODO: automated testing
- // Since the whole behaviour of the program is represented by its stdout, it should be pretty easy to have some autotests
|