├── .gitignore ├── GRANT.txt ├── LICENSE.txt ├── Makefile ├── README.md ├── basicidea.c ├── basicidea.exe ├── demo.jpg ├── derasterize.c ├── derasterize.exe ├── derasterize.png ├── samples ├── egret.jpg ├── egret.jpg.uaart ├── lemur.jpg ├── lemur.jpg.uaart ├── snake.jpg ├── snake.jpg.uaart ├── vimperator.png ├── vimperator.png.uaart ├── wave.png └── wave.png.uaart ├── tally.sh └── uchar.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | -------------------------------------------------------------------------------- /GRANT.txt: -------------------------------------------------------------------------------- 1 | This copyright assignment (“grant”) is for the code of derasterize found on 2 | https://github.com/jart/derasterize/ and covers all my past and future 3 | contributions, and also all my past and future suggestions sent by email. 4 | 5 | Under this grant, I, CSDVRX place all my work in the public domain. 6 | 7 | I also grant Justine Tunney a perpetual right to relicense my work under any 8 | license of her chosing, as long as I am either A) not cited in any way (anonymous 9 | contributor), or B) cited as “csdvrx” 10 | 11 | By Csdvrx on Dec 19, 2019 12 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2019 Justine Alexandra Roberts Tunney, Charlotte Csdvrx 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | The following test images in "samples/" directory are public domain: 16 | 17 | - egret.jpg 18 | 19 | author: Jon Sullivan 20 | url: http://public-domain-photos.com/animals/egret-4.htm 21 | 22 | - snake.jpg 23 | 24 | author: Jon Sullivan 25 | url: http://public-domain-photos.com/animals/snake-4.htm 26 | 27 | - vimperator3.png 28 | 29 | author: @k_wizard 30 | url: http://quadrantem.com/~k_wizard/vimprtan/ 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: samples test 2 | 3 | CFLAGS=-g -march=native -Ofast 4 | LDFLAGS=-lm 5 | 6 | all: derasterize test 7 | 8 | derasterize: 9 | $(CC) $(CFLAGS) derasterize.c -o derasterize $(LDFLAGS) 10 | 11 | test: 12 | chmod +x derasterize.c 13 | ./derasterize.c -y12 -x30 ./samples/snake.jpg | ./tally.sh 14 | 15 | samples: 16 | for file in samples/* ; do ./derasterize.c -y20 -x70 $$file > $$file.uaart ; done 17 | 18 | clean: 19 | rm derasterize 20 | 21 | mrproper: 22 | rm samples/*uaart 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Derasterize](derasterize.png) 2 | 3 | This program converts pictures into Unicode text with Ansi colors for display 4 | within a terminal. It performs lots of AVX2 optimized math to deliver the best 5 | quality on modern terminals with 24-bit color support, e.g. Kitty, Gnome Terminal, 6 | CMD.EXE, mintty, mlterm, etc. 7 | 8 | Here's how it compares to the previous best, [hiptext](https://github.com/jart/hiptext): 9 | 10 | ![basicidea.c vs rasterize.c inside tmux using sample/snake.jpg](demo.jpg) 11 | 12 | Not so bad considering the original image is: 13 | 14 | ![original snake.jpg image](samples/snake.jpg) 15 | 16 | ## Getting Started 17 | 18 | You just need `cc` and `convert` on the PATH. Windows users can get that 19 | from Cygwin, MSYS2, or WSL. Mac users can try Homebrew. 20 | 21 | On Windows/msys2: 22 | ```bash 23 | pacman -S msys/gcc mingw64/mingw-w64-x86_64-imagemagick 24 | export PATH=$PATH:/mingw64/bin/ 25 | ``` 26 | 27 | On Debian and Ubuntu: 28 | ```bash 29 | apt-get install build-essential imagemagick 30 | ``` 31 | 32 | Then run: 33 | ```bash 34 | ./derasterize.c samples/lemur.png 35 | ``` 36 | -------------------------------------------------------------------------------- /basicidea.c: -------------------------------------------------------------------------------- 1 | /*bin/echo ' -*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;coding:utf-8 -*-┤ 2 | │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ 3 | ╞══════════════════════════════════════════════════════════════════════════════╡ 4 | │ To the extent possible under law, Justine Tunney has waived │ 5 | │ all copyright and related or neighboring rights to this file, │ 6 | │ as it is written in the following disclaimers: │ 7 | │ • http://unlicense.org/ │ 8 | │ • http://creativecommons.org/publicdomain/zero/1.0/ │ 9 | ╚────────────────────────────────────────────────────────────────────'>/dev/null 10 | if ! [ "${0%.*}" -nt "$0" ]; then 11 | cc -O -o "${0%.*}" "$0" || exit 12 | fi 13 | exec "./${0%.*}" "$@" 14 | exit 15 | 16 | OVERVIEW 17 | 18 | Simple Terminal Image Printer 19 | 20 | AUTHOR 21 | 22 | Justine Tunney 23 | 24 | DESCRIPTION 25 | 26 | This program demonstrates a straightforward technique for displaying 27 | PNG/GIF/JPG/etc. files from the command line. This program supports 28 | xterm256 and 24-bit color with unicode half blocks. 29 | 30 | For example: 31 | 32 | apt install build-essential imagemagick 33 | ./basicidea.c samples/lemur.png 34 | 35 | NOTES 36 | 37 | More advanced techniques exist for (1) gaining finer detail, as well 38 | as (2) reducing the number of bytes emitted; this program is here to 39 | get you started. */ 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #define ORDIE(X) \ 52 | do { \ 53 | if (!(X)) perror(#X), exit(1); \ 54 | } while (0) 55 | 56 | static int want24bit_; 57 | 58 | static const unsigned kXtermCube[] = {0, 0137, 0207, 0257, 0327, 0377}; 59 | 60 | static unsigned sqr(int x) { return x * x; } 61 | static int dist(int x, int y) { return x - y; } 62 | static unsigned rgbdist(unsigned a, unsigned b, unsigned c, unsigned x, 63 | unsigned y, unsigned z) { 64 | return sqr(dist(a, x)) + sqr(dist(b, y)) + sqr(dist(c, z)); 65 | } 66 | 67 | static unsigned uncube(unsigned x) { 68 | return x < 48 ? 0 : x < 115 ? 1 : (x - 35) / 40; 69 | } 70 | 71 | /** 72 | * Quantizes 24-bit RGB to xterm256 code range [16,256). 73 | */ 74 | unsigned rgb2xterm256(unsigned r, unsigned g, unsigned b) { 75 | unsigned av, ir, ig, ib, il, qr, qg, qb, ql; 76 | av = (r + g + b) / 3; 77 | ql = (il = av > 238 ? 23 : (av - 3) / 10) * 10 + 8; 78 | qr = kXtermCube[(ir = uncube(r))]; 79 | qg = kXtermCube[(ig = uncube(g))]; 80 | qb = kXtermCube[(ib = uncube(b))]; 81 | if (rgbdist(qr, qg, qb, r, g, b) <= rgbdist(ql, ql, ql, r, g, b)) { 82 | return ir * 36 + ig * 6 + ib + 020; 83 | } else { 84 | return il + 0350; 85 | } 86 | } 87 | 88 | /** 89 | * Prints raw packed 8-bit RGB data from memory. 90 | */ 91 | void PrintImage(void *p, unsigned yn, unsigned xn) { 92 | unsigned y, x; 93 | unsigned char(*rgb)[yn][xn][3] = p; 94 | for (y = 0; y < yn; y += 2) { 95 | if (y) printf("\r\n"); 96 | for (x = 0; x < xn; ++x) { 97 | if (want24bit_) { 98 | printf("\033[48;2;%hhu;%hhu;%hhu;38;2;%hhu;%hhu;%hhum▄", 99 | (*rgb)[y + 0][x][0], (*rgb)[y + 0][x][1], (*rgb)[y + 0][x][2], 100 | (*rgb)[y + 1][x][0], (*rgb)[y + 1][x][1], (*rgb)[y + 1][x][2]); 101 | } else { 102 | printf("\033[48;5;%hhu;38;5;%hhum▄", 103 | rgb2xterm256((*rgb)[y + 0][x][0], (*rgb)[y + 0][x][1], 104 | (*rgb)[y + 0][x][2]), 105 | rgb2xterm256((*rgb)[y + 1][x][0], (*rgb)[y + 1][x][1], 106 | (*rgb)[y + 1][x][2])); 107 | } 108 | } 109 | } 110 | printf("\033[0m\r"); 111 | } 112 | 113 | /** 114 | * Determines dimensions of teletypewriter. 115 | */ 116 | void GetTermSize(unsigned *out_rows, unsigned *out_cols) { 117 | struct winsize ws; 118 | ws.ws_row = 20; 119 | ws.ws_col = 80; 120 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); 121 | ioctl(STDIN_FILENO, TIOCGWINSZ, &ws); 122 | *out_rows = ws.ws_row; 123 | *out_cols = ws.ws_col; 124 | } 125 | 126 | void ReadAll(int fd, char *p, size_t n) { 127 | ssize_t rc; 128 | size_t got; 129 | do { 130 | ORDIE((rc = read(fd, p, n)) != -1); 131 | got = rc; 132 | if (!got && n) { 133 | fprintf(stderr, "error: expected eof\n"); 134 | exit(EXIT_FAILURE); 135 | } 136 | p += got; 137 | n -= got; 138 | } while (n); 139 | } 140 | 141 | unsigned char *LoadImageOrDie(char *path, unsigned yn, unsigned xn) { 142 | size_t size; 143 | void *rgb; 144 | char dim[10 + 1 + 10 + 1 + 1]; 145 | int pid, wstatus, readwrite[2]; 146 | sprintf(dim, "%ux%u" /* jfc */ "!", xn, yn); 147 | pipe(readwrite); 148 | if (!(pid = fork())) { 149 | close(readwrite[0]); 150 | dup2(readwrite[1], STDOUT_FILENO); 151 | execvp("convert", /* apt install imagemagick */ 152 | (char *const[]){"convert", path, "-resize", dim, "-colorspace", 153 | "RGB", "-depth", "8", "rgb:-", NULL}); 154 | _Exit(EXIT_FAILURE); 155 | } 156 | close(readwrite[1]); 157 | ORDIE((rgb = malloc((size = yn * xn * 3)))); 158 | ReadAll(readwrite[0], rgb, size); 159 | ORDIE(close(readwrite[0]) != -1); 160 | ORDIE(waitpid(pid, &wstatus, 0) != -1); 161 | ORDIE(WEXITSTATUS(wstatus) == 0); 162 | return rgb; 163 | } 164 | 165 | int main(int argc, char *argv[]) { 166 | int i; 167 | void *rgb; 168 | unsigned yn, xn; 169 | setlocale(LC_ALL, "C.UTF-8"); 170 | GetTermSize(&yn, &xn); 171 | yn *= 2; 172 | for (i = 1; i < argc; ++i) { 173 | if (strcmp(argv[i], "-t") == 0) { 174 | want24bit_ = 1; 175 | } else { 176 | rgb = LoadImageOrDie(argv[i], yn, xn); 177 | PrintImage(rgb, yn, xn); 178 | free(rgb); 179 | } 180 | } 181 | return 0; 182 | } 183 | -------------------------------------------------------------------------------- /basicidea.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csdvrx/derasterize/dc98c8b782664904eb48116c0c90c206dbb7b048/basicidea.exe -------------------------------------------------------------------------------- /demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csdvrx/derasterize/dc98c8b782664904eb48116c0c90c206dbb7b048/demo.jpg -------------------------------------------------------------------------------- /derasterize.c: -------------------------------------------------------------------------------- 1 | /*bin/echo ' -*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;coding:utf-8 -*-│ 2 | │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ 3 | ╞══════════════════════════════════════════════════════════════════════════════╡ 4 | │ Copyright 2019 Csdvrx & Justine Alexandra Roberts Tunney │ 5 | │ │ 6 | │ Permission to use, copy, modify, and/or distribute this software for any │ 7 | │ purpose with or without fee is hereby granted, provided that the above │ 8 | │ copyright notice and this permission notice appear in all copies. │ 9 | │ │ 10 | │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES │ 11 | │ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF │ 12 | │ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR │ 13 | │ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES │ 14 | │ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN │ 15 | │ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF │ 16 | │ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. │ 17 | ╞══════════════════════════════════════════════════════════════════════════════╡ 18 | │ To use this, install a compiler and (for now) imagemagick: │ 19 | │ apt install build-essential imagemagick │ 20 | │ │ 21 | │ Then make this file executable or run it with sh: │ 22 | │ chmod +x derasterize.c │ 23 | │ sh derasterize.c -y10 -x10 samples/wave.png │ 24 | │ │ 25 | │ You can use the c file directly: the blub below recompiles when needed. │ 26 | ╚────────────────────────────────────────────────────────────────────'>/dev/null 27 | if ! [ "${0%.*}" -nt "$0" ]; then 28 | if [ -z "$CC" ]; then 29 | CC=$(command -v clang-9) || 30 | CC=$(command -v clang-8) || 31 | CC=$(command -v clang) || 32 | CC=$(command -v gcc) || 33 | CC=$(command -v cc) 34 | fi 35 | COPTS="-g -march=native -Ofast" 36 | $CC $COPTS -o "${0%.*}" "$0" -lm || exit 37 | fi 38 | exec ./"${0%.*}" "$@" 39 | exit 40 | 41 | */ 42 | 43 | #define HELPTEXT "\n\ 44 | NAME\n\ 45 | \n\ 46 | derasterize - convert pictures to text using unicode ANSI art\n\ 47 | \n\ 48 | SYNOPSIS\n\ 49 | \n\ 50 | derasterize [PNG|JPG|ETC]...\n\ 51 | \n\ 52 | DESCRIPTION\n\ 53 | \n\ 54 | This program converts pictures into unicode text and ANSI colors so\n\ 55 | that images can be displayed within a terminal. It performs lots of\n\ 56 | AVX2 optimized math to deliver the best quality on modern terminals\n\ 57 | with 24-bit color support, e.g. Kitty, Gnome Terminal, CMD.EXE, etc\n\ 58 | \n\ 59 | The default output if fullscreen but can be changed:\n\ 60 | -xX\n\ 61 | If X is positive, hardcode the width in caracters to X\n\ 62 | If X is negative, remove as much from the fullscreen width\n\ 63 | -y\n\ 64 | If Y is positive, hardcode the height in caracters to Y\n\ 65 | If Y is negative, remove as much from the fullscreen height\n\ 66 | \n\ 67 | EXAMPLES\n\ 68 | \n\ 69 | $ ./derasterize.c samples/wave.png > wave.uaart\n\ 70 | $ cat wave.uaart\n\ 71 | \n\ 72 | AUTHORS\n\ 73 | \n\ 74 | Csdvrx \n\ 75 | Justine Tunney \n\ 76 | " 77 | 78 | #ifdef __ELF__ 79 | __asm__(".ident\t\"\\n\\n\ 80 | derasterize (ISC License)\\n\ 81 | Copyright 2019 Csdvrx & Justine Alexandra Roberts Tunney\""); 82 | #endif 83 | #include 84 | #include 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | #include 93 | #include 94 | #include 95 | #include 96 | #include 97 | #include 98 | #include 99 | // Can be missing in msys2 100 | #include 101 | 102 | #define BEST 0 103 | #define FAST 1 104 | #define FASTER 2 105 | 106 | #ifndef MODE 107 | #ifdef __AVX2__ 108 | #define MODE BEST 109 | #else 110 | #define MODE FAST 111 | #endif 112 | #endif 113 | 114 | // TODO: we should make that runtime choices, with separate command line options for MC and GN 115 | 116 | #if MODE == BEST 117 | #define MC 9u /* log2(#) of color combos to consider */ 118 | // FIXME: shouldn't that be 44 in best? Or maybe declare mode A, mode B, etc. 119 | #define GN 35u /* # of glyphs to consider */ 120 | #elif MODE == FAST 121 | #define MC 6u 122 | #define GN 35u 123 | #elif MODE == FASTER 124 | #define MC 4u 125 | #define GN 25u 126 | #endif 127 | 128 | #define FLOAT float 129 | #define FLOAT_C(X) X##f 130 | #define CN 3u /* # channels (rgb) */ 131 | #define YS 8u /* row stride -or- block height */ 132 | #define XS 4u /* column stride -or- block width */ 133 | #define GT 44u /* total glyphs */ 134 | #define BN (YS * XS) /* # scalars in block/glyph plane */ 135 | 136 | #define PHIPRIME 0x9E3779B1u 137 | #define SQR(X) ((X) * (X)) 138 | #define ABS(X) FLOAT_C(fabs)(X) 139 | #define SQRT(X) FLOAT_C(sqrt)(X) 140 | #define MOD(X, Y) FLOAT_C(fmod)(X, Y) 141 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) 142 | #define MAX(x, y) ((x) > (y) ? (x) : (y)) 143 | 144 | #define ARRAYLEN(A) \ 145 | ((sizeof(A) / sizeof(*(A))) / ((unsigned)!(sizeof(A) % sizeof(*(A))))) 146 | 147 | #define ORDIE(X) \ 148 | do \ 149 | if (!(X)) { \ 150 | exit(EXIT_FAILURE); \ 151 | } \ 152 | while (0) 153 | 154 | // The modes below use various unicodes for 'progresssive' pixelization: 155 | // each mode supersets the previous to increase resolution more and more. 156 | // Ideally, a fully dense mapping of the (Y*X) space defined by kGlyph size 157 | // would produce a picture perfect image, but at the cost of sampling speed. 158 | // Therefore, supersets are parcimonious: they only add the minimal set of 159 | // missing shapes that can increase resolution. 160 | // Ideally, this should be based on a study of the residual, but some logic 161 | // can go a long way: after some block pixelization, will need diagonals 162 | // FIXME: then shouldn't box drawing go right after braille? 163 | 164 | // TODO: explain the differences between each mode: 165 | // Mode A is full, empty, half blocks top and bottom: , █,▄,▀ 166 | // Mode B superset: with quadrants: ▐,▌,▝,▙,▗,▛,▖,▜,▘,▟,▞,▚, 167 | // Mode C superset: with fractional eights along X and Y 168 | // _,▁,▂,▃,▄,▅,▆,▇ :█:▉,▊,▋,▌,▍,▎,▏ 169 | // Mode X use box drawing, mode X use diagonal blocks, mode X use braille etc 170 | 171 | #define W(B, S) B##U << S 172 | #define G(AA, AB, AC, AD, BA, BB, BC, BD, CA, CB, CC, CD, DA, DB, DC, DD, EA, \ 173 | EB, EC, ED, FA, FB, FC, FD, GA, GB, GC, GD, HA, HB, HC, HD) \ 174 | (W(AA, 000) | W(AB, 001) | W(AC, 002) | W(AD, 003) | W(BA, 004) | \ 175 | W(BB, 005) | W(BC, 006) | W(BD, 007) | W(CA, 010) | W(CB, 011) | \ 176 | W(CC, 012) | W(CD, 013) | W(DA, 014) | W(DB, 015) | W(DC, 016) | \ 177 | W(DD, 017) | W(EA, 020) | W(EB, 021) | W(EC, 022) | W(ED, 023) | \ 178 | W(FA, 024) | W(FB, 025) | W(FC, 026) | W(FD, 027) | W(GA, 030) | \ 179 | W(GB, 031) | W(GC, 032) | W(GD, 033) | W(HA, 034) | W(HB, 035) | \ 180 | W(HC, 036) | W(HD, 037)) 181 | 182 | 183 | // The glyph size it set by the resolution of the most precise mode, ex: 184 | // - Mode C: along the X axis, need >= 8 steps for the 8 fractional width 185 | // FIXME: now we can only use 4 chars instead of the extra ▉,▊,▋,▌,▍,▎,▏ 186 | // 187 | // - Mode X: along the Y axis, need >= 8 steps to separate the maximal 6 dots 188 | // from the space left below, seen by overimposing an underline ⠿_ 189 | // along the 3 dots, the Y axis is least 1,0,1,0,1,0,0,1 so 8 steps 190 | // 191 | // Problem: fonts are taller than wider, and terminals are tradionally 80x24, so 192 | // - we shouldn't use square glyphs, 8x16 seems to be the minimal size 193 | // - we should adapt the conversion to BMP to avoid accidental Y downsampling 194 | 195 | static const uint32_t kGlyphs[GT] = /* clang-format off */ { 196 | /* U+0020 ' ' empty block [ascii:20,cp437:20] */ 197 | G(0,0,0,0, 198 | 0,0,0,0, 199 | 0,0,0,0, 200 | 0,0,0,0, 201 | 0,0,0,0, 202 | 0,0,0,0, 203 | 0,0,0,0, 204 | 0,0,0,0), 205 | /* U+2588 '█' full block [cp437] */ 206 | G(1,1,1,1, 207 | 1,1,1,1, 208 | 1,1,1,1, 209 | 1,1,1,1, 210 | 1,1,1,1, 211 | 1,1,1,1, 212 | 1,1,1,1, 213 | 1,1,1,1), 214 | /* U+2584 '▄' lower half block [cp437:dc] */ 215 | G(0,0,0,0, 216 | 0,0,0,0, 217 | 0,0,0,0, 218 | 0,0,0,0, 219 | 1,1,1,1, 220 | 1,1,1,1, 221 | 1,1,1,1, 222 | 1,1,1,1), 223 | /* U+2580 '▀' upper half block [cp437] */ 224 | G(1,1,1,1, 225 | 1,1,1,1, 226 | 1,1,1,1, 227 | 1,1,1,1, 228 | 0,0,0,0, 229 | 0,0,0,0, 230 | 0,0,0,0, 231 | 0,0,0,0), 232 | // Mode B 233 | /* U+2590 '▐' right half block [cp437:de] */ 234 | G(0,0,1,1, 235 | 0,0,1,1, 236 | 0,0,1,1, 237 | 0,0,1,1, 238 | 0,0,1,1, 239 | 0,0,1,1, 240 | 0,0,1,1, 241 | 0,0,1,1), 242 | /* U+258c '▌' left half block [cp437] */ 243 | G(1,1,0,0, 244 | 1,1,0,0, 245 | 1,1,0,0, 246 | 1,1,0,0, 247 | 1,1,0,0, 248 | 1,1,0,0, 249 | 1,1,0,0, 250 | 1,1,0,0), 251 | /* U+259d '▝' quadrant upper right */ 252 | G(0,0,1,1, 253 | 0,0,1,1, 254 | 0,0,1,1, 255 | 0,0,1,1, 256 | 0,0,0,0, 257 | 0,0,0,0, 258 | 0,0,0,0, 259 | 0,0,0,0), 260 | /* U+2599 '▙' quadrant upper left and lower left and lower right */ 261 | G(1,1,0,0, 262 | 1,1,0,0, 263 | 1,1,0,0, 264 | 1,1,0,0, 265 | 1,1,1,1, 266 | 1,1,1,1, 267 | 1,1,1,1, 268 | 1,1,1,0), 269 | /* U+2597 '▗' quadrant lower right */ 270 | G(0,0,0,0, 271 | 0,0,0,0, 272 | 0,0,0,0, 273 | 0,0,0,0, 274 | 0,0,1,1, 275 | 0,0,1,1, 276 | 0,0,1,1, 277 | 0,0,1,1), 278 | /* U+259b '▛' quadrant upper left and upper right and lower left */ 279 | G(1,1,1,1, 280 | 1,1,1,1, 281 | 1,1,1,1, 282 | 1,1,1,1, 283 | 1,1,0,0, 284 | 1,1,0,0, 285 | 1,1,0,0, 286 | 1,1,0,1), 287 | /* U+2596 '▖' quadrant lower left */ 288 | G(0,0,0,0, 289 | 0,0,0,0, 290 | 0,0,0,0, 291 | 0,0,0,0, 292 | 1,1,0,0, 293 | 1,1,0,0, 294 | 1,1,0,0, 295 | 1,1,0,0), 296 | /* U+259c '▜' quadrant upper left and upper right and lower right */ 297 | G(1,1,1,1, 298 | 1,1,1,1, 299 | 1,1,1,1, 300 | 1,1,1,1, 301 | 0,0,1,1, 302 | 0,0,1,1, 303 | 0,0,1,1, 304 | 0,0,1,0), 305 | /* U+2598 '▘' quadrant upper left */ 306 | G(1,1,0,0, 307 | 1,1,0,0, 308 | 1,1,0,0, 309 | 1,1,0,0, 310 | 0,0,0,0, 311 | 0,0,0,0, 312 | 0,0,0,0, 313 | 0,0,0,0), 314 | /* U+259F '▟' quadrant upper right and lower left and lower right */ 315 | G(0,0,1,1, 316 | 0,0,1,1, 317 | 0,0,1,1, 318 | 0,0,1,1, 319 | 1,1,1,1, 320 | 1,1,1,1, 321 | 1,1,1,1, 322 | 1,1,1,0), 323 | /* U+259e '▞' quadrant upper right and lower left */ 324 | G(0,0,1,1, 325 | 0,0,1,1, 326 | 0,0,1,1, 327 | 0,0,1,1, 328 | 1,1,0,0, 329 | 1,1,0,0, 330 | 1,1,0,0, 331 | 1,1,0,0), 332 | /* U+259a '▚' quadrant upper left and lower right */ 333 | G(1,1,0,0, 334 | 1,1,0,0, 335 | 1,1,0,0, 336 | 1,1,0,0, 337 | 0,0,1,1, 338 | 0,0,1,1, 339 | 0,0,1,1, 340 | 0,0,1,0), 341 | // Mode C 342 | /* U+2594 '▔' upper one eighth block */ 343 | G(1,1,1,1, 344 | 0,0,0,0, 345 | 0,0,0,0, 346 | 0,0,0,0, 347 | 0,0,0,0, 348 | 0,0,0,0, 349 | 0,0,0,0, 350 | 0,0,0,0), 351 | /* U+2581 '▁' lower one eighth block */ 352 | G(0,0,0,0, 353 | 0,0,0,0, 354 | 0,0,0,0, 355 | 0,0,0,0, 356 | 0,0,0,0, 357 | 0,0,0,0, 358 | 0,0,0,0, 359 | 1,1,1,1), 360 | /* U+2582 '▂' lower one quarter block */ 361 | G(0,0,0,0, 362 | 0,0,0,0, 363 | 0,0,0,0, 364 | 0,0,0,0, 365 | 0,0,0,0, 366 | 0,0,0,0, 367 | 1,1,1,1, 368 | 1,1,1,1), 369 | /* U+2583 '▃' lower three eighths block */ 370 | G(0,0,0,0, 371 | 0,0,0,0, 372 | 0,0,0,0, 373 | 0,0,0,0, 374 | 0,0,0,0, 375 | 1,1,1,1, 376 | 1,1,1,1, 377 | 1,1,1,1), 378 | /* U+2585 '▃' lower five eighths block */ 379 | G(0,0,0,0, 380 | 0,0,0,0, 381 | 0,0,0,0, 382 | 1,1,1,1, 383 | 1,1,1,1, 384 | 1,1,1,1, 385 | 1,1,1,1, 386 | 1,1,1,1), 387 | /* U+2586 '▆' lower three quarters block */ 388 | G(0,0,0,0, 389 | 0,0,0,0, 390 | 1,1,1,1, 391 | 1,1,1,1, 392 | 1,1,1,1, 393 | 1,1,1,1, 394 | 1,1,1,1, 395 | 1,1,1,1), 396 | /* U+2587 '▇' lower seven eighths block */ 397 | G(0,0,0,0, 398 | 1,1,1,1, 399 | 1,1,1,1, 400 | 1,1,1,1, 401 | 1,1,1,1, 402 | 1,1,1,1, 403 | 1,1,1,1, 404 | 1,1,1,1), 405 | /* U+258e '▎' left one quarter block */ 406 | G(1,0,0,0, 407 | 1,0,0,0, 408 | 1,0,0,0, 409 | 1,0,0,0, 410 | 1,0,0,0, 411 | 1,0,0,0, 412 | 1,0,0,0, 413 | 1,0,0,0), 414 | /* U+258a '▊' left three quarters block */ 415 | G(1,1,1,0, 416 | 1,1,1,0, 417 | 1,1,1,0, 418 | 1,1,1,0, 419 | 1,1,1,0, 420 | 1,1,1,0, 421 | 1,1,1,0, 422 | 1,1,1,0), 423 | /* ▁ *\ 424 | 2501▕━▎box drawings heavy horizontal 425 | \* ▔ */ 426 | G(0,0,0,0, 427 | 0,0,0,0, 428 | 0,0,0,0, 429 | 1,1,1,1, 430 | 1,1,1,1, 431 | 0,0,0,0, 432 | 0,0,0,0, 433 | 0,0,0,0), 434 | /* ▁ *\ 435 | 25019▕┉▎box drawings heavy quadruple dash horizontal 436 | \* ▔ */ 437 | G(0,0,0,0, 438 | 0,0,0,0, 439 | 0,0,0,0, 440 | 1,0,1,0, 441 | 0,1,0,1, 442 | 0,0,0,0, 443 | 0,0,0,0, 444 | 0,0,0,0), 445 | /* ▁ *\ 446 | 2503▕┃▎box drawings heavy vertical 447 | \* ▔ */ 448 | G(0,1,1,0, 449 | 0,1,1,0, 450 | 0,1,1,0, 451 | 0,1,1,0, 452 | 0,1,1,0, 453 | 0,1,1,0, 454 | 0,1,1,0, 455 | 0,1,1,0), 456 | /* ▁ *\ 457 | 254b▕╋▎box drawings heavy vertical and horizontal 458 | \* ▔ */ 459 | G(0,1,1,0, 460 | 0,1,1,0, 461 | 0,1,1,0, 462 | 1,1,1,1, 463 | 1,1,1,1, 464 | 0,1,1,0, 465 | 0,1,1,0, 466 | 0,1,1,0), 467 | /* ▁ *\ 468 | 2579▕╹▎box drawings heavy up 469 | \* ▔ */ 470 | G(0,1,1,0, 471 | 0,1,1,0, 472 | 0,1,1,0, 473 | 0,1,1,0, 474 | 0,0,0,0, 475 | 0,0,0,0, 476 | 0,0,0,0, 477 | 0,0,0,0), 478 | /* ▁ *\ 479 | 257a▕╺▎box drawings heavy right 480 | \* ▔ */ 481 | G(0,0,0,0, 482 | 0,0,0,0, 483 | 0,0,0,0, 484 | 0,0,1,1, 485 | 0,0,1,1, 486 | 0,0,0,0, 487 | 0,0,0,0, 488 | 0,0,0,0), 489 | /* ▁ *\ 490 | 257b▕╻▎box drawings heavy down 491 | \* ▔ */ 492 | G(0,0,0,0, 493 | 0,0,0,0, 494 | 0,0,0,0, 495 | 0,0,0,0, 496 | 0,1,1,0, 497 | 0,1,1,0, 498 | 0,1,1,0, 499 | 0,1,1,0), 500 | /* ▁ *\ 501 | 2578▕╸▎box drawings heavy left 502 | \* ▔ */ 503 | G(0,0,0,0, 504 | 0,0,0,0, 505 | 0,0,0,0, 506 | 1,1,0,0, 507 | 1,1,0,0, 508 | 0,0,0,0, 509 | 0,0,0,0, 510 | 0,0,0,0), 511 | /* ▁ *\ 512 | 250f▕┏▎box drawings heavy down and right 513 | \* ▔ */ 514 | G(0,0,0,0, 515 | 0,0,0,0, 516 | 0,0,0,0, 517 | 0,1,1,1, 518 | 0,1,1,1, 519 | 0,1,1,0, 520 | 0,1,1,0, 521 | 0,1,1,0), 522 | /* ▁ *\ 523 | 251b▕┛▎box drawings heavy up and left 524 | \* ▔ */ 525 | G(0,1,1,0, 526 | 0,1,1,0, 527 | 0,1,1,0, 528 | 1,1,1,0, 529 | 1,1,1,0, 530 | 0,0,0,0, 531 | 0,0,0,0, 532 | 0,0,0,0), 533 | /* ▁ *\ 534 | 2513▕┓▎box drawings heavy down and left 535 | \* ▔ */ 536 | G(0,0,0,0, 537 | 0,0,0,0, 538 | 0,0,0,0, 539 | 1,1,1,0, 540 | 1,1,1,0, 541 | 0,1,1,0, 542 | 0,1,1,0, 543 | 0,1,1,0), 544 | /* ▁ *\ 545 | 2517▕┗▎box drawings heavy up and right 546 | \* ▔ */ 547 | G(0,1,1,0, 548 | 0,1,1,0, 549 | 0,1,1,0, 550 | 0,1,1,1, 551 | 0,1,1,1, 552 | 0,0,0,0, 553 | 0,0,0,0, 554 | 0,0,0,0), 555 | /* ▁ *\ 556 | 25E2▕◢▎black lower right triangle 557 | \* ▔ */ 558 | G(0,0,0,0, 559 | 0,0,0,0, 560 | 0,0,0,0, 561 | 0,0,0,1, 562 | 0,0,1,1, 563 | 1,1,1,1, 564 | 0,0,0,0, 565 | 0,0,0,0), 566 | /* ▁ *\ 567 | 25E3▕◣▎black lower left triangle 568 | \* ▔ */ 569 | G(0,0,0,0, 570 | 0,0,0,0, 571 | 0,0,0,0, 572 | 1,0,0,0, 573 | 1,1,0,0, 574 | 1,1,1,1, 575 | 0,0,0,0, 576 | 0,0,0,0), 577 | /* ▁ *\ 578 | 25E4▕◥▎black upper right triangle 579 | \* ▔ */ 580 | G(0,0,0,0, 581 | 0,0,0,0, 582 | 0,0,0,0, 583 | 1,1,1,1, 584 | 0,0,1,1, 585 | 0,0,0,1, 586 | 0,0,0,0, 587 | 0,0,0,0), 588 | /* ▁ *\ 589 | 25E5▕◤▎black upper left triangle 590 | \* ▔ */ 591 | G(0,0,0,0, 592 | 0,0,0,0, 593 | 0,0,0,0, 594 | 1,1,1,1, 595 | 1,1,0,0, 596 | 1,0,0,0, 597 | 0,0,0,0, 598 | 0,0,0,0), 599 | /* ▁ *\ 600 | 2500▕═▎box drawings double horizontal 601 | \* ▔ */ 602 | G(0,0,0,0, 603 | 0,0,0,0, 604 | 1,1,1,1, 605 | 0,0,0,0, 606 | 1,1,1,1, 607 | 0,0,0,0, 608 | 0,0,0,0, 609 | 0,0,0,0), 610 | /* ▁ *\ 611 | 23BB▕⎻▎horizontal scan line 3 612 | \* ▔ */ 613 | G(0,0,0,0, 614 | 0,0,0,0, 615 | 1,1,1,1, 616 | 0,0,0,0, 617 | 0,0,0,0, 618 | 0,0,0,0, 619 | 0,0,0,0, 620 | 0,0,0,0), 621 | /* ▁ *\ 622 | 23BD▕⎼▎horizontal scan line 9 623 | \* ▔ */ 624 | G(0,0,0,0, 625 | 0,0,0,0, 626 | 0,0,0,0, 627 | 0,0,0,0, 628 | 0,0,0,0, 629 | 1,1,1,1, 630 | 0,0,0,0, 631 | 0,0,0,0), 632 | } /* clang-format on */; 633 | 634 | static const char16_t kRunes[GT] = { 635 | u' ', /* 0020 empty block [ascii:20,cp437:20] */ 636 | u'█', /* 2588 full block [cp437] */ 637 | u'▄', /* 2584 lower half block [cp437:dc] */ 638 | u'▀', /* 2580 upper half block [cp437] */ 639 | u'▐', /* 2590 right half block [cp437:de] */ 640 | u'▌', /* 258C left half block */ 641 | u'▝', /* 259D quadrant upper right */ 642 | u'▙', /* 2599 quadrant upper left and lower left and lower right */ 643 | u'▗', /* 2597 quadrant lower right */ 644 | u'▛', /* 259B quadrant upper left and upper right and lower left */ 645 | u'▖', /* 2596 quadrant lower left */ 646 | u'▜', /* 259C quadrant upper left and upper right and lower right */ 647 | u'▘', /* 2598 quadrant upper left */ 648 | u'▟', /* 259F quadrant upper right and lower left and lower right */ 649 | u'▞', /* 259E quadrant upper right and lower left */ 650 | u'▚', /* 259A quadrant upper left and lower right */ 651 | u'▔', /* 2594 upper one eighth block */ 652 | u'▁', /* 2581 lower one eighth block */ 653 | u'▂', /* 2582 lower one quarter block */ 654 | u'▃', /* 2583 lower three eighths block */ 655 | u'▅', /* 2585 lower five eighths block */ 656 | u'▆', /* 2586 lower three quarters block */ 657 | u'▇', /* 2587 lower seven eighths block */ 658 | u'▎', /* 258E left one quarter block */ 659 | u'▊', /* 258A left three quarters block */ 660 | u'━', /* 2501 box drawings heavy horizontal */ 661 | u'┉', /* 2509 box drawings heavy quadruple dash horizontal */ 662 | u'┃', /* 2503 box drawings heavy vertical */ 663 | u'╋', /* 254B box drawings heavy vertical & horiz. */ 664 | u'╹', /* 2579 box drawings heavy up */ 665 | u'╺', /* 257A box drawings heavy right */ 666 | u'╻', /* 257B box drawings heavy down */ 667 | u'╸', /* 2578 box drawings heavy left */ 668 | u'┏', /* 250F box drawings heavy down and right */ 669 | u'┛', /* 251B box drawings heavy up and left */ 670 | u'┓', /* 2513 box drawings heavy down and left */ 671 | u'┗', /* 2517 box drawings heavy up and right */ 672 | u'◢', /* 25E2 black lower right triangle */ 673 | u'◣', /* 25E3 black lower left triangle */ 674 | u'◥', /* 25E4 black upper right triangle */ 675 | u'◤', /* 25E5 black upper left triangle */ 676 | u'═', /* 2550 box drawings double horizontal */ 677 | u'⎻', /* 23BB horizontal scan line 3 */ 678 | u'⎼', /* 23BD horizontal scan line 9 */ 679 | }; 680 | 681 | /*───────────────────────────────────────────────────────────────────────────│─╗ 682 | │ derasterize § encoding ─╬─│┼ 683 | ╚────────────────────────────────────────────────────────────────────────────│*/ 684 | 685 | /** 686 | * Returns binary logarithm of integer. 687 | * @dominion 𝑥≥1 ∧ 𝑥∊ℤ 688 | * @return [0,31) 689 | */ 690 | static unsigned bsr(unsigned x) { 691 | #if -__STRICT_ANSI__ + !!(__GNUC__ + 0) && (__i386__ + __x86_64__ + 0) 692 | asm("bsr\t%1,%0" : "=r"(x) : "r"(x) : "cc"); 693 | #else 694 | static const unsigned char kDebruijn[32] = { 695 | 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 696 | 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31}; 697 | x |= x >> 1; 698 | x |= x >> 2; 699 | x |= x >> 4; 700 | x |= x >> 8; 701 | x |= x >> 16; 702 | x = kDebruijn[(x * 0x07c4acddu) >> 27]; 703 | #endif 704 | return x; 705 | } 706 | 707 | /** 708 | * Encodes Thompson-Pike variable length integer. 709 | * 710 | * @return word-encoded multibyte string 711 | * @note canonicalizes multibyte NUL and other numbers banned by the IETF 712 | * @note designed on a napkin in a New Jersey diner 713 | */ 714 | static unsigned long tpenc(wchar_t x) { 715 | if (0x00 <= x && x <= 0x7f) { 716 | return x; 717 | } else { 718 | static const struct ThomPike { 719 | unsigned char len, mark; 720 | } kThomPike[32 - 7] = { 721 | {1, 0xc0}, {1, 0xc0}, {1, 0xc0}, {1, 0xc0}, {2, 0xe0}, 722 | {2, 0xe0}, {2, 0xe0}, {2, 0xe0}, {2, 0xe0}, {3, 0xf0}, 723 | {3, 0xf0}, {3, 0xf0}, {3, 0xf0}, {3, 0xf0}, {4, 0xf8}, 724 | {4, 0xf8}, {4, 0xf8}, {4, 0xf8}, {4, 0xf8}, {5, 0xfc}, 725 | {5, 0xfc}, {5, 0xfc}, {5, 0xfc}, {5, 0xfc}, {5, 0xfc}, 726 | }; 727 | wchar_t wc; 728 | unsigned long ec; 729 | struct ThomPike op; 730 | ec = 0; 731 | wc = x; 732 | op = kThomPike[bsr(wc) - 7]; 733 | do { 734 | ec |= 0x3f & wc; 735 | ec |= 0x80; 736 | ec <<= 010; 737 | wc >>= 006; 738 | } while (--op.len); 739 | return ec | wc | op.mark; 740 | } 741 | } 742 | 743 | /** 744 | * Formats Thompson-Pike variable length integer to array. 745 | * 746 | * @param p needs at least 8 bytes 747 | * @return p + number of bytes written, cf. mempcpy 748 | * @note no NUL-terminator is added 749 | */ 750 | static char *tptoa(char *p, wchar_t x) { 751 | unsigned long w; 752 | for (w = tpenc(x); w; w >>= 010) *p++ = w & 0xff; 753 | return p; 754 | } 755 | 756 | /** 757 | * Formats byte to array as decimal. 758 | * 759 | * @param p needs at least 4 bytes 760 | * @return p + number of bytes written, cf. mempcpy 761 | * @note call btoa(0,0) once at startup 762 | * @note no NUL-terminator is added 763 | */ 764 | static char *btoa(char *p, int c) { 765 | static char kBtoa[256][4]; 766 | if (p) { 767 | memcpy(p, kBtoa[c & 0xff], 4); 768 | return p + (p[3] & 0xff); 769 | } else { 770 | for (c = 0; c < 256; ++c) { 771 | if (c < 10) { 772 | kBtoa[c][0] = '0' + c; 773 | kBtoa[c][3] = 1; 774 | } else if (c < 100) { 775 | kBtoa[c][0] = '0' + c / 10; 776 | kBtoa[c][1] = '0' + c % 10; 777 | kBtoa[c][3] = 2; 778 | } else { 779 | kBtoa[c][0] = '0' + c / 100; 780 | kBtoa[c][1] = '0' + c % 100 / 10; 781 | kBtoa[c][2] = '0' + c % 100 % 10; 782 | kBtoa[c][3] = 3; 783 | } 784 | } 785 | return 0; 786 | } 787 | } 788 | 789 | /*───────────────────────────────────────────────────────────────────────────│─╗ 790 | │ derasterize § colors ─╬─│┼ 791 | ╚────────────────────────────────────────────────────────────────────────────│*/ 792 | 793 | /** Perform a sRGB gamma correction by power 2.4 approximation 794 | * 795 | * @param x Argument, in the range 0.09 to 1. 796 | * @return Value raised into power 2.4, approximate. 797 | */ 798 | 799 | static FLOAT pow24(FLOAT x) { 800 | FLOAT x2, x3, x4; 801 | x2 = x * x; 802 | x3 = x * x * x; 803 | x4 = x * x * x * x; 804 | return FLOAT_C(0.0985766365536824) + FLOAT_C(0.839474952656502) * x2 + 805 | FLOAT_C(0.363287814061725) * x3 - 806 | FLOAT_C(0.0125559718896615) / 807 | (FLOAT_C(0.12758338921578) + FLOAT_C(0.290283465468235) * x) - 808 | FLOAT_C(0.231757513261358) * x - FLOAT_C(0.0395365717969074) * x4; 809 | } 810 | 811 | /** 812 | * Approximately linearize the sRGB gamma value. 813 | * 814 | * @param s sRGB gamma value, in the range 0 to 1. 815 | * @return Linearized sRGB gamma value, approximated. 816 | */ 817 | 818 | static FLOAT frgb2linl(FLOAT x) { 819 | FLOAT r1, r2; 820 | r1 = x / FLOAT_C(12.92); 821 | r2 = pow24((x + FLOAT_C(0.055)) / (FLOAT_C(1.0) + FLOAT_C(0.055))); 822 | return x < FLOAT_C(0.04045) ? r1 : r2; 823 | } 824 | 825 | /** 826 | * Converts standard RGB to linear RGB. 827 | * 828 | * This makes subtraction look good by flattening out the bias curve 829 | * that PC display manufacturers like to use. 830 | */ 831 | static void rgb2lin(FLOAT f[CN * BN], const unsigned char u[CN * BN]) { 832 | unsigned i; 833 | for (i = 0; i < CN * BN; ++i) f[i] = u[i]; 834 | for (i = 0; i < CN * BN; ++i) f[i] /= FLOAT_C(255.0); 835 | for (i = 0; i < CN * BN; ++i) f[i] = frgb2linl(f[i]); 836 | } 837 | 838 | /*───────────────────────────────────────────────────────────────────────────│─╗ 839 | │ derasterize § blocks ─╬─│┼ 840 | ╚────────────────────────────────────────────────────────────────────────────│*/ 841 | 842 | struct Cell { 843 | char16_t rune; 844 | unsigned char bg[CN], fg[CN]; 845 | }; 846 | 847 | /** 848 | * Serializes ANSI background, foreground, and UNICODE glyph to wire. 849 | */ 850 | static char *celltoa(char *p, struct Cell cell, struct Cell last) { 851 | *p++ = 033; 852 | *p++ = '['; 853 | *p++ = '4'; 854 | *p++ = '8'; 855 | *p++ = ';'; 856 | *p++ = '2'; 857 | *p++ = ';'; 858 | p = btoa(p, cell.bg[0]); 859 | *p++ = ';'; 860 | p = btoa(p, cell.bg[1]); 861 | *p++ = ';'; 862 | p = btoa(p, cell.bg[2]); 863 | *p++ = ';'; 864 | *p++ = '3'; 865 | *p++ = '8'; 866 | *p++ = ';'; 867 | *p++ = '2'; 868 | *p++ = ';'; 869 | p = btoa(p, cell.fg[0]); 870 | *p++ = ';'; 871 | p = btoa(p, cell.fg[1]); 872 | *p++ = ';'; 873 | p = btoa(p, cell.fg[2]); 874 | *p++ = 'm'; 875 | p = tptoa(p, cell.rune); 876 | return p; 877 | } 878 | 879 | /** 880 | * Picks ≤2**MC unique (bg,fg) pairs from product of lb. 881 | */ 882 | static unsigned combinecolors(unsigned char bf[1u << MC][2], 883 | const unsigned char bl[CN * BN]) { 884 | uint64_t hv, ht[(1u << MC) * 2]; 885 | unsigned i, j, n, b, f, h, hi, bu, fu; 886 | memset(ht, 0, sizeof(ht)); 887 | for (n = b = 0; b < BN && n < (1u << MC); ++b) { 888 | bu = bl[2 * BN + b] << 020 | bl[1 * BN + b] << 010 | bl[0 * BN + b]; 889 | hi = 0; 890 | hi = (((bu >> 000) & 0xff) + hi) * PHIPRIME; 891 | hi = (((bu >> 010) & 0xff) + hi) * PHIPRIME; 892 | hi = (((bu >> 020) & 0xff) + hi) * PHIPRIME; 893 | for (f = b + 1; f < BN && n < (1u << MC); ++f) { 894 | fu = bl[2 * BN + f] << 020 | bl[1 * BN + f] << 010 | bl[0 * BN + f]; 895 | h = hi; 896 | h = (((fu >> 000) & 0xff) + h) * PHIPRIME; 897 | h = (((fu >> 010) & 0xff) + h) * PHIPRIME; 898 | h = (((fu >> 020) & 0xff) + h) * PHIPRIME; 899 | h = h & 0xffff; 900 | h = MAX(1, h); 901 | hv = 0; 902 | hv <<= 030; 903 | hv |= fu; 904 | hv <<= 030; 905 | hv |= bu; 906 | hv <<= 020; 907 | hv |= h; 908 | for (i = 0;; ++i) { 909 | j = (h + i * (i + 1) / 2) & (ARRAYLEN(ht) - 1); 910 | if (!ht[j]) { 911 | ht[j] = hv; 912 | bf[n][0] = b; 913 | bf[n][1] = f; 914 | n++; 915 | break; 916 | } else if (ht[j] == hv) { 917 | break; 918 | } 919 | } 920 | } 921 | } 922 | return n; 923 | } 924 | 925 | /** 926 | * Computes distance between synthetic block and actual. 927 | */ 928 | static FLOAT adjudicate(unsigned b, unsigned f, unsigned g, 929 | const FLOAT lb[CN * BN]) { 930 | unsigned i, k, gu; 931 | FLOAT p[BN], q[BN], fu, bu, r; 932 | memset(q, 0, sizeof(q)); 933 | for (k = 0; k < CN; ++k) { 934 | gu = kGlyphs[g]; 935 | bu = lb[k * BN + b]; 936 | fu = lb[k * BN + f]; 937 | for (i = 0; i < BN; ++i) p[i] = (gu & (1u << i)) ? fu : bu; 938 | for (i = 0; i < BN; ++i) p[i] -= lb[k * BN + i]; 939 | // For a minimization problem, abs could do, but not faster in practice 940 | for (i = 0; i < BN; ++i) p[i] *= p[i]; 941 | for (i = 0; i < BN; ++i) q[i] += p[i]; 942 | } 943 | r = 0; 944 | // sqrt(x) is strictly increasing in x 945 | // so arg min sqrt(x) = arg min x 946 | // so we can go faster by simply commenting out 947 | // for (i = 0; i < BN; ++i) q[i] = SQRT(q[i]); 948 | for (i = 0; i < BN; ++i) r += q[i]; 949 | return r; 950 | } 951 | 952 | /** 953 | * Converts tiny bitmap graphic into unicode glyph. 954 | */ 955 | static struct Cell derasterize(unsigned char block[CN * BN]) { 956 | struct Cell cell; 957 | FLOAT r, best, lb[CN * BN]; 958 | unsigned i, n, b, f, g; 959 | unsigned char bf[1u << MC][2]; 960 | rgb2lin(lb, block); 961 | n = combinecolors(bf, block); 962 | best = -1u; 963 | cell.rune = 0; 964 | for (i = 0; i < n; ++i) { 965 | b = bf[i][0]; 966 | f = bf[i][1]; 967 | for (g = 0; g < GN; ++g) { 968 | r = adjudicate(b, f, g, lb); 969 | if (r < best) { 970 | best = r; 971 | cell.rune = kRunes[g]; 972 | cell.bg[0] = block[0 * BN + b]; 973 | cell.bg[1] = block[1 * BN + b]; 974 | cell.bg[2] = block[2 * BN + b]; 975 | cell.fg[0] = block[0 * BN + f]; 976 | cell.fg[1] = block[1 * BN + f]; 977 | cell.fg[2] = block[2 * BN + f]; 978 | if (!r) return cell; 979 | } 980 | } 981 | } 982 | return cell; 983 | } 984 | 985 | /*───────────────────────────────────────────────────────────────────────────│─╗ 986 | │ derasterize § graphics ─╬─│┼ 987 | ╚────────────────────────────────────────────────────────────────────────────│*/ 988 | 989 | /** 990 | * Turns packed 8-bit RGB graphic into ANSI UNICODE text. 991 | */ 992 | static char *RenderImage(char *vt, const unsigned char *rgb, unsigned yn, 993 | unsigned xn) { 994 | char *v; 995 | struct Cell c1, c2; 996 | unsigned y, x, i, j, k, w; 997 | unsigned char block[CN * BN]; 998 | const unsigned char *rows[YS]; 999 | v = vt; 1000 | c1.rune = 0; 1001 | rgb -= (w = xn * XS * CN); 1002 | for (y = 0; y < yn; ++y) { 1003 | if (y) { 1004 | while (v > vt && v[-1] == ' ') --v; 1005 | *v++ = '\r'; 1006 | *v++ = '\n'; 1007 | } 1008 | for (i = 0; i < YS; ++i) { 1009 | rows[i] = (rgb += w) - XS * CN; 1010 | } 1011 | for (x = 0; x < xn; ++x) { 1012 | for (i = 0; i < YS; ++i) { 1013 | rows[i] += XS * CN; 1014 | for (j = 0; j < XS; ++j) { 1015 | for (k = 0; k < CN; ++k) { 1016 | block[(k * YS + i) * XS + j] = rows[i][j * CN + k]; 1017 | } 1018 | } 1019 | } 1020 | c2 = derasterize(block); 1021 | v = celltoa(v, c2, c1); 1022 | c1 = c2; 1023 | } 1024 | } 1025 | return v; 1026 | } 1027 | 1028 | /*───────────────────────────────────────────────────────────────────────────│─╗ 1029 | │ derasterize § systems ─╬─│┼ 1030 | ╚────────────────────────────────────────────────────────────────────────────│*/ 1031 | 1032 | static void PrintImage(void *rgb, unsigned yn, unsigned xn) { 1033 | char *v, *vt; 1034 | vt = valloc(yn * (xn * (32 + (2 + (1 + 3) * 3) * 2 + 1 + 3)) * 1 + 5 + 1); 1035 | v = RenderImage(vt, rgb, yn, xn); 1036 | *v++ = '\r'; 1037 | *v++ = 033; 1038 | *v++ = '['; 1039 | *v++ = '0'; 1040 | *v++ = 'm'; 1041 | write(1, vt, v - vt); 1042 | free(vt); 1043 | } 1044 | 1045 | /** 1046 | * Determines dimensions of teletypewriter to default to full screen output 1047 | */ 1048 | static void GetTermSize(unsigned *out_rows, unsigned *out_cols) { 1049 | struct winsize ws; 1050 | ws.ws_row = 24; 1051 | ws.ws_col = 80; 1052 | if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) { 1053 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); 1054 | } 1055 | *out_rows = ws.ws_row; 1056 | *out_cols = ws.ws_col; 1057 | } 1058 | 1059 | static void ReadAll(int fd, char *p, size_t n) { 1060 | ssize_t rc; 1061 | size_t got; 1062 | do { 1063 | ORDIE((rc = read(fd, p, n)) != -1); 1064 | got = rc; 1065 | if (!got && n) { 1066 | exit(EXIT_FAILURE); 1067 | } 1068 | p += got; 1069 | n -= got; 1070 | } while (n); 1071 | } 1072 | 1073 | static unsigned char *LoadImageOrDie(char *path, unsigned yn, unsigned xn) { 1074 | void *rgb; 1075 | size_t size; 1076 | int pid, ws, rw[2]; 1077 | char dim[10 + 1 + 10 + 1 + 1]; 1078 | sprintf(dim, "%ux%u!", xn * XS, yn * YS); 1079 | pipe(rw); 1080 | if (!(pid = fork())) { 1081 | close(rw[0]); 1082 | dup2(rw[1], STDOUT_FILENO); 1083 | execlp("convert", "convert", path, "-resize", dim, "-colorspace", "RGB", 1084 | "-depth", "8", "rgb:-", NULL); 1085 | _exit(EXIT_FAILURE); 1086 | } 1087 | close(rw[1]); 1088 | ORDIE((rgb = valloc((size = yn * YS * xn * XS * CN)))); 1089 | ReadAll(rw[0], rgb, size); 1090 | ORDIE(close(rw[0]) != -1); 1091 | ORDIE(waitpid(pid, &ws, 0) != -1); 1092 | ORDIE(WEXITSTATUS(ws) == 0); 1093 | return rgb; 1094 | } 1095 | 1096 | int main(int argc, char *argv[]) { 1097 | int i, j; 1098 | char *option, *filename; 1099 | void *rgb; 1100 | unsigned yd, xd; 1101 | int y=0, x=0; 1102 | 1103 | btoa(0, 0); // FIXME: this is needed. But why? 1104 | 1105 | // Must provide at least one filename 1106 | if (argc < 2) { 1107 | printf (HELPTEXT); 1108 | exit (255); 1109 | } 1110 | 1111 | // Dirty option parsing without getopt 1112 | for (i = 1; i < argc; ++i) { 1113 | option= argv[i]; // option=-y12 1114 | switch( (int) option[0] ) { 1115 | case '-': // unix style 1116 | case '/': // dos style 1117 | option++; // option=y12 1118 | switch( (int) option[0]) { 1119 | case 'x': 1120 | x = atoi(++option); 1121 | break; 1122 | case 'y': 1123 | y = atoi(++option); 1124 | break; 1125 | case 'h': 1126 | printf (HELPTEXT); 1127 | exit (1); 1128 | default: 1129 | printf( "Unknown option %c\n\n", (int) option[0]); 1130 | } // switch 1131 | default: 1132 | filename=option; 1133 | } // switch 1134 | } //for i 1135 | 1136 | // Use termize to default to full screen if no x and y are given 1137 | GetTermSize(&yd, &xd); 1138 | 1139 | // if sizes are given, 2 cases: 1140 | // - positive values: use that as the target size 1141 | // - negative values: add, for ex to offset the command prompt size 1142 | if (y <= 0) { 1143 | y += yd; 1144 | } 1145 | if (x <= 0) { 1146 | x += xd; 1147 | } 1148 | 1149 | // FIXME: on the conversion stage should do 2Y because of halfblocks 1150 | // printf( "filename >%s<\tx >%d<\ty >%d<\n\n", filename, x, y); 1151 | rgb = LoadImageOrDie(filename, y, x); 1152 | PrintImage(rgb, y, x); 1153 | free(rgb); 1154 | return 0; 1155 | } 1156 | -------------------------------------------------------------------------------- /derasterize.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csdvrx/derasterize/dc98c8b782664904eb48116c0c90c206dbb7b048/derasterize.exe -------------------------------------------------------------------------------- /derasterize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csdvrx/derasterize/dc98c8b782664904eb48116c0c90c206dbb7b048/derasterize.png -------------------------------------------------------------------------------- /samples/egret.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csdvrx/derasterize/dc98c8b782664904eb48116c0c90c206dbb7b048/samples/egret.jpg -------------------------------------------------------------------------------- /samples/egret.jpg.uaart: -------------------------------------------------------------------------------- 1 | ▃▃▃▄▅▅▃▅▅▅▄▄▄▄▅▄▄▃▄▅▅▅▃▃▅▅▄▄▄▅▄▅▄▄▅▄▄▅▅▄▄▅▄▃▖▄▆▄▅▅▃▄▇▖▎▌▖▅▃▃▃▃▃▂▂▄▆┏▂▐ 2 | ▄▅▄▄▆▄▃▃▄▂▃▄▅▃▅▃▄▅▅▅▅▅▄▄▅▄▄▅▆▃▃▅▂▂▂▃▃▖▔▅▖▅▅▖▝▅▖▄▁▜▄▖▎▝▊▙▄▜▁▁ ▗ ▆▅▅▇▇▄▅ 3 | ▅▄▄▃▄▅▄▅▄▄▄▄▅▅▄▅▅▅▅▆▄▄▅▃▅▅▅▅▄▆▅▆▝▂▎▗▄▂▃▄▗▌▇▎▅▐▆▂▄▅▄▖▜▇▄▎▖▅▄▅▁▛▄▃▃▁▀╹▇▗ 4 | ▄▄▅▅▅▗▇▗▊▛▅▆▎━▆▄▂▄▄▂▐▀▃▁▃▂▂▂▅▃▃▁▅▅▄▁▂▄▃▄▂▅▂▄╹▗▘▄▌▂▊▊▎▐╹▟▇▗ ▗▄▅▄▃▀▁▁▘▁▂ 5 | ▃▁▆▄▝▄▄▃▃▜▅▄▄▜▆▅▆▆▂▂▄▄▃▃▃▂▂▅▄▃▅▃▃▄▃▃▃▄▆▄▆▄▅▃▃▄▃▄▂▆▄▄▘▗▘▘▘▄▗▅╻▃▄▆▅▆▅▂▅▗ 6 | ▅▄▄▄▄▅▅▆▃▃▅▄▄▃▃▅▄▃▄▅▃▃▃▃▅▄▃▄▅▅▄▅▃▂▅▂▄▃▃▄▄▃▂▄▄▄▃▄▄▃▁▄▁▁▁▃▁▐▃▃▘▂ ━╸▆▅▇▂▂ 7 | ▃▃▅▄▄▄▄▄▅▄▃▃▅▅▆▅▅▄▅▅▄▃▃▅▄▅▅▅▄▄▄▅▆▅▄▄▄▅▄▄▄▄▅▅▁▂▃▄▅▆▆▇▇▇▇▇▆▆▅▄▃▂▁▁▅▄▅▂▂▅ 8 | ▄▄▅▄▇▆▄▆▅▆▄▅▅▄▄▄▅▅▅▄▅▅▅▅▄▄▃▃▄▄▃▃▄▅▄▅▄▄▅▁▃▅▆▃▆▄▃▄▅▅▅▆▄▄▆▆▄▅▅▅▅▄▇▇▆▅▃▂▁▁ 9 | ▄▃▃▃▄▅▅▅▄▆▅▅▅▅▅▅▃▃▃▄▅▅▄▅▄▄▃▆▅▄▁▁▁▂▂▃▃▅▇▅▅▅▅▆▆▅▅▄▆▇▅▅▅▆▆▅▆▆▆▅▅▅▄▄▅▅▅▆▅▃ 10 | ▅▅▅▂▆▄▃▃▃▅▄▃▃▅▄▄▁▁▂▂▃▃▄▅▅▆▆▇▇▇▆▇▇▂▆▆▆▆▃▂▃▆▇▅▄▆▟▄▂▄▇▃▄▅▄▅▆▆▆▆▇▅▅▖▄▃▄▆▆▅ 11 | ▃▂▃▁▇▜▃▁▂▁▂▃▅▆▇▀▆▇▅▅▅▆▆▆▇▇▇▆▆▇▇▇▇━━▆▇▅▅▆▇▇▇▆▆▅▆▅▅▄▃▂▂▂▄▄▂▟▁━▇▝▅▅▄▆▆▊▂▄ 12 | ▅▅▅╻▃▄━━▀▃▃▅▆▆▆▆▆▆▅▅▅▅▅▅▆▆▆▅▄▄▄▃▃━▇▇▆━▁▆▇━▁▂▂▃▃▃▃▅▇▐▘▂▀▇▇▆▄▃▃▆▖╺▌▖▇▄━▂ 13 |     ▇▆▝▘▖▖ ▄▖▇╹▄▄▇▖▄▅▎ █    ▆▂▅▂▄ ▔█▔▇▗▄▇▃▄▅▅▆▅▅▆▃▃▆▆▃▐▄▅▆━▐▌▟▘▐▐┃▗▁▃▟ 14 |       ╹▆▙▛  ▝    ▇      ▃▃╻▁   ▙ █ ▆▅▗▇▄▆▆▅▄▅▆▆▅▃▃▄▅▄▟▇▄▆▅▅▄▄▅▄┏▇▆▐▆▌▗ 15 | ▖       ▇▎█  ━▂  ▂▗▄▅▅▅▄▄▁▆▆▂┉▅▆╹▆▅▃╺▐▇▇▇▆▂▁▅▆▆▅▅▅▆▂▙▌▅▅▆▅▇▄▅▆▃▂▃▐▝▆▄▃ 16 |  ▊      ╺▃▅▂▘▁  ▗▇       ┃▝▖  ▃▆▃▅▃▊▊▎┛▃▂▟▄▅▆▄▄▅▅▅▃▃▃▄▄▄▄▃▐▆▆▟▖▆▇▄▅▅▄▃ 17 | ▃▃▅▅ ┉  ▊━ ▎▝▎ ▊        ▎▃█╸▘  ▎ ▘━▐▊▅▄▅▅━▝▂▌▖▆▐▙▖╺┏▂▂▂▆▅▅▅▅▃▂▃▖▙▅▆▅▆▅ 18 |  ▂ ▊    ▘▇▄▜▝▄  ▖     ▁▄▄▇▇▘  ▅█▂▜▁ ▊▆▖▜▄▄▇▌▝▝▆▆▅▅▆▁▖▗▇▆▝▙▄▄▜▜▄▃▙▘▟▄▐▂ 19 | ▎ ▇▊╺   █╹ ▘▝▂▅▄▝▅▄▄▘ ▔█    ▄▅▂▂▂▄▁▁▘▂▆▅▅▃▆▅▅▅▆▄▄▄▄▃▆▅▄▅▅▅▆▇▇▆▆▖▁▟▊▗▃▖ 20 | ▎▗█▊  █      █▝▜▁▆▅▄▃▂▂▂▂▃▂▃▅▆█ ▇   ┉▌▇▇▆▆▅▄▅▄▅▅▅▄▃▆▅▅▄▆▄▅▅▄▅▅▟▇▝▗▚▖▅▄  -------------------------------------------------------------------------------- /samples/lemur.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csdvrx/derasterize/dc98c8b782664904eb48116c0c90c206dbb7b048/samples/lemur.jpg -------------------------------------------------------------------------------- /samples/lemur.jpg.uaart: -------------------------------------------------------------------------------- 1 | ▄▃▃▃▃▄▂▂▐▘▆▅▅▄▅▄▖▖▖▄▆▙▖▎▄▖▖▌▖▐▖▝▝▄▃▅▅▅▅▄▄▖▖▎▄▅▅▄▃▄▃▅▅▄▆▅▆▄▄▄▄▃▃▃▂▆▅▄▂▄ 2 | ▆▇━█▅▝▁▂▘▐▎▗▎▃▆▊▊▊▎▄▅▂▘▗▗▄▅▖▐▖▌▌▄▃▃▄▗▄▄▅▃▃▙▄▃▂▙▖▙▅▅▖▖▆╹▐▌▗▃▅▆▆▅▄▄▄▄▃▂▁ 3 | ▃▃▃▃▃▅▄▄▆▊▌▝▃▅▄▄▆▅╹▆▗▗▘▘▟▅▐▐▐▐▐▄▄▅▖▐▝▃▃▃▃▃━▅▄▁▁▐━▃▃▄▄▂▊▊▌▌▄▘▃▅▐▅▄▆▅▅▄▄ 4 | ▄▃▄▅▅▃▝▆▌▄▄▄▄▃▄▃▅▃▗▅▆▅▃▇▂▝▐▜▟▌▌▂▗▃▃▄▅▃▖▜▁▙▁▄▄▅▃▎▆▖▅▁▂▄▄▄▂▂▗▃▃▅▄▄▄▅▖▖▄▆ 5 | ▃▗▇▃▖▝▎▎▂▃▄▃▃▄▃▄▇▅▗▘▘▗▐▝▃▅▌▌▌▟▂▟▆▇━▁━▅▄▄▖▄▄▃▅┏▌▟▃▃▅▇▂▃▅▅▄▚▖▄▆▄▅▅▄▖▌▝▄▄ 6 | ▇▝▘▆▆▐▝▂▃▂▃▅▅▃▄▃▂▄▄▄▄▁▗▄▅▅▅▅▐▁▟▃▄▅▆▆▆▆▆▃▝▃▄▃▄▇▃▅▇▅▅▆▃▄▘▃▐▌▝▐▐▗▄▄▅▄▖▅▅▃ 7 | ▂┏╸▂▄▆▄╹▅▅▆▅▆▇▄▂━━▊▇▃▄▊▁▂▃▄▅▇▆▄▆▖▁▖▇▆▖▚▙▖▖╹┃▅▂▐▃▂▆▛▇▟▅▀▝▂▐▎▎▃▂▌▗▖▄▖▄▅▄ 8 | ▙▀▝▎▘▃╺▃╹▅▃▂▅▅▄▄▄▃▃▗▃▃▄▖▛▝▂▆▆▆▅▆▅▌▆▆▇▆┛▆▟▐┏▆▅━▆▄▅▆▇▘▇▊▃▇▄▖╋▎▝▐▐▊▜▅▝▅▄▃ 9 | ▜▅▄▙▁▗▅▄▗▆▄▅▅▃▅▂▆▅▄▊ ━▄▆▃▄▅▝▆━▃▅▟▄▛▅▁▃▗▆▐▆▅┉▝▇▇▂▃▘█▁  ▁▃▄▟▐▎▄▌▌▐▆▃▝▊▎▖ 10 | ▖▎▐━▆▘▃▅▄▘▃▅▔▆▅▆▅▐▗▅▅▁▗━╹▃━▟▆▆▆▆▜▆▃▄▃▂▐▅▙━▆━╺▝▐▁▁▘╺╺▆▆▅▄╺▅▆▖▖▟▖▝▖▂▝▝▝▆ 11 | ▗▄▃▘▟▅▄▂▅▜▄▝▂▆▇▆▝▝▝▃▄▗▆▆▃▞▆▃▀▆▆▅▀▆▇▇▚▇▇▆▂╸▆▗▇▅┏▃▄▇▆▇▅▟▃▄▆▆▙▆▗▝▖▙▖▝▗▌▙▃ 12 | ▅▅▘▂▁▃▄▜▟▂▅▆▄▃▄▂▄▄▄▅━▅▃▁▁▁▁▁▁▁▁▇▁▆▆▇▅▃▎▘▅╺▟▗▅▅▇▆▄▅▄▅━▄━▃▇▇▝▝▌▂▃▂▐▖▃▆▄▝ 13 | ▆▅▅▁▗▜▝▅▆▄▆▄▃▅▃━▃▄▜▃▐▙▄▖▆▇▇▇╻▇▇▆▅▄▂━━▄▆▅▇▆▆▅▅▄▅▟▅▄▐▅▅▆▇▀▅▅▝┛▀▅▇▆▁━╻▌▊▅ 14 | ▖▖▃┉▆▃┛▇┛▜▝▝▃▂▅▂▃▆▖▗▄▂▘▅▄▅┛▄╺▌▗▖▆▅▖▃━━▂━▆▄▅╸▌▇▗▂━▗▅▆━▁▂▂▄▃▐▊━▝▅▙▗▝▃▗▃▄ 15 | ▊▆▂▃▅▅▖▝▄▃▂╻▃▄▄▅▄▗▅▆▃▃▆▛▅┏▃▗▃▆▐▐▘▄▄▖▁━━━━▁▂▃▄┛▄┛▂▖▆▅▆▇▎▀▂▅▆▅▆▌▟━▂▎▅▃▖▅ 16 | ▊━━▎▜▃▄▅▁▁▁▁▅▝▁▅━▜▄┛▃▆▄▄▂▅▆▄▆▇▐▐▃▘▄▅▆▆▌▆▙▅▅▇▟▅▄▐▅▆▐▇━▜▂▘▄▃━▐▇┏▅▄▅▙▝▖▖▐ 17 | ▜▆▄ ▄▝▅▆▅▃▖▗▚▄┉▄▃▊╺▂▄▄▃▃▂▁▂▇▆▟▆▟▎▚▗▟▟▇▙▖▆▇━▇▎▄▆▃▝▆▃┏▟▙▄▆━▃▃▂▆▄▅▃▅▃▄▖▖▝ 18 | ╺▗▟▟▗▇╻▅▗▅▘┛▐▅╹▆▇▇▁▗▟▄▁▃▂▖━▃▇▘▙▎▊▖▁▆▊▝▊▇▇━▅▆▂▇▃▅▞▂▃▅▟━▃▅▆━━▃▁▁▃▎▊▜▝▇▐▊ 19 | ▝▝▃▃▃▅▄▀╻▌▄▃▆▄▅▆▃▗▇▗▅▁▐▟┏▄▙▗▐▝▖┛▖▁━━▅╹▂▂━▆▅▅▁▂▅▎▝▅▆▆┏▅▇▄▆▆▇▇▇▆▅▅▄▇▇▄▖▖ 20 | ▅▇▃▅▄▄▅▃▃▄▂▅▆▇▁▅▅▘▝▂▇▟▆▇▇▆┏▌▅▁▙▗▁▃▅┛▃▃▃▃▂━▆▂▀▄▃▃▃▃▅▇▄▆▃▅▆▅▆▂▄▅▇▆▅▆▐▌▆▐  -------------------------------------------------------------------------------- /samples/snake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csdvrx/derasterize/dc98c8b782664904eb48116c0c90c206dbb7b048/samples/snake.jpg -------------------------------------------------------------------------------- /samples/snake.jpg.uaart: -------------------------------------------------------------------------------- 1 | ▙▂▁▁▂▘▇▇▂▟▄▆▗▐▐▄╻▙▌▊▘▘▎▂▎▐▟▊▂▅▃▂▅▙▇▇▆▘▘━▇▇▄━▁▝▅▄▌▗▅▟▁▛▗▚▃▖▇▟▂▟▐▊▐━▎▂▅▆ 2 | ▆▆▄▆▅▃▄▅▆▛▎┛▔▇▐▎▎▝▊▂▌▝▅▖▐╺▐┏▄▂▙▐━▗╺▘▅▐▇▆▟━▆▂━▂▂▌━▗▗▁━▀▐▇╺▇▄▘▊▄▂▃▖▙▗▃▝▌ 3 | ▁▇▇▅▅▘▟▇━▟▚▊▌▎▗▁▘▐┃▂▙┏▆▐▝▆┏▐▄▇╻▟▇▖▟▇▐▁▊▙▐▝▖▆▅▇▇▇▝▁▁▁▁▃▂▁▁▝▄╺▆▘▄▆▝▌▙▃▌▗ 4 | ▄▄▆▎▅▆┛▙▘▙▀╺▃▖▆▃▃▃▅▆▘▖▗▐▎▎▜▆▅▜▄▄▖▐▅▐▊┏▄▝▂▅▟▗▄━▆▆▆▇▇▀▇▇▄▇▆▙▘▆▇▀▟▆▆▆━▄▌▐ 5 | ▗▃▅▅▖▇▐▐▆▂▁▘━▄▐▂┛▎▙▆╺▌┏▐▂▗▙▐▇▆▊▂▃▆▇▆▗▊▁▃▃▇▐▔▇▊▄▐▇▞▆▗▅▆▇▇▅▐▇▅▃━▐▇━▐▄▇▜▐ 6 | ▂▃▄▃▃▄▄▄▄▄▄▄▄▄▂╸▃╸▙┃▐▟▗▄▐▂▂▂▂▁▂▂▃▃▃▃▃▃▖▆▆▛▄▖▆▎▇▟▆▄▜▅▆▄▆▅▂▟▆▊━▆▐▐▆▅▅▗▆▐ 7 | ▆▆▅▇▆▆▅▄▐▆▆▖▆▆▆▅▃▃▃▃▃▅▆▆▆▆▅▄▃▃▂▄━▅▄▆▃▃▇▆▅▝▆▌▆╸┏╸▐▄▙▇▄▃▄▇▅▟┏▅▐▚▀▙▖━▁▁▃▃ 8 | ▅▄━▂▂▄▅▄▃▃▅▅▆▆▆▆▅▄▃━▆▅▄▃━▅▄▂▆▅▄▆▄▃▗▄▄▃━▆━▘━▐┛▅╸▇▇▄▆▄▇▊▁▄▇▖▐▇▇▁▂▄▄▇━▃▄▄ 9 | ▅▝▄▅▃▙▆▃▂▃▟▅▂▂▂▁▅▇▇▆▅▂▆▆▅▆▆▅▆▆▆▆▆▂▌▎▅▅▃▆▝▇▇▆▇▅▗▗▅▖▅▎▆▆╹▇▁▁▂▗▆┛▁▂▞▟▂▃▃▃ 10 | ▄━▐▖▅▅▂▄▇▜▇▙▖▄▂▂▃▄▄▅▂▁▁━▃▂▃▃▂▃▂▃▆▅▇▐▇▆▇▇▃▂▇▄▆▛▟▆▆▆▟▁▃▄▃▆▇▖▗▅▆▇▇▌▐▇▄▟▃▂ 11 | ▘▆▅▃▅▟▃▆▅▙▆▄▟▂▃▇▇▇▅▅▃▄━━▃▃▄▄▃▇▇▃▆▇▇▆▄▆▆━▂▄▄▃▆▅▆▆▇▗▇▇▌▗▀▇▜▜▗▆▇▇▐▎▇▇▆▜▙▄ 12 | ▇▛▄▆▅▄▄▃▅▄▂▃▘▃▂▝▖▅▟▃▄▄▂▌▐▆▗▆▆▇▀▂▃▅▃▅▄▄▗▄▄▄▅▆▇▅▎▗▟▇▇▆▐▅▅▅▅▆▅▅▃▄▝▖▅▃▆▀╸▇ 13 | ━▝▃▃▃▌▀▇━▟▅▅▄▃▇▅▞▄▟▄▁▐▐▇▆▇▅▆▄▗▅▟▅▄▟▃▅▖▖▃▞▃▃▃▆▇▂▙▄▄▄▄▂▃▆▆▟╺▇▃▟▂▆▞▟▘▆▃▆▃ 14 | ▂▌▝▃▊▆▆▆▌▄▃▟▆━▇▗▟▅▅▄▘▇╺▇▇▆▄▞▄▘▄▃▂▂▄▅▄▃▂▂▅▅▇▆▅▝▂▄▁▄▞▅▇▇▖▅▆▂━▆╺▀▌▆▃▆▄▄▂▄ 15 | ▆▆▆▅▃▂▁▅▙▝▃▊▝▇━▝▅▃▟▗▆▆▐▂▆▅▟▃▆▆▅▄▘▆▆▆▅▟▂▂▅▘▐▃▗▆▅▖▐▙▟▙▃▞▅▆▃▅▄▟▅▄━━▂▃▅▇▄▇ 16 | ▅▅▆▄▅▖▙▇▆▅▄▃▂▁▆▌▝▀▘▆▆▀▝▄▅▂▆▅▃▟▝▇▄╹▟▂╻▜▂▃▎╹▄▂▞▄▝▅▄▞▃▂▇▄▆▃▁▅▆▂▂▄▅▇▇▗╻▙▄▅ 17 | ▇▄▂▆▟▅▙▄▄▎▂▅▄▇▇▆▅▄▃▃▂▁▙▝▆▆▎▝▄▟▇━▀▊▄▀▘▄▃▀▟▙▅▄▂▙▂▄▁▃▅▃▂▂▃▄▅▆▇▎▅▁▆▇▅▇▐▃▜▌ 18 | ▁━▆━▄▆▅▆▅▅▅╻▇▆▅▂╹▃▗▅▖▜▇▇▆▆▅▄▄▃▃▃▂▂▂▂▂▂▂▂▂▂▃▃▃▄▄▅▆▆▇▇▄▆▟▅▆▆▅▃▟▀▆▃▄▅▂▂▃▅ 19 | ▃▟▅▄▅▙╸▇▘▄▂▗▊▙▆▇▆▄▗▄▖▄▁▊▗▆▛▇▇▄▀▅▅▅▅▆▇▇▇▟▃▄▅▆▆▃▂▙▅▙▙▌▗▘▆▅▃▅▅▄▄▃▄▅▂▇▆▄▃▅ 20 | ▂▆┛▅╹╹▙╺╺▂▄━▇▅▐▃▖▀┛▝▄▆▐▎▇▐╻▖▖▟▇╻▇▇▆▇▗▅▆▅▆▄▃▟▖▂▄▊▙▖▇▄▆▆▆▖▄▅▆▗▄▅▆▗▂▃▅▆▃▃  -------------------------------------------------------------------------------- /samples/vimperator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csdvrx/derasterize/dc98c8b782664904eb48116c0c90c206dbb7b048/samples/vimperator.png -------------------------------------------------------------------------------- /samples/vimperator.png.uaart: -------------------------------------------------------------------------------- 1 |                                      ▁▁▂▁▂▃▃ ▁▁▁▁                     2 |                        ▗▂▂▄▃▂▂▂▂▂▁▂▂▂▇▆▇▂▂▃▄▅▅▆▂▄▖▎                   3 |                        ▝▝▁▇▇╸▂▂▁▃▁▗▅▂▂▁▁▁▂▁ ▝━▂█▗▗▘                   4 |                         ▄▇▅▆▆▅▅┏╸▇╺▌▄▌▆▆▂▘▇╸▎ █▇▐█                    5 |                          ▆▇▎▁▐╺╹▂▁▁▐▎▇▆▝▃▂▃▅▊▜▖▊┃╻                    6 |                           ▊▝┃▞▂▅▃▂▂▁▁━▁▂▂▃▄▆▝┏▘▃▞▊   ▗▁▁▂▂▗▁  ▂       7 |        ▗▂▁▁▁▁▁▁▁▁▂▂▂▃▃▃▃▃▄▄▄▖▄▂▝▙▗▅▇▙▃▃▅━▆▗▅┏▅▄▃▟█   ▔▇▆▄━▇▇▅▂▁ ▁     8 |        ╹▄▄▃▇▇▇▌▃▄▘▄▄▄▅▅▅▅▅▅▅▅▅▅▅▆▗▘▇▆▖▅▎▗▆▇▝▄▁ █▇╻        ▇▇▖▂▅▖▎╻▜   9 |           ▆▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▗▆▇   ▆▙▟▂▁  ▔▝▃▜ ▌▎        ▐▐▌▄▙▖▎▖╻  10 |                              ▗▟▁      ▙▆▅┛    ▔▙▖▙▖▖      ▗▗▄▅▁▁▙▘▊▎  11 |                              ▇▆▅▃▂▁▂▁▂▆▝▁▂▁▁▂▃▄▅▆▆▟▖  ▁▁▁▂▅▀▄▅▅▅▅▅▟▐  12 |                               ▁▄▇▆▆▅▇▄▆▝▇▃▄▄▅▆▆▆▆━━▄▄▄▅▆▅▅▆▅▅▅▆▆▆▄▟   13 |                              ▗▆▘          ▁ ▁▁▂▃▅█ ▆▄▃▃▃▃▃▃▄▅▅▇▆▆█    14 |                             ╺▁▁▁▁▁▁▁▂▂▃▄▄▅▆▇█  ▁▁▁▂▃▅▇▔▇▇▇            15 |                              ▆▇▇▐▆▆▆▅▅▗▅▅▅▅▆▆▆▇▊▎▇█                   16 |                                ▊▐▎▇▇▇▇▖▐ ▊▎▇▇▇▇▊▐▎                    17 |                                ▊▊▎    ▝▎ ▎▐    ▊▌▐                    18 |                                 ▝▖▁▁▂▃▃▘▎▊▝▃▃▃▃▃▟▘                    19 |                                  ▝▁▁▁▂▂▃┛▊▂▁▁▁▂▄▘                     20 |                                   ▔▆▇▇    ▔▆▇▆▇                        -------------------------------------------------------------------------------- /samples/wave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csdvrx/derasterize/dc98c8b782664904eb48116c0c90c206dbb7b048/samples/wave.png -------------------------------------------------------------------------------- /tally.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Run the aart output though that script to check the frequency of the unicode used 3 | echo "Unicode tally of uaart output:" 4 | echo 5 | cat $1 | sed 's/\(.\)/\1\n/g' |sort|uniq -ic| \ 6 | # sort by count and remove the non unicode 7 | sort -nr -k1 | grep -v '[0-9m;\[]$' | tr -d '\n' 8 | 9 | echo 10 | -------------------------------------------------------------------------------- /uchar.h: -------------------------------------------------------------------------------- 1 | /* Just in case uchar.h is missing, as in some msys2 */ 2 | 3 | #if !(defined(__cplusplus) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ 4 | !defined(__GNUC__) || \ 5 | (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4)) 6 | typedef uint_least16_t char16_t; 7 | typedef uint_least32_t char32_t; 8 | #endif 9 | --------------------------------------------------------------------------------