cosmopolitan/examples/dinlerp.c

377 lines
11 KiB
C
Raw Normal View History

2020-06-15 14:18:57 +00:00
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "dsp/core/core.h"
#include "dsp/core/twixt8.h"
#include "dsp/scale/scale.h"
#include "dsp/tty/itoa8.h"
#include "dsp/tty/quant.h"
#include "libc/alg/arraylist2.internal.h"
2020-06-15 14:18:57 +00:00
#include "libc/calls/calls.h"
#include "libc/calls/ioctl.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/winsize.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/mem/mem.h"
#include "libc/runtime/gc.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/termios.h"
#include "libc/x/x.h"
#include "third_party/stb/stb_image.h"
/**
* Hat tips go out to Hornet, CRTC, DESiRE, Hans Petter Jansson, Guihua
* Cui, Ming Ronnier Luoand anyone else Mister Rogers would describe as
* the The Helpers Of This World who've helped us learn more about the
* cool yet exceedingly difficult things possible to do with graphics.
*/
#define DIN99 1
#define GAMMA 4.1
#define GAMMA_A .05
#define GAMMA_B 50.
#define GAMMA_C .055
#define COLORSPACE_SRC kSrgbD65ToXyzD50
#define COLORSPACE_TTY kSrgbD65ToXyzD50
#define SQR(X) ((X) * (X))
#define DEG(X) ((X)*M_PI / 180)
const double kXyzKappa = 24389 / 27.;
const double kXyzEpsilon = 216 / 24389.;
const double kSrgbD65ToXyzD50[3][3] = {
{.4360747, .3850649, .1430804},
{.2225045, .7168786, .0606169},
{.0139322, .0971045, .7141733},
};
const double kCieRgbEToXyz[3][3] = {
{.4887180, .3106803, .2006017},
{.1762044, .8129847, .0108109},
{.0000000, .0102048, .9897952},
};
const unsigned char kShadePct[] = {0300, 0200, 0100};
const unsigned short kShadeRune[] = {u'', u'', u''};
const unsigned char kAnsiTango[16][3] = {
{46, 52, 54}, {204, 0, 0}, {78, 154, 6}, {196, 160, 0},
{52, 101, 164}, {117, 80, 123}, {6, 152, 154}, {211, 215, 207},
{85, 87, 83}, {239, 41, 41}, {138, 226, 52}, {252, 233, 79},
{114, 159, 207}, {173, 127, 168}, {52, 226, 226}, {238, 238, 236},
};
const unsigned char kCga[16][3] = {
{0, 0, 0}, {170, 0, 0}, {0, 170, 0}, {170, 85, 0},
{0, 0, 170}, {170, 0, 170}, {0, 170, 170}, {170, 170, 170},
{85, 85, 85}, {255, 85, 85}, {85, 255, 85}, {255, 255, 85},
{85, 85, 255}, {255, 85, 255}, {85, 255, 255}, {255, 255, 255},
};
struct Blends {
size_t i, n;
struct Blend {
unsigned char color[4];
unsigned char bg, fg;
unsigned short rune;
} * p;
} blends;
static double Gamma(double x) {
if (x < GAMMA_A) return x / GAMMA_B;
return pow(1 / (1 + GAMMA_C) * (x + GAMMA_C), GAMMA);
}
static void *LoadRgb(double rgb[3], const unsigned char pix[3]) {
rgb[0] = Gamma(1. / 255 * pix[0]);
rgb[1] = Gamma(1. / 255 * pix[1]);
rgb[2] = Gamma(1. / 255 * pix[2]);
return rgb;
}
static double Lab(double x) {
return x > kXyzEpsilon ? cbrtf(x) : (kXyzKappa * x + 16) / 116;
}
static void XyzToLab(double lab[3], const double d50xyz[3]) {
double Y, L, a, b;
Y = Lab(d50xyz[1]);
L = 116 * Y - 16;
a = 500 * (Lab(d50xyz[0]) - Y);
b = 200 * (Y - Lab(d50xyz[2]));
lab[0] = L, lab[1] = a, lab[2] = b;
}
static void XyzToDin99d(unsigned char din99d[3], const double d50xyz[3]) {
double e, f, G, C, h, xyz[3], lab[3];
memcpy(xyz, d50xyz, sizeof(xyz));
xyz[0] = 1.12f * xyz[0] - .12f * xyz[2];
XyzToLab(lab, xyz);
e = (+lab[1] * cosf(DEG(50)) + lab[2] * sinf(DEG(50)));
f = (-lab[1] * sinf(DEG(50)) + lab[2] + cosf(DEG(50))) * 1.14;
G = sqrtf(SQR(e) + SQR(f));
C = logf(1 + .06 * G) * 22.5f;
h = atan2f(f, e) + DEG(50);
h = fmodf(h, DEG(360));
if (h < 0) h += DEG(360);
din99d[0] = MIN(255, MAX(0, 325.22f * logf(1 + .0036 * lab[0]) * 2.5f));
din99d[1] = MIN(255, MAX(0, C * cos(h) * 2.5f + 128));
din99d[2] = MIN(255, MAX(0, C * sin(h) * 2.5f + 128));
}
static void AddColor(int bg, int fg, int rune, int r, int g, int b, int x) {
int i;
if (blends.i) {
for (i = blends.i; --i;) {
if (blends.p[i].color[0] == r && blends.p[i].color[1] == g &&
blends.p[i].color[2] == b) {
return;
}
}
}
CHECK_NE(
-1,
APPEND(&blends.p, &blends.i, &blends.n,
(&(struct Blend){
.bg = bg, .fg = fg, .rune = rune, .color = {r, g, b, x}})));
}
static void AddBlend(int bgxterm, int fgxterm, int rune, int shade,
const unsigned char bgrgb[4],
const unsigned char fgrgb[4]) {
AddColor(bgxterm, fgxterm, rune, twixt8(bgrgb[0], fgrgb[0], shade),
twixt8(bgrgb[1], fgrgb[1], shade), twixt8(bgrgb[2], fgrgb[2], shade),
0);
}
static void MakeBlends(const unsigned char palette[16][3]) {
int r, i, j, k;
double rgb[3], xyz[3];
unsigned char tab[256][4];
unsigned char cube[6] = {0, 0137, 0207, 0257, 0327, 0377};
unsigned char seqs[2][2] = {{16, 255}};
for (i = 0; i < 16; ++i) {
tab[i][0] = palette[i][0];
tab[i][1] = palette[i][1];
tab[i][2] = palette[i][2];
tab[i][3] = i;
}
for (i = 16; i < 232; ++i) {
tab[i][0] = cube[((i - 020) / 044) % 6];
tab[i][1] = cube[((i - 020) / 6) % 6];
tab[i][2] = cube[(i - 020) % 6];
tab[i][3] = i;
}
for (i = 232; i < 256; ++i) {
tab[i][0] = 8 + (i - 232) * 10;
tab[i][1] = 8 + (i - 232) * 10;
tab[i][2] = 8 + (i - 232) * 10;
tab[i][3] = i;
}
#if DIN99
for (i = 0; i < 256; ++i) {
LoadRgb(rgb, tab[i]);
vmatmul3(xyz, rgb, COLORSPACE_TTY);
XyzToDin99d(tab[i], xyz);
}
#endif
for (r = 0; r < ARRAYLEN(seqs); ++r) {
for (i = seqs[r][0]; i <= seqs[r][1]; ++i) {
for (j = seqs[r][0]; j <= seqs[r][1]; ++j) {
if (i == j) {
AddColor(i, 0, 0, tab[i][0], tab[i][1], tab[i][2], 0);
} else {
for (k = 0; k < ARRAYLEN(kShadeRune); ++k) {
AddBlend(i, j, kShadeRune[k], kShadePct[k], tab[i], tab[j]);
AddBlend(j, i, kShadeRune[k], kShadePct[k], tab[j], tab[i]);
}
}
}
}
}
}
static int PickColor(unsigned char color[3]) {
int i, best, least, dist;
best = -1;
least = INT_MAX;
for (i = 0; i < blends.i; ++i) {
dist = SQR(blends.p[i].color[0] - color[0]) +
SQR(blends.p[i].color[1] - color[1]) +
SQR(blends.p[i].color[2] - color[2]);
if (dist < least) {
least = dist;
best = i;
}
}
return best;
}
static void SetBg(int x) {
if (0 <= x && x < 16) {
if (x < 8) {
x += 40;
} else {
x -= 8;
x *= 100;
}
printf("\e[%dm", x);
} else {
/* assert(false); */
printf("\e[48;5;%dm", x);
}
}
static void SetFg(int x) {
if (0 <= x && x < 16) {
if (x < 8) {
x += 30;
} else {
x -= 8;
x *= 90;
}
printf("\e[%dm", x);
} else {
/* assert(false); */
printf("\e[38;5;%dm", x);
}
}
static void PrintImage(long yn, long xn, unsigned char src[4][yn][xn]) {
int y, x, c;
double rgb[3], xyz[3];
unsigned char din99d[3];
for (y = 0; y < yn; ++y) {
printf("\n");
for (x = 0; x < xn; ++x) {
din99d[0] = src[0][y][x];
din99d[1] = src[1][y][x];
din99d[2] = src[2][y][x];
#if DIN99
LoadRgb(rgb, din99d);
vmatmul3(xyz, rgb, COLORSPACE_SRC);
XyzToDin99d(din99d, xyz);
#endif
c = PickColor(din99d);
if (blends.p[c].rune) {
SetBg(blends.p[c].bg);
SetFg(blends.p[c].fg);
printf("%hc", blends.p[c].rune);
} else {
SetBg(blends.p[c].bg);
printf(" ");
}
}
printf("\e[0m");
}
printf("\r");
}
static void *Deinterlace(long dcn, long dyn, long dxn,
unsigned char dst[dcn][dyn][dxn], long syw, long sxw,
long scw, const unsigned char src[syw][sxw][scw],
long syn, long sxn, long sy0, long sx0, long sc0) {
long y, x, c;
for (y = 0; y < dyn; ++y) {
for (x = 0; x < dxn; ++x) {
for (c = 0; c < dcn; ++c) {
dst[c][y][x] = src[sy0 + y][sx0 + x][sc0 + c];
}
}
}
return dst;
}
static void GetTermSize(int out_rows[1], int out_cols[1]) {
struct winsize ws;
ws.ws_row = 24;
ws.ws_col = 80;
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) {
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
}
out_rows[0] = ws.ws_row;
out_cols[0] = ws.ws_col;
}
static void ProcessImage(long syn, long sxn, unsigned char img[syn][sxn][4]) {
int dyn, dxn;
double ry, rx;
GetTermSize(&dyn, &dxn);
ry = syn, rx = sxn;
ry /= dyn, rx /= dxn;
PrintImage(
dyn, dxn,
EzGyarados(4, dyn, dxn, gc(xmemalign(32, dyn * dxn * 4)), 4, syn, sxn,
Deinterlace(4, syn, sxn, gc(xmemalign(32, syn * sxn * 4)), syn,
sxn, 4, img, syn, sxn, 0, 0, 0),
0, 4, dyn, dxn, syn, sxn, ry, rx, 0, 0));
}
static void WithImageFile(const char *path,
void fn(long yn, long xn,
unsigned char src[yn][xn][4])) {
struct stat st;
void *map, *data;
int fd, yn, xn, cn;
CHECK_NE(-1, (fd = open(path, O_RDONLY)), "%s", path);
CHECK_NE(-1, fstat(fd, &st));
CHECK_GT(st.st_size, 0);
CHECK_LE(st.st_size, INT_MAX);
CHECK_NE(MAP_FAILED,
(map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)));
CHECK_NOTNULL(
(data = stbi_load_from_memory(map, st.st_size, &xn, &yn, NULL, 4)), "%s",
path);
CHECK_NE(-1, munmap(map, st.st_size));
CHECK_NE(-1, close(fd));
fn(yn, xn, data);
free(data);
}
static void PrintData(void) {
int i;
for (i = 0; i < blends.i; ++i) {
printf("%3d %3d %3d %3d %3d %d\n", blends.p[i].color[0],
blends.p[i].color[1], blends.p[i].color[2], blends.p[i].bg,
blends.p[i].fg, blends.p[i].rune);
}
}
void *OpenRgbMap(const char *path) {
int fd;
void *map;
size_t size;
size = 256 * 256 * 256 * 3;
CHECK_NE(-1, (fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644)));
CHECK_NE(-1, ftruncate(fd, size));
CHECK_NE(MAP_FAILED,
(map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)));
CHECK_NE(-1, close(fd));
return map;
}
int main(int argc, char *argv[]) {
int i;
MakeBlends(kCga);
for (i = 1; i < argc; ++i) {
WithImageFile(argv[i], ProcessImage);
}
return 0;
}