333 lines
10 KiB
C
333 lines
10 KiB
C
/*-*- 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 │
|
|
│ │
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#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);
|
|
}
|