/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright 2020 Justine Alexandra Roberts Tunney │ │ │ │ This program is free software; you can redistribute it and/or modify │ │ it under the terms of the GNU General Public License as published by │ │ the Free Software Foundation; version 2 of the License. │ │ │ │ This program is distributed in the hope that it will be useful, but │ │ WITHOUT ANY WARRANTY; without even the implied warranty of │ │ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ │ General Public License for more details. │ │ │ │ You should have received a copy of the GNU General Public License │ │ along with this program; if not, write to the Free Software │ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/log/check.h" #include "libc/macros.h" #include "libc/rand/rand.h" #include "libc/runtime/gc.h" #include "libc/str/str.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" #include "libc/x/x.h" #include "tool/viz/lib/ycbcr.h" #define C1331(A, B, C, D) \ ({ \ unsigned short Ax, Bx; \ unsigned char Al, Bl, Cl, Dl; \ Al = (A); \ Bl = (B); \ Cl = (C); \ Dl = (D); \ Bx = Bl; \ Bx += Cl; \ Bx *= 3; \ Ax = Al; \ Ax += Dl; \ Ax += Bx; \ Ax += 4; \ Ax >>= 3; \ Al = Ax; \ Al; \ }) TEST(C1331, test) { EXPECT_EQ(0, C1331(0, 0, 0, 0)); EXPECT_EQ(255, C1331(255, 255, 255, 255)); EXPECT_EQ(79, C1331(33, 100, 77, 69)); EXPECT_EQ(80, C1331(39, 100, 77, 69)); } #define C161(A, B, C) \ ({ \ short Dx; \ unsigned char Dl; \ unsigned char Al, Bl, Cl; \ unsigned short Ax, Bx, Cx; \ Al = (A); \ Bl = (B); \ Cl = (C); \ \ Bx = Bl; \ Bx += 3; \ Bx >>= 1; \ Bx += Bl; \ \ Ax = Al; \ Ax += 4; \ Ax >>= 2; \ \ Cx = Cl; \ Cx += 4; \ Cx >>= 2; \ \ Dx = Bx; \ Dx -= Ax; \ Dx -= Cx; \ \ Dx = MAX(0, Dx); \ Dx = MIN(255, Dx); \ Dl = Dx; \ Dl; \ }) TEST(C161, test) { EXPECT_EQ(0, C161(0, 0, 0)); EXPECT_EQ(255, C161(255, 255, 255)); EXPECT_EQ(124, C161(33, 100, 69)); /* 124.50 = 3/2.*100 -1/4.*33 -1/4.*69 */ EXPECT_EQ(124, C161(33, 100, 70)); /* 124.25 = 3/2.*100 -1/4.*33 -1/4.*70 */ EXPECT_EQ(126, C161(33, 101, 69)); /* 126.00 = 3/2.*101 -1/4.*33 -1/4.*69 */ } #define C121(A, B, C) \ ({ \ short Dx; \ unsigned char Dl; \ unsigned char Al, Bl, Cl; \ unsigned short Ax, Bx, Cx; \ Al = (A); \ Bl = (B); \ Cl = (C); \ \ Bx = Bl; \ Bx += 3; \ Bx >>= 1; \ Bx += Bl; \ \ Ax = Al; \ Ax += 4; \ Ax >>= 2; \ \ Cx = Cl; \ Cx += 4; \ Cx >>= 2; \ \ Dx = Bx; \ Dx -= Ax; \ Dx -= Cx; \ \ Dx = MAX(0, Dx); \ Dx = MIN(255, Dx); \ Dl = Dx; \ Dl; \ }) TEST(C121, test) { EXPECT_EQ(0, C161(0, 0, 0)); EXPECT_EQ(255, C161(255, 255, 255)); EXPECT_EQ(124, C161(33, 100, 69)); /* 124.50 = 3/2.*100 -1/4.*33 -1/4.*69 */ EXPECT_EQ(124, C161(33, 100, 70)); /* 124.25 = 3/2.*100 -1/4.*33 -1/4.*70 */ EXPECT_EQ(126, C161(33, 101, 69)); /* 126.00 = 3/2.*101 -1/4.*33 -1/4.*69 */ } #define BLERP(A, B, P) \ ({ \ unsigned char Al, Bl, Cl, Dl; \ unsigned short Bx; \ Al = (A); \ Bl = (B); \ Cl = MAX(Al, Bl); \ Al = MIN(Al, Bl); \ Dl = Cl - Al; \ Bl = (P); \ Bx = Bl; \ Bx *= Dl; \ Bx += 255; \ Bx >>= 8; \ Bx += Al; \ Al = Bx; \ Al; \ }) TEST(BLERP, test) { EXPECT_EQ(0, BLERP(0, 0, 128)); EXPECT_EQ(255, BLERP(255, 255, 128)); EXPECT_EQ(64, BLERP(0, 128, 128)); EXPECT_EQ(64, BLERP(128, 0, 128)); EXPECT_EQ(32, BLERP(0, 128, 64)); EXPECT_EQ(32, BLERP(128, 0, 64)); EXPECT_EQ(8, BLERP(0, 128, 16)); EXPECT_EQ(8, BLERP(128, 0, 16)); EXPECT_EQ(0, BLERP(0, 128, 0)); EXPECT_EQ(0, BLERP(128, 0, 0)); EXPECT_EQ(128, BLERP(0, 128, 255)); EXPECT_EQ(128, BLERP(128, 0, 255)); } #define MIX(A, B) \ ({ \ short Ax, Bx; \ Ax = (A); \ Bx = (B); \ Ax += Bx; \ Ax += 1; \ Ax /= 2; \ Ax; \ }) TEST(MIX, test) { EXPECT_EQ(0, MIX(0, 0)); EXPECT_EQ(255, MIX(255, 255)); EXPECT_EQ(64, MIX(0, 128)); EXPECT_EQ(64, MIX(128, 0)); EXPECT_EQ(127, MIX(0, 254)); } void ExpandLuminosityRange(unsigned n, unsigned char *Y) { unsigned i, j; unsigned char b[16]; unsigned short s[16]; CHECK_ALIGNED(16, Y); for (i = 0; i < n; i += 16) { memcpy(b, Y + i, 16); for (j = 0; j < 16; ++j) b[j] = MAX(0, b[j] - 16); for (j = 0; j < 16; ++j) s[j] = b[j]; for (j = 0; j < 16; ++j) s[j] *= 150; for (j = 0; j < 16; ++j) s[j] /= 128; for (j = 0; j < 16; ++j) s[j] = MIN(255, s[j]); for (j = 0; j < 16; ++j) b[j] = s[j]; memcpy(Y + i, b, 16); } } TEST(ExpandLuminosityRange, test) { unsigned char Y[32]; Y[0] = 0; ExpandLuminosityRange(16, Y); EXPECT_EQ(0, Y[0]); Y[0] = 16; ExpandLuminosityRange(16, Y); EXPECT_EQ(0, Y[0]); Y[0] = 17; ExpandLuminosityRange(16, Y); EXPECT_EQ(1, Y[0]); Y[0] = 128; ExpandLuminosityRange(16, Y); EXPECT_EQ(131, Y[0]); Y[0] = 233; ExpandLuminosityRange(16, Y); EXPECT_EQ(254, Y[0]); Y[0] = 235; ExpandLuminosityRange(16, Y); EXPECT_EQ(255, Y[0]); Y[0] = 255; ExpandLuminosityRange(16, Y); EXPECT_EQ(255, Y[0]); } #if 1 void NtscItu601YCbCrToStandardRgb(unsigned n, unsigned char *restrict Y, unsigned char *restrict Cb, unsigned char *restrict Cr) { unsigned i; short r, g, b; unsigned char y; for (i = 0; i < n; ++i) { y = MIN(255, (MIN(235, MAX(16, Y[i])) - 16) * 150 / 128); r = y + ((Cr[i] + ((Cr[i] * 103) / 256)) - 179); g = y - (((Cb[i] * 88) / 256) - 44 + ((Cr[i] * 183) / 256) - 91); b = y + ((Cb[i] + ((Cb[i] * 198) / 256)) - 227); Y[i] = MIN(255, MAX(0, r)); Cb[i] = MIN(255, MAX(0, g)); Cr[i] = MIN(255, MAX(0, b)); } } #else void NtscItu601YCbCrToStandardRgb(size_t n, unsigned char *restrict Y, unsigned char *restrict Cb, unsigned char *restrict Cr) { unsigned i; short y, gb, gr, r, g, b; unsigned char gs, cb, cr; unsigned short bw, ky, kr, kb, kgb, kgr; for (i = 0; i < n; ++i) { y = Y[i]; cb = Cb[i]; cr = Cr[i]; y -= 16; /* luminance (expand tv range [16,235] for pc [0,255]) */ y = MAX(0, y); y = MIN(234 - 16, y); ky = y; ky *= 150; ky /= 128; gs = ky; kr = cr; /* red */ kr *= 103; kr /= 256; kr += cr; r = kr; r -= 179; r += gs; kb = cb; /* blue */ kb *= 198; kb /= 256; kb += cb; b = kb; b -= 227; b += gs; kgb = cb; /* green */ kgb *= 88; kgb /= 256; gb = kgb; gb -= 44; kgr = cr; kgr *= 183; kgr /= 256; gr = kgr; gr -= 91; g = gs; g -= gr; g -= gb; r = MAX(0, r); /* clamp */ g = MAX(0, g); b = MAX(0, b); r = MIN(255, r); g = MIN(255, g); b = MIN(255, b); Y[i] = r; Cb[i] = g; Cr[i] = b; } } #endif TEST(NtscItu601YCbCrToStandardRgb, testWhite) { unsigned char a, b, c; a = 234; b = 128; c = 128; NtscItu601YCbCrToStandardRgb(1, &a, &b, &c); EXPECT_EQ(255, a); EXPECT_EQ(255, b); EXPECT_EQ(255, c); a = 235; b = 128; c = 128; NtscItu601YCbCrToStandardRgb(1, &a, &b, &c); EXPECT_EQ(255, a); EXPECT_EQ(255, b); EXPECT_EQ(255, c); a = 255; b = 128; c = 128; NtscItu601YCbCrToStandardRgb(1, &a, &b, &c); EXPECT_EQ(255, a); EXPECT_EQ(255, b); EXPECT_EQ(255, c); }