archive- Random tools & helpful resources for IRC |
git clone git://git.acid.vegas/archive.git |
Log | Files | Refs | Archive |
p2u.c (15067B)
1 /* Copyright (c) 2018 Trollforge. All rights reserved. 2 * 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions 5 * are met: 6 * 1. Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * 2. Redistributions in binary form must reproduce the above copyright 9 * notice, this list of conditions and the following disclaimer in the 10 * documentation and/or other materials provided with the distribution. 11 * 3. Trollforge's name may not be used to endorse or promote products 12 * derived from this software without specific prior written permission. 13 */ 14 15 #include <math.h> 16 #include <stdio.h> 17 #include <string.h> 18 #include <stdlib.h> 19 #include <unistd.h> 20 #include <getopt.h> 21 #include <stdbool.h> 22 23 #define STB_IMAGE_IMPLEMENTATION 24 #include "stb_image.h" 25 26 #define STB_IMAGE_RESIZE_IMPLEMENTATION 27 #include "stb_image_resize.h" 28 29 #define R 0 30 #define G 1 31 #define B 2 32 #define A 3 33 34 #define DIST(x, y) fabs(sqrtf((x[R] - y[R]) * (x[R] - y[R]) + \ 35 (x[G] - y[G]) * (x[G] - y[G]) + \ 36 (x[B] - y[B]) * (x[B] - y[B]))) 37 38 #define ANSI_FMT 0 39 #define MIRC_FMT 1 40 #define EMOJI_FMT 2 41 42 #define VGA_PAL 0 43 #define MIRC_PAL 1 44 #define XIRC_PAL 2 45 46 typedef struct block_s { 47 int color; 48 } block_t; 49 50 void usage(void); 51 int nearestcolor(float *pixel, int palette, float tlevel); 52 double huetorgb(double p, double q, double t); 53 void tweak(float *pixel, float sat, float lum); 54 55 int 56 main(int argc, char *argv[]) 57 { 58 int width = 0; 59 int height = 0; 60 int channels = 0; 61 block_t *block = NULL; 62 63 int format = ANSI_FMT; 64 int palette = VGA_PAL; 65 66 bool cp437 = false; 67 bool useice = false; 68 bool resize = false; 69 70 long resize_width = 0; 71 long resize_height = 0; 72 73 int ch = 0; 74 int fg = 0; 75 int bg = 0; 76 int lfg = 0; 77 int lbg = 0; 78 79 float *pixel = NULL; 80 float *resized = NULL; 81 82 float brightness = 100.0f; 83 float saturation = 100.0f; 84 float tlevel = 0.5f; 85 86 bool verbose = false; 87 88 while((ch = getopt(argc, argv, "b:f:p:s:t:w:v")) != -1) { 89 switch (ch) { 90 case 'b': 91 brightness = strtof(optarg, NULL); 92 break; 93 case 'f': 94 switch (optarg[0]) { 95 case 'a': 96 format = ANSI_FMT; 97 break; 98 case 'd': 99 format = ANSI_FMT; 100 cp437 = true; 101 useice = true; 102 break; 103 case 'm': 104 format = MIRC_FMT; 105 break; 106 case 'e': 107 format = EMOJI_FMT; 108 break; 109 default: 110 usage(); 111 break; 112 } 113 break; 114 case 'p': 115 switch (optarg[0]) { 116 case 'm': 117 palette = MIRC_PAL; 118 break; 119 case 'v': 120 palette = VGA_PAL; 121 break; 122 case 'x': 123 palette = XIRC_PAL; 124 break; 125 default: 126 usage(); 127 break; 128 } 129 break; 130 case 's': 131 saturation = strtof(optarg, NULL); 132 break; 133 case 't': 134 tlevel = strtof(optarg, NULL); 135 break; 136 case 'w': 137 resize_width = strtol(optarg, NULL, 10); 138 resize = true; 139 break; 140 case 'v': 141 verbose = true; 142 break; 143 default: 144 usage(); 145 } 146 } 147 argc -= optind; 148 argv += optind; 149 150 if (argc < 1) { 151 usage(); 152 } 153 154 /* XXX handle alpha eventually */ 155 /* channels is the number of channels in the original file, not our buffer) */ 156 pixel = stbi_loadf(argv[0], &width, &height, &channels, STBI_rgb_alpha); 157 158 if (!pixel) { 159 fprintf(stderr, "Unable to read file: %s\n", argv[0]); 160 usage(); 161 } 162 163 if (resize) { 164 resize_height = height * resize_width / width; 165 166 resized = malloc(sizeof(float) * resize_width * resize_height * STBI_rgb_alpha); 167 168 stbir_resize_float(pixel, width, height, 0, 169 resized, resize_width, resize_height, 0, 170 STBI_rgb_alpha); 171 172 free(pixel); 173 174 pixel = resized; 175 width = resize_width; 176 height = resize_height; 177 } 178 179 if (!pixel) { 180 usage(); 181 } 182 183 if (verbose) { 184 fprintf(stderr, "file: %s\n", argv[0]); 185 fprintf(stderr, "format: %s\n", format == ANSI_FMT ? "ANSI" : 186 format == MIRC_FMT ? "mIRC" : 187 "emoji"); 188 fprintf(stderr, "palette: %s\n", palette == VGA_PAL ? "VGA" : "mIRC"); 189 190 if (format == ANSI_FMT) { 191 fprintf(stderr, "iCE: %s\n", useice ? "true" : "false"); 192 fprintf(stderr, "CP437: %s\n", cp437 ? "true" : "false"); 193 } 194 195 fprintf(stderr, "resized: %s\n", resize ? "true" : "false"); 196 fprintf(stderr, "geometry: %dx%d\n", width, height); 197 fprintf(stderr, "channels: %d\n", STBI_rgb); 198 199 fprintf(stderr, "saturation: %f\n", saturation); 200 fprintf(stderr, "brightness: %f\n", brightness); 201 } 202 203 if (brightness != 100.0f || saturation != 100.0f) { 204 for (int i = 0; i < height; i++) { 205 for (int j = 0; j < width; j++) { 206 tweak(&pixel[((width * i) + j) * STBI_rgb_alpha], 207 saturation, brightness); 208 } 209 } 210 } 211 212 block = malloc(sizeof(block_t) * height * width); 213 214 if (format == EMOJI_FMT) { 215 palette = VGA_PAL; 216 } 217 218 for (int i = 0; i < height; i++) { 219 for (int j = 0; j < width; j++) { 220 block[(width * i) + j].color = 221 nearestcolor(&pixel[((width * i) + j) * STBI_rgb_alpha], 222 palette, tlevel); 223 } 224 } 225 free(pixel); 226 227 if (format == EMOJI_FMT) { 228 for (int i = 0; i < height; i++) { 229 for (int j = 0; j < width; j++) { 230 switch (block[(width * i) + j].color) { 231 case 0: 232 printf("⬛"); 233 break; 234 case 1: 235 printf("🔴"); 236 break; 237 case 2: 238 printf("💚"); 239 break; 240 case 3: 241 printf("💩"); 242 break; 243 case 4: 244 printf("💙"); 245 break; 246 case 5: 247 printf("💜"); 248 break; 249 case 6: 250 printf("📫"); 251 break; 252 case 7: 253 printf("👽"); 254 break; 255 case 8: 256 printf("💣"); 257 break; 258 case 9: 259 printf("🧠"); 260 break; 261 case 10: 262 printf("🎾"); 263 break; 264 case 11: 265 printf("🌞"); 266 break; 267 case 12: 268 printf("♿"); 269 break; 270 case 13: 271 printf("🐷"); 272 break; 273 case 14: 274 printf("💦"); 275 break; 276 case 15: 277 printf("💭"); 278 break; 279 /* transparent */ 280 case -1: 281 printf(" "); 282 break; 283 } 284 } 285 printf("\n"); 286 } 287 return 0; 288 } 289 290 for (int i = 0; i + 1 < height; i += 2) { 291 for (int j = 0; j < width; j++) { 292 fg = block[(width * i) + j].color; 293 bg = block[(width * (i + 1)) + j].color; 294 295 /* dont print color codes if we dont have to */ 296 if (j != 0 && lbg == bg && lfg == fg) { 297 /* try to save bytes */ 298 if (bg == fg) { 299 printf(" "); 300 } else { 301 cp437 ? printf("\xdf") : printf("▀"); 302 } 303 } else { 304 /* XXX we dont really have to print both attrs */ 305 /* XXX not handling alpha here either */ 306 if (format == ANSI_FMT) { 307 if (useice) { 308 printf("\x1b[%s%d;%dm%s", 309 /* bold and ice */ 310 (fg > 7 && bg > 7) ? "1;5;" : 311 /* bold only */ 312 (fg > 7 && bg < 8) ? "1;" : 313 /* ice only */ 314 (fg < 8 && bg > 7) ? "5;" : 315 /* neither */ 316 "", 317 fg > 7 ? fg - 8 + 30 : fg + 30, 318 bg > 7 ? bg - 8 + 40 : bg + 40, 319 bg == fg ? " " : cp437 ? "\xdf" : "▀"); 320 } else { 321 /* XXX this doesnt work for extended colors */ 322 printf("\x1b[%d;%dm%s", 323 fg < 8 ? fg + 30 : fg - 8 + 90, 324 bg < 8 ? bg + 40 : bg - 8 + 100, 325 bg == fg ? " " : cp437 ? "\xdf" : "▀"); 326 } 327 } else { 328 if (bg == -1 && fg != -1) { 329 printf("\x03%d%s", fg, cp437 ? "\xdf" : "▀"); 330 } else if (fg == -1 && bg != -1) { 331 printf("\x03%d%s", bg, cp437 ? "\xdc" : "▄"); 332 } else if (fg == -1 && bg == -1) { 333 printf("\x03 "); 334 } else { 335 printf("\x03%d,%d%s", fg, bg, 336 bg == fg ? " " : cp437 ? "\xdf" : "▀"); 337 } 338 } 339 } 340 lbg = bg; 341 lfg = fg; 342 } 343 /* reset to prevent line bleeding on terms */ 344 if (format == ANSI_FMT) { 345 printf("\x1b[0m%s", cp437 && width == 80 ? "" : "\n"); 346 } else { 347 printf("\n"); 348 } 349 } 350 return 0; 351 } 352 353 int 354 nearestcolor(float *pixel, int palette, float tlevel) 355 { 356 357 if (pixel[A] < tlevel) { 358 return -1; 359 } 360 /* vga palette, maybe add more */ 361 float vga_palette[16][3] = {{0.00f, 0.00f, 0.00f}, 362 {0.66f, 0.00f, 0.00f}, 363 {0.00f, 0.66f, 0.00f}, 364 {0.66f, 0.33f, 0.00f}, 365 {0.00f, 0.00f, 0.66f}, 366 {0.66f, 0.00f, 0.66f}, 367 {0.00f, 0.66f, 0.66f}, 368 {0.66f, 0.66f, 0.66f}, 369 {0.33f, 0.33f, 0.33f}, 370 {1.00f, 0.85f, 0.85f}, 371 {0.33f, 1.00f, 0.33f}, 372 {1.00f, 1.00f, 0.33f}, 373 {0.33f, 0.33f, 1.00f}, 374 {1.00f, 0.33f, 1.00f}, 375 {0.33f, 1.00f, 1.00f}, 376 {1.00f, 1.00f, 1.00f}}; 377 378 float mirc_palette[16][3] = {{1.00f, 1.00f, 1.00f}, 379 {0.00f, 0.00f, 0.00f}, 380 {0.00f, 0.00f, 0.50f}, 381 {0.00f, 0.57f, 0.00f}, 382 {1.00f, 0.00f, 0.00f}, 383 {0.50f, 0.00f, 0.00f}, 384 {0.61f, 0.00f, 0.61f}, 385 {0.98f, 0.50f, 0.00f}, 386 {1.00f, 1.00f, 0.00f}, 387 {0.00f, 0.98f, 0.00f}, 388 {0.00f, 0.57f, 0.57f}, 389 {0.00f, 1.00f, 1.00f}, 390 {0.00f, 0.33f, 0.98f}, 391 {1.00f, 0.00f, 1.00f}, 392 {0.50f, 0.50f, 0.50f}, 393 {0.82f, 0.82f, 0.82f}}; 394 395 float xirc_palette[99][3] = {{1.00f, 1.00f, 1.00f}, 396 {0.00f, 0.00f, 0.00f}, 397 {0.00f, 0.00f, 0.50f}, 398 {0.00f, 0.57f, 0.00f}, 399 {1.00f, 0.00f, 0.00f}, 400 {0.50f, 0.00f, 0.00f}, 401 {0.61f, 0.00f, 0.61f}, 402 {0.98f, 0.50f, 0.00f}, 403 {1.00f, 1.00f, 0.00f}, 404 {0.00f, 0.98f, 0.00f}, 405 {0.00f, 0.57f, 0.57f}, 406 {0.00f, 1.00f, 1.00f}, 407 {0.00f, 0.33f, 0.98f}, 408 {1.00f, 0.00f, 1.00f}, 409 {0.50f, 0.50f, 0.50f}, 410 {0.82f, 0.82f, 0.82f}, 411 {0.28f, 0.00f, 0.00f}, 412 {0.28f, 0.13f, 0.00f}, 413 {0.28f, 0.28f, 0.00f}, 414 {0.20f, 0.28f, 0.00f}, 415 {0.00f, 0.28f, 0.00f}, 416 {0.00f, 0.28f, 0.17f}, 417 {0.00f, 0.28f, 0.28f}, 418 {0.00f, 0.15f, 0.28f}, 419 {0.00f, 0.00f, 0.28f}, 420 {0.18f, 0.00f, 0.28f}, 421 {0.28f, 0.00f, 0.28f}, 422 {0.28f, 0.00f, 0.16f}, 423 {0.45f, 0.00f, 0.00f}, 424 {0.45f, 0.23f, 0.00f}, 425 {0.45f, 0.45f, 0.00f}, 426 {0.32f, 0.45f, 0.00f}, 427 {0.00f, 0.45f, 0.00f}, 428 {0.00f, 0.45f, 0.29f}, 429 {0.00f, 0.45f, 0.45f}, 430 {0.00f, 0.25f, 0.45f}, 431 {0.00f, 0.00f, 0.45f}, 432 {0.29f, 0.00f, 0.45f}, 433 {0.45f, 0.00f, 0.45f}, 434 {0.45f, 0.00f, 0.27f}, 435 {0.71f, 0.00f, 0.00f}, 436 {0.71f, 0.39f, 0.00f}, 437 {0.71f, 0.71f, 0.00f}, 438 {0.49f, 0.71f, 0.00f}, 439 {0.00f, 0.71f, 0.00f}, 440 {0.00f, 0.71f, 0.44f}, 441 {0.00f, 0.71f, 0.71f}, 442 {0.00f, 0.39f, 0.71f}, 443 {0.00f, 0.00f, 0.71f}, 444 {0.46f, 0.00f, 0.71f}, 445 {0.71f, 0.00f, 0.71f}, 446 {0.71f, 0.00f, 0.42f}, 447 {1.00f, 0.00f, 0.00f}, 448 {1.00f, 0.55f, 0.00f}, 449 {1.00f, 1.00f, 0.00f}, 450 {0.70f, 1.00f, 0.00f}, 451 {0.00f, 1.00f, 0.00f}, 452 {0.00f, 1.00f, 0.63f}, 453 {0.00f, 1.00f, 1.00f}, 454 {0.00f, 0.55f, 1.00f}, 455 {0.00f, 0.00f, 1.00f}, 456 {0.65f, 0.00f, 1.00f}, 457 {1.00f, 0.00f, 1.00f}, 458 {1.00f, 0.00f, 0.60f}, 459 {1.00f, 0.35f, 0.35f}, 460 {1.00f, 0.71f, 0.35f}, 461 {1.00f, 1.00f, 0.44f}, 462 {0.81f, 1.00f, 0.38f}, 463 {0.44f, 1.00f, 0.44f}, 464 {0.40f, 1.00f, 0.79f}, 465 {0.43f, 1.00f, 1.00f}, 466 {0.35f, 0.71f, 1.00f}, 467 {0.35f, 0.35f, 1.00f}, 468 {0.77f, 0.35f, 1.00f}, 469 {1.00f, 0.40f, 1.00f}, 470 {1.00f, 0.35f, 0.74f}, 471 {1.00f, 0.61f, 0.61f}, 472 {1.00f, 0.83f, 0.61f}, 473 {1.00f, 1.00f, 0.61f}, 474 {0.89f, 1.00f, 0.61f}, 475 {0.61f, 1.00f, 0.61f}, 476 {0.61f, 1.00f, 0.86f}, 477 {0.61f, 1.00f, 1.00f}, 478 {0.61f, 0.83f, 1.00f}, 479 {0.61f, 0.61f, 1.00f}, 480 {0.86f, 0.61f, 1.00f}, 481 {1.00f, 0.61f, 1.00f}, 482 {1.00f, 0.58f, 0.83f}, 483 {0.00f, 0.00f, 0.00f}, 484 {0.07f, 0.07f, 0.07f}, 485 {0.16f, 0.16f, 0.16f}, 486 {0.21f, 0.21f, 0.21f}, 487 {0.30f, 0.30f, 0.30f}, 488 {0.40f, 0.40f, 0.40f}, 489 {0.51f, 0.51f, 0.51f}, 490 {0.62f, 0.62f, 0.62f}, 491 {0.74f, 0.74f, 0.74f}, 492 {0.89f, 0.89f, 0.89f}, 493 {1.00f, 1.00f, 1.00f}}; 494 495 float delta = 10; 496 int color = 0; 497 498 if (palette == MIRC_PAL) { 499 for (int i = 0; i < 16; i++) { 500 if (DIST(pixel, mirc_palette[i]) < delta) { 501 delta = DIST(pixel, mirc_palette[i]); 502 color = i; 503 } 504 } 505 } else if (palette == VGA_PAL) { 506 for (int i = 0; i < 16; i++) { 507 if (DIST(pixel, vga_palette[i]) < delta) { 508 delta = DIST(pixel, vga_palette[i]); 509 color = i; 510 } 511 } 512 } else { /* XIRC_PAL */ 513 for (int i = 0; i < 99; i++) { 514 if (DIST(pixel, xirc_palette[i]) < delta) { 515 delta = DIST(pixel, xirc_palette[i]); 516 color = i; 517 } 518 } 519 } 520 return color; 521 } 522 523 double 524 huetorgb(double p, double q, double t) 525 { 526 if (t < 0.0f) { 527 t += 1.0f; 528 } else if (t > 1.0f) { 529 t -= 1.0f; 530 } 531 532 if (t < 1.0f/6.0f) { 533 return p + (q - p) * 6.0f * t; 534 } 535 536 if (t < 0.5f) { 537 return q; 538 } 539 540 if (t < 2.0f/3.0f) { 541 return p + (q - p) * ((2.0f/3.0f) - t) * 6.0f; 542 } 543 return p; 544 } 545 546 /* sat and lum are percentages */ 547 void 548 tweak(float *pixel, float sat, float lum) 549 { 550 /* convert rgb to hsl */ 551 float r = pixel[R]; 552 float g = pixel[G]; 553 float b = pixel[B]; 554 555 float max = r > g ? r > b ? r : b : g > b ? g : b; 556 float min = r < g ? r < b ? r : b : g < b ? g : b; 557 558 float h = (min + max) / 2.0f; 559 float s = (min + max) / 2.0f; 560 float l = (min + max) / 2.0f; 561 562 float d = max - min; 563 564 float q = 0.0f; 565 float p = 0.0f; 566 567 if (max == min) { 568 s = l = 0.0f; 569 } else { 570 s = l > 0.5f ? d / (2.0f - max - min) : d / (max + min); 571 if (max == r) { 572 h = (g - b) / d + (g < b ? 6.0f : 0.0f); 573 } else if (max == g) { 574 h = (b - r) / d + 2.0f; 575 } else { /* max == b */ 576 h = (r - g) / d + 4.0f; 577 } 578 } 579 h /= 6.0f; 580 581 /* apply tweaks */ 582 s *= sat * 0.01f; 583 l *= lum * 0.01f; 584 585 /* convert from hsl to rgb */ 586 if (s == 0.0f) { 587 r = g = b = l; 588 } else { 589 q = l < 0.5f ? l * (1.0f + s) : l + s - l * s; 590 p = 2 * l - q; 591 r = huetorgb(p, q, h + 1.0f/3.0f); 592 g = huetorgb(p, q, h); 593 b = huetorgb(p, q, h - 1.0f/3.0f); 594 } 595 596 /* clamp values */ 597 pixel[R] = r < 0.0f ? 0.0f : r > 1.0f ? 1.0f : r; 598 pixel[G] = g < 0.0f ? 0.0f : g > 1.0f ? 1.0f : g; 599 pixel[B] = b < 0.0f ? 0.0f : b > 1.0f ? 1.0f : b; 600 } 601 602 void 603 usage(void) 604 { 605 fprintf(stderr, "usage: p2u [options] input\n"); 606 fprintf(stderr, "\n"); 607 fprintf(stderr, "-b percent Adjust brightness levels, default is 100.\n"); 608 fprintf(stderr, "-f a|d|e|m Specify output format ANSI, DOS (ANSI with\n"); 609 fprintf(stderr, " CP437 characters), emoji or mirc. Default is ANSI.\n"); 610 fprintf(stderr, "-p m|v|x Specify palette to use, mirc, VGA, or extended mirc,\n"); 611 fprintf(stderr, " default is VGA.\n"); 612 fprintf(stderr, "-s percent Adjust saturation levels, default is 100.\n"); 613 fprintf(stderr, "-t percent Adjust transparency threshold of alpha channel,\n"); 614 fprintf(stderr, " default is 50.\n"); 615 fprintf(stderr, "-w width Specify output width, default is the image width.\n"); 616 fprintf(stderr, "\n"); 617 exit(1); 618 }