diff --git a/img2cpi.c b/img2cpi.c index 0eb74d5..1d82070 100644 --- a/img2cpi.c +++ b/img2cpi.c @@ -19,6 +19,10 @@ #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 cc_char { unsigned char character; @@ -27,6 +31,7 @@ struct cc_char { struct arguments { bool fast_mode; + bool verbose; int width, height; enum cpi_version { CPI_VERSION_AUTO, @@ -55,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, @@ -62,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 { @@ -135,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" @@ -165,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); @@ -177,6 +185,11 @@ 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); @@ -189,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 struct palette *palette = &cc_default_palette; switch (args.palette_type) { 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; - case PALETTE_PATH: assert(0 && "Not implemented"); break; + case PALETTE_LIST: assert(0 && "Not implemented"); break; // TODO + case PALETTE_PATH: assert(0 && "Not implemented"); break; // TODO default: assert(0 && "Unreachable"); } @@ -206,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"); @@ -231,16 +252,32 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } + if (args.verbose) { + printf("Converting image "); + } + if (args.fast_mode) { + if (args.verbose) { + printf(" using fast method\n"); + } convert_2x3(quantized_image, characters); } else { + if (args.verbose) { + printf(" using slow method\n"); + } convert_6x9(quantized_image, characters); } + if (args.verbose) { + printf("Conversion done, saving image "); + } + FILE *fp = fopen(args.output_path, "wb"); - if (args.width < 256) { + 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); @@ -305,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; @@ -321,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; @@ -582,6 +622,14 @@ float get_color_brightness(union color clr) { 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; @@ -632,6 +680,9 @@ void convert_2x3(const struct image_pal *img, struct cc_char *characters) { characters[x + y * w].fg = brightest_i; } } + if (args.verbose) { + printf("\n"); + } } void convert_6x9(const struct image_pal *img, struct cc_char *characters) { @@ -644,6 +695,14 @@ void convert_6x9(const struct image_pal *img, struct cc_char *characters) { } 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 @@ -687,6 +746,9 @@ void convert_6x9(const struct image_pal *img, struct cc_char *characters) { characters[x + y * w].fg = closest_color >> 4; } } + if (args.verbose) { + printf("\n"); + } } struct {