#include "_asciiporn.h" #include #include #include #include typedef unsigned char uch; typedef unsigned long long i64; int aa, bb, cc, ii, jj, kk, nn, xx, yy, zz; uch *uchp0, *uchp1, *uchp2, *uchp3; int *intp0, *intp1, *intp2, *intp3; i64 *i64p0, *i64p1, *i64p2, *i64p3; char *FPATH; FILE *FPIN = NULL, *FPOUT = NULL; uch ASC_BUF[0x400000]; int ASC_ROWS = 0, ASC_COLS = 0; uch *IMG_BUF = NULL; #define ERROR 1 #define ASC_FROWS 10 #define ASC_FCOLS 6 #define IMG_ROWS (ASC_ROWS * ASC_FROWS) #define IMG_COLS (ASC_COLS * ASC_FCOLS) #define IMG_ROWBYTES (IMG_COLS * 3) #define img_malloc() (IMG_BUF = (uch *) realloc(IMG_BUF, IMG_ROWS * IMG_ROWBYTES * sizeof(uch))) #define IMG_64 ((i64 *) ASC_BUF) #define IMG_COLOR ((uch *)(IMG_64 + ASC_ROWS * ASC_COLS)) #define IMG_CHR (IMG_COLOR + ASC_ROWS * ASC_COLS) #define ASC_STK (IMG_CHR + ASC_ROWS * ASC_COLS) #include "png.h" // png png_structp PNG_READ = NULL, PNG_WRITE = NULL; png_infop PNG_INFO = NULL; int png_close(int err) { if (PNG_READ != NULL) { png_destroy_read_struct((PNG_READ == NULL) ? NULL : &PNG_READ, (PNG_INFO == NULL) ? NULL : &PNG_INFO, NULL); PNG_READ = NULL; PNG_INFO = NULL; } else { png_destroy_write_struct((PNG_WRITE == NULL) ? NULL : &PNG_WRITE, (PNG_INFO == NULL) ? NULL : &PNG_INFO); PNG_WRITE = NULL; PNG_INFO = NULL; } if (FPIN != NULL) fclose(FPIN); FPIN = NULL; if (FPOUT != NULL) fclose(FPOUT); FPOUT = NULL; return err; } int png_read(char *fpath) { FPATH = fpath; if ((FPIN = fopen(FPATH, "rb")) == NULL) { fprintf(stderr, "asciiporn - fail fopen(%s, \"rb\")\n", FPATH); return png_close(ERROR); } if (fread(ASC_BUF, 1, 8, FPIN) != 8) { fprintf(stderr, "asciiporn - fail fread(%s)\n", FPATH); return png_close(ERROR); } if (!png_check_sig(ASC_BUF, 8)) { fprintf(stderr, "asciiporn - fail png_check_sig(%s)\n", FPATH); return png_close(ERROR); } if ((PNG_READ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL || (PNG_INFO = png_create_info_struct(PNG_READ)) == NULL) { fprintf(stderr, "asciiporn - fail png_create_read_struct(%s)\n", FPATH); return png_close(ERROR); } if (setjmp(png_jmpbuf(PNG_READ))) { fprintf(stderr, "asciiporn - fail png_setjmp(%s)\n", FPATH); return png_close(ERROR); } png_init_io(PNG_READ, FPIN); png_set_sig_bytes(PNG_READ, 8); png_read_info(PNG_READ, PNG_INFO); ASC_ROWS = (PNG_INFO->height + ASC_FROWS - 1) / ASC_FROWS; ASC_COLS = (PNG_INFO->width + ASC_FCOLS - 1) / ASC_FCOLS; if (sizeof(ASC_BUF) < ASC_ROWS * ASC_COLS * 64) { fprintf(stderr, "asciiporn - img(%s) too big\n", FPATH); return png_close(ERROR); } if (PNG_INFO->color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(PNG_READ); // palette -> rgb if (PNG_INFO->color_type == PNG_COLOR_TYPE_GRAY && PNG_INFO->bit_depth < 8) png_set_expand(PNG_READ); // gray 1,2,4bit -> gray 8bit if (png_get_valid(PNG_READ, PNG_INFO, PNG_INFO_tRNS)) png_set_expand(PNG_READ); // transparency -> rgba if (PNG_INFO->bit_depth == 16) png_set_strip_16(PNG_READ); // 16bit -> 8bit if (PNG_INFO->color_type == PNG_COLOR_TYPE_GRAY || PNG_INFO->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(PNG_READ); // gray -> rgb png_read_update_info(PNG_READ, PNG_INFO); if (PNG_INFO->rowbytes != PNG_INFO->width * 3) { fprintf(stderr, "asciiporn - invalid rowbytes(%s)\n", FPATH); return png_close(ERROR); } if (img_malloc() == NULL) { fprintf(stderr, "asciiporn - fail img_malloc(%s)\n", FPATH); return png_close(ERROR); } for (ii = 0, uchp0 = IMG_BUF; ii < PNG_INFO->height; ii ++, uchp0 += IMG_ROWBYTES) { png_read_row(PNG_READ, uchp0, NULL); for (jj = PNG_INFO->width * 3; jj < IMG_ROWBYTES; jj ++) uchp0[jj] = 127; // reset extra cols } for (ii = 0; ii < (IMG_ROWS - PNG_INFO->height) * IMG_ROWBYTES; ii ++, uchp0 ++) *uchp0 = 127; // reset extra rows return png_close(0); } int bmp_write(char *fpath) { // BUG - IMG_COLS must be multiple of 4 if ((FPOUT = fopen(fpath, "wb")) == NULL) { fprintf(stderr, "fail - fopen(%s, \"wb\")\n", fpath); return ERROR; } fwrite("BM\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x01\x00\x18\x00", 1, 26, FPOUT); uch *img_row, *img_col; for (img_row = IMG_BUF + (IMG_ROWS - 1) * IMG_ROWBYTES; img_row >= IMG_BUF; img_row -= IMG_ROWBYTES) // reverse rows for (xx = 0, img_col = img_row; xx < IMG_COLS; xx ++, img_col += 3) { fputc(img_col[2], FPOUT); fputc(img_col[1], FPOUT); fputc(img_col[0], FPOUT); // reverse rgb } int size = ftell(FPOUT); fseek(FPOUT, 2, SEEK_SET); for (ii = 0; ii < 4; ii ++) { fputc(size & 0xff, FPOUT); size >>= 8; } fseek(FPOUT, 18, SEEK_SET); fputc(IMG_COLS & 0xff, FPOUT); fputc(IMG_COLS >> 8, FPOUT); fputc(IMG_ROWS & 0xff, FPOUT); fputc(IMG_ROWS >> 8, FPOUT); fclose(FPOUT); return 0; } int png_write(char *fpath) { FPATH = fpath; if ((FPOUT = fopen(FPATH, "wb")) == NULL) { fprintf(stderr, "asciiporn - fail fopen(%s, \"wb\")\n", FPATH); return png_close(ERROR); } if ((PNG_WRITE = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL || (PNG_INFO = png_create_info_struct(PNG_WRITE)) == NULL) { fprintf(stderr, "asciiporn - fail png_create_write_struct(%s)\n", FPATH); return png_close(ERROR); } if (setjmp(png_jmpbuf(PNG_WRITE))) { fprintf(stderr, "asciiporn - fail png_setjmp(%s)\n", FPATH); return png_close(ERROR); } png_init_io(PNG_WRITE, FPOUT); png_set_IHDR(PNG_WRITE, PNG_INFO, IMG_COLS, IMG_ROWS, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); uch **ptr = (uch **) ASC_STK; for (ii = 1, *ptr = IMG_BUF; ii < IMG_ROWS; ii ++) ptr[ii] = ptr[ii - 1] + IMG_ROWBYTES; png_set_rows(PNG_WRITE, PNG_INFO, ptr); png_write_png(PNG_WRITE, PNG_INFO, PNG_TRANSFORM_IDENTITY, NULL); return png_close(0); } // helper fnc #define max(a, b) ((a > b) ? a : b) #define min(a, b) ((a < b) ? a : b) int popcount64(i64 x) { x -= ((x >> 1) & 0x5555555555555555LLU); // count 2bits x = (((x >> 2) & 0x3333333333333333LLU) + (x & 0x3333333333333333LLU)); // count 4bits x = (((x >> 4) + x) & 0x0f0f0f0f0f0f0f0fLLU); // count 8bits x += (x >> 8); // count 16bits x += (x >> 16); // count 32bits x += (x >> 32); // count 64bits return x & 0x7f; } bool ASC_PLAINTXT = false; // asciiporn void asc_plaintxt(int x) {ASC_PLAINTXT = x;} void img_64_to_img() { i64 mask = 1; for (ii = 0, kk = 0; ii < ASC_ROWS; ii ++) for (jj = 0, uchp0 = IMG_BUF + ii * ASC_FROWS * IMG_ROWBYTES; jj < ASC_COLS; jj ++, kk ++, uchp0 += ASC_FCOLS * 3) for (aa = 0, mask = 1; aa < ASC_FROWS; aa ++) for (bb = 0, uchp1 = uchp0 + aa * IMG_ROWBYTES; bb < ASC_FCOLS; bb ++, uchp1 += 3, mask <<= 1) { if (!(IMG_64[kk] & mask)) uchp1[0] = uchp1[1] = uchp1[2] = 0; // black else { // color uchp2 = (uch *) (IMG_PALETTE216 + IMG_COLOR[kk] * 3); uchp1[0] = uchp2[0]; uchp1[1] = uchp2[1]; uchp1[2] = uchp2[2]; }}} int img_read(char *fpath, int IMG_RESCALE) { if (png_read(fpath)) return ERROR; assert(IMG_RESCALE >= 1); if (sizeof(ASC_BUF) < ASC_ROWS * ASC_COLS * IMG_RESCALE * IMG_RESCALE * (8 + 1 + 1 + 32)) { fprintf(stderr, "asciiporn - img(%s) too big\n", FPATH); return png_close(ERROR); } if (IMG_RESCALE > 1) { aa = IMG_ROWS; bb = IMG_COLS; ASC_ROWS *= IMG_RESCALE; ASC_COLS *= IMG_RESCALE; if (img_malloc() == NULL) { fprintf(stderr, "asciiporn - fail img_malloc(%s)\n", FPATH); return png_close(ERROR); } uchp0 = IMG_BUF + aa * bb * 3 - 3; uchp1 = IMG_BUF + aa * IMG_ROWBYTES - 3; for (ii = aa; ii; ii --) for (jj = bb; jj; jj --, uchp0 -= 3) for (kk = IMG_RESCALE; kk; kk --, uchp1 -= 3) { uchp1[0] = uchp0[0]; uchp1[1] = uchp0[1]; uchp1[2] = uchp0[2]; } uchp0 = IMG_BUF + (aa - 1) * IMG_ROWBYTES; uchp1 = IMG_BUF + (IMG_ROWS - 1) * IMG_ROWBYTES; for (ii = aa; ii; ii --, uchp0 -= IMG_ROWBYTES) for (jj = IMG_RESCALE; jj; jj --, uchp1 -= IMG_ROWBYTES) for (kk = 0; kk < IMG_ROWBYTES; kk ++) uchp1[kk] = uchp0[kk]; } if (ASC_PLAINTXT) { for (uchp0 = IMG_COLOR, uchp1 = IMG_COLOR + ASC_ROWS * ASC_COLS; uchp0 != uchp1; uchp0 ++) *uchp0 = 215; } else { for (ii = 0, uchp0 = IMG_COLOR; ii < ASC_ROWS; ii ++) // color buf for (jj = ASC_COLS, uchp1 = IMG_BUF + ii * ASC_FROWS * IMG_ROWBYTES; jj; jj --, uchp0 ++, uchp1 += ASC_FCOLS * 3) *uchp0 = ((uchp1[0] * 6) >> 8) * 36 + ((uchp1[1] * 6) >> 8) * 6 + ((uchp1[2] * 6) >> 8); } int *histogram = (int *) ASC_STK; for (ii = 0, uchp0 = IMG_BUF; ii < IMG_ROWS * IMG_COLS; ii ++, uchp0 += 3) histogram[ IMG_BUF[ii] = (77 * uchp0[0] + 150 * uchp0[1] + 29 * uchp0[2]) >> 8 ] ++; // gray int cutoff; for (ii = IMG_ROWS * IMG_COLS * (ASC_PLAINTXT ? 0.25 : 0.5), cutoff = 0, intp0 = histogram; ii > 0; ii -= *intp0, cutoff ++, intp0 ++); #define dither(x) (x < 0 ? 0 : x > 255 ? 255 : x) // dither gray -> bw for (ii = IMG_ROWS, uchp0 = IMG_BUF, uchp1 = uchp0 + IMG_COLS - 1; ii; ii --) { for (jj = 0; jj < IMG_COLS; jj ++, uchp0 ++, uchp1 ++) { xx = *uchp0; *uchp0 = *uchp0 < cutoff ? 0 : 1; yy = xx - *uchp0 * 255; uchp0[1] = dither(((7 * yy) >> 4) + uchp0[1]); // cff 7/16 if (jj) uchp1[0] = dither(((3 * yy) >> 4) + uchp1[0]); // cff 3/16 - skip 1st col uchp1[1] = dither(((5 * yy) >> 4) + uchp1[1]); // cff 5/16 uchp1[2] = dither((( yy) >> 4) + uchp1[2]); // cff 1/16 - note dithering extends past last row }} for (ii = 0, i64p0 = IMG_64; ii < ASC_ROWS; ii ++) // block -> i64 for (jj = ASC_COLS, uchp0 = IMG_BUF + ii * ASC_FROWS * IMG_COLS; jj; jj --, uchp0 += ASC_FCOLS, i64p0 ++) for (aa = 0, *i64p0 = 0; aa < ASC_FROWS; aa ++) for (bb = 0, uchp1 = uchp0 + aa * IMG_COLS; bb < ASC_FCOLS; bb ++, uchp1 ++) *i64p0 |= (i64)* uchp1 << (aa * ASC_FCOLS + bb); return 0; } #define _itp(idx) {\ i64p1 = (i64 *)(ASC_FBMP + idx * 5);\ xx = popcount64(*i64p0 & *i64p1) * wmatch;\ xx -= popcount64(*i64p0 ^ *i64p1) * wmismatch;\ for (kk = 1; kk < 5; kk ++) xx += popcount64(*i64p0 & i64p1[kk]);\ if (xx > wmax) {*uchp0 = idx; wmax = xx;}\ } int asc_itp(int wmatch, int wmismatch) { // main algorithm - interpolate 64bit block -> ascii chr int wmax; if (ASC_PLAINTXT) { // plain txt for (uchp0 = IMG_CHR, i64p0 = IMG_64, i64p3 = i64p0 + ASC_ROWS * ASC_COLS; i64p0 != i64p3; uchp0 ++, i64p0 ++) { // loop - popcount xx = popcount64(*i64p0); assert (xx <= 60); if (!xx) *uchp0 = 0xc0; // black ASC_PLAINTXT else for (ii = 0xa1, wmax = -0x80000000; ii < 0xff; ii ++) _itp(ii); }} else { // ansi txt for (uchp0 = IMG_CHR, i64p0 = IMG_64, i64p3 = i64p0 + ASC_ROWS * ASC_COLS; i64p0 != i64p3; uchp0 ++, i64p0 ++) { // loop - popcount xx = popcount64(*i64p0); assert (xx <= 60); if (!xx) *uchp0 = 0x20; // black else { for (ii = 0; !(ASC_FBIN[ii] <= xx && xx < ASC_FBIN[ii + 1]); ii ++); // find fbin for (jj = ASC_FBINN[ii], intp0 = (int *)ASC_FBINI[ii], wmax = -0x80000000; jj; jj --, intp0 ++) _itp(*intp0); }}} return 0; } bool TERM_BACKGROUND = false; void term_reset() {*uchp0 = '\033'; uchp0 ++; *uchp0 = '['; uchp0 ++; *uchp0 = '0'; uchp0 ++; *uchp0 = 'm'; uchp0 ++;} void term_color(bool background, int color) { *uchp0 = '\033'; uchp0 ++; *uchp0 = '['; uchp0 ++; *uchp0 = background ? '4' : '3'; uchp0 ++; // background *uchp0 = '8'; uchp0 ++; *uchp0 = ';'; uchp0 ++; *uchp0 = '5'; uchp0 ++; *uchp0 = ';'; uchp0 ++; uch *x = (uch *) IMG_ANSI_COLOR[color]; // color *uchp0 = x[0]; uchp0 ++; *uchp0 = x[1]; uchp0 ++; if (color >= 84) {*uchp0 = x[2]; uchp0 ++;} *uchp0 = 'm'; uchp0 ++; } void term_init() {TERM_BACKGROUND = false; term_color(false, 0); term_color(true, 0);} char *asc_str() { if (ASC_PLAINTXT) { for (ii = 0, uchp0 = ASC_STK, uchp1 = IMG_CHR; ii < ASC_ROWS; ii ++, *uchp0 = '\n', uchp0 ++) for (jj = 0; jj < ASC_COLS; jj ++, uchp0 ++, uchp1 ++) *uchp0 = *uchp1 & 0x7f; uchp0[-1] = '\x00'; } else { bool background; uchp0 = ASC_STK; term_init(); for (ii = 0, uchp1 = IMG_CHR, uchp2 = IMG_COLOR; ii < ASC_ROWS; ii ++, term_reset(), *uchp0 = '\n', uchp0 ++, term_color(TERM_BACKGROUND ^ true, 0)) for (jj = 0; jj < ASC_COLS; jj ++, uchp1 ++, uchp2 ++) { assert (*uchp1); background = *uchp1 >> 7; if (TERM_BACKGROUND ^ background) {term_color(TERM_BACKGROUND, 0); TERM_BACKGROUND ^= true;} term_color(background, *uchp2); *uchp0 = *uchp1 & 0x7f; uchp0 ++; } uchp0[-11] = '\x00'; } return (char *) ASC_STK; } int asc_png(char *fpath) { for (ii = 0, uchp0 = IMG_CHR, i64p0 = IMG_64; ii < ASC_ROWS * ASC_COLS; ii ++, uchp0 ++, i64p0 ++) *i64p0 = ASC_FBMP[*uchp0 * 5]; img_64_to_img(); return png_write(fpath); } int test(char *fpath) { ASC_PLAINTXT = 0; if (img_read(fpath, 1) == ERROR) return 0; printf("rows x cols %d %d\n", IMG_ROWS, IMG_COLS); // asc_itp(4, 16); asc_itp(4, 16); asc_png("index_html/out.png"); // bmp_write("index_html/out.bmp"); // png_write("index_html/out.png"); // printf("%d\n", stderr); printf("bye\n"); return 0; }