Skip to content

Commit 571fc09

Browse files
committed
specs: Simple mixbox implementation.
1 parent 5ec8417 commit 571fc09

File tree

6 files changed

+203
-3
lines changed

6 files changed

+203
-3
lines changed

src/libs/karm-gfx/color.h

+6-3
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@ struct Color {
2626
return {red, green, blue, alpha};
2727
}
2828

29-
ALWAYS_INLINE constexpr Color() : red(0), green(0), blue(0), alpha(0) {}
29+
ALWAYS_INLINE constexpr Color()
30+
: red(0), green(0), blue(0), alpha(0) {}
3031

31-
ALWAYS_INLINE constexpr Color(u8 red, u8 green, u8 blue, u8 alpha = 255) : red(red), green(green), blue(blue), alpha(alpha) {}
32+
ALWAYS_INLINE constexpr Color(u8 red, u8 green, u8 blue, u8 alpha = 255)
33+
: red(red), green(green), blue(blue), alpha(alpha) {}
3234

33-
ALWAYS_INLINE constexpr Color(Math::Vec4u v) : red(v.x), green(v.y), blue(v.z), alpha(v.w) {}
35+
ALWAYS_INLINE constexpr Color(Math::Vec4u v)
36+
: red(v.x), green(v.y), blue(v.z), alpha(v.w) {}
3437

3538
ALWAYS_INLINE constexpr Color blendOver(Color const background) const {
3639
if (alpha == 0xff) {

src/specs/mixbox/lut.c

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[[gnu::used]] unsigned char const _Mixbox_LUT[] = {
2+
#include "lut.inc"
3+
};
4+
5+
[[gnu::used]] unsigned long long int const _Mixbox_LUT_size = sizeof(_Mixbox_LUT);

src/specs/mixbox/lut.inc

+1
Large diffs are not rendered by default.

src/specs/mixbox/manifest.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"$schema": "https://schemas.cute.engineering/stable/cutekit.manifest.component.v1",
3+
"id": "mixbox-spec",
4+
"type": "lib",
5+
"description": "Mixbox is a library for natural color mixing based on real pigments",
6+
"requires": [
7+
"karm-io"
8+
]
9+
}

src/specs/mixbox/mixbox.cpp

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#include "mixbox.h"
2+
3+
extern "C" unsigned char const _Mixbox_LUT[];
4+
extern "C" unsigned long long int const _Mixbox_LUT_size;
5+
6+
namespace Mixbox {
7+
8+
Bytes const LUT = Bytes(_Mixbox_LUT, _Mixbox_LUT_size);
9+
10+
static f64 srgb2linear(f64 srgb) {
11+
if (srgb <= 0.04045f)
12+
return srgb / 12.92f;
13+
return ::pow((srgb + 0.055f) / 1.055f, 2.4f);
14+
}
15+
16+
static Math::Vec3f srgb2linear(Gfx::Color color) {
17+
return {
18+
srgb2linear(color.red / 255.0),
19+
srgb2linear(color.green / 255.0),
20+
srgb2linear(color.blue / 255.0),
21+
};
22+
}
23+
24+
static f64 linear2srgb(f64 linear) {
25+
if (linear <= 0.0031308f)
26+
return linear * 12.92f;
27+
return 1.055f * ::pow(linear, 1.0f / 2.4f) - 0.055f;
28+
}
29+
30+
static Gfx::Color linear2srgb(Math::Vec3f linear) {
31+
return {
32+
static_cast<u8>(linear2srgb(linear[0]) * 255.0),
33+
static_cast<u8>(linear2srgb(linear[1]) * 255.0),
34+
static_cast<u8>(linear2srgb(linear[2]) * 255.0),
35+
255,
36+
};
37+
}
38+
39+
static Math::Vec3f evalPolynomial(f64 c0, f64 c1, f64 c2, f64 c3) {
40+
auto c00 = c0 * c0;
41+
auto c11 = c1 * c1;
42+
auto c22 = c2 * c2;
43+
auto c33 = c3 * c3;
44+
auto c01 = c0 * c1;
45+
auto c02 = c0 * c2;
46+
auto c12 = c1 * c2;
47+
48+
auto r = 0.0, g = 0.0, b = 0.0;
49+
50+
// clang-format off
51+
auto w00 = c0 * c00; r += 0.07717053*w00; g += 0.02826978*w00; b += 0.24832992*w00;
52+
auto w01 = c1 * c11; r += 0.95912302*w01; g += 0.80256528*w01; b += 0.03561839*w01;
53+
auto w02 = c2 * c22; r += 0.74683774*w02; g += 0.04868586*w02; b += 0.00000000*w02;
54+
auto w03 = c3 * c33; r += 0.99518138*w03; g += 0.99978149*w03; b += 0.99704802*w03;
55+
auto w04 = c00 * c1; r += 0.04819146*w04; g += 0.83363781*w04; b += 0.32515377*w04;
56+
auto w05 = c01 * c1; r += -0.68146950*w05; g += 1.46107803*w05; b += 1.06980936*w05;
57+
auto w06 = c00 * c2; r += 0.27058419*w06; g += -0.15324870*w06; b += 1.98735057*w06;
58+
auto w07 = c02 * c2; r += 0.80478189*w07; g += 0.67093710*w07; b += 0.18424500*w07;
59+
auto w08 = c00 * c3; r += -0.35031003*w08; g += 1.37855826*w08; b += 3.68865000*w08;
60+
auto w09 = c0 * c33; r += 1.05128046*w09; g += 1.97815239*w09; b += 2.82989073*w09;
61+
auto w10 = c11 * c2; r += 3.21607125*w10; g += 0.81270228*w10; b += 1.03384539*w10;
62+
auto w11 = c1 * c22; r += 2.78893374*w11; g += 0.41565549*w11; b += -0.04487295*w11;
63+
auto w12 = c11 * c3; r += 3.02162577*w12; g += 2.55374103*w12; b += 0.32766114*w12;
64+
auto w13 = c1 * c33; r += 2.95124691*w13; g += 2.81201112*w13; b += 1.17578442*w13;
65+
auto w14 = c22 * c3; r += 2.82677043*w14; g += 0.79933038*w14; b += 1.81715262*w14;
66+
auto w15 = c2 * c33; r += 2.99691099*w15; g += 1.22593053*w15; b += 1.80653661*w15;
67+
auto w16 = c01 * c2; r += 1.87394106*w16; g += 2.05027182*w16; b += -0.29835996*w16;
68+
auto w17 = c01 * c3; r += 2.56609566*w17; g += 7.03428198*w17; b += 0.62575374*w17;
69+
auto w18 = c02 * c3; r += 4.08329484*w18; g += -1.40408358*w18; b += 2.14995522*w18;
70+
auto w19 = c12 * c3; r += 6.00078678*w19; g += 2.55552042*w19; b += 1.90739502*w19;
71+
// clang-format on
72+
73+
return {r, g, b};
74+
}
75+
76+
Latent unmix(Math::Vec3f rgb) {
77+
auto r01 = clamp01(rgb[0]);
78+
auto g01 = clamp01(rgb[1]);
79+
auto b01 = clamp01(rgb[2]);
80+
81+
auto x = r01 * 63.0;
82+
auto y = g01 * 63.0;
83+
auto z = b01 * 63.0;
84+
85+
isize ix = x;
86+
isize iy = y;
87+
isize iz = z;
88+
89+
auto tx = x - ((f64)ix);
90+
auto ty = y - ((f64)iy);
91+
auto tz = z - ((f64)iz);
92+
93+
auto lut = next(LUT, (((ix + iy * 64 + iz * 64 * 64) & 0x3FFFF) * 3));
94+
95+
auto c0 = 0.0;
96+
auto c1 = 0.0;
97+
auto c2 = 0.0;
98+
99+
// clang-format off
100+
auto w0 = (1.0 - tx) * (1.0 - ty) * (1.0 - tz); c0 += w0 * lut[ 192]; c1 += w0 * lut[ 193]; c2 += w0 * lut[ 194];
101+
auto w1 = ( tx) * (1.0 - ty) * (1.0 - tz); c0 += w1 * lut[ 195]; c1 += w1 * lut[ 196]; c2 += w1 * lut[ 197];
102+
auto w2 = (1.0 - tx) * ( ty) * (1.0 - tz); c0 += w2 * lut[ 384]; c1 += w2 * lut[ 385]; c2 += w2 * lut[ 386];
103+
auto w3 = ( tx) * ( ty) * (1.0 - tz); c0 += w3 * lut[ 387]; c1 += w3 * lut[ 388]; c2 += w3 * lut[ 389];
104+
auto w4 = (1.0 - tx) * (1.0 - ty) * ( tz); c0 += w4 * lut[12480]; c1 += w4 * lut[12481]; c2 += w4 * lut[12482];
105+
auto w5 = ( tx) * (1.0 - ty) * ( tz); c0 += w5 * lut[12483]; c1 += w5 * lut[12484]; c2 += w5 * lut[12485];
106+
auto w6 = (1.0 - tx) * ( ty) * ( tz); c0 += w6 * lut[12672]; c1 += w6 * lut[12673]; c2 += w6 * lut[12674];
107+
auto w7 = ( tx) * ( ty) * ( tz); c0 += w7 * lut[12675]; c1 += w7 * lut[12676]; c2 += w7 * lut[12677];
108+
// clang-format on
109+
110+
c0 *= 1.0 / 255.0;
111+
c1 *= 1.0 / 255.0;
112+
c2 *= 1.0 / 255.0;
113+
114+
auto c3 = 1.0 - (c0 + c1 + c2);
115+
116+
auto mixrgb = evalPolynomial(c0, c1, c2, c3);
117+
118+
return {c0, c1, c2, c3,
119+
r01 - mixrgb[0], g01 - mixrgb[1], b01 - mixrgb[2]};
120+
}
121+
122+
Math::Vec3f mix(Latent latent) {
123+
auto rgb = evalPolynomial(
124+
latent[0], latent[1], latent[2], latent[3]);
125+
126+
return {
127+
clamp01(rgb[0] + latent[4]),
128+
clamp01(rgb[1] + latent[5]),
129+
clamp01(rgb[2] + latent[6]),
130+
};
131+
}
132+
133+
Latent unmixColor(Gfx::Color color) {
134+
return unmix(srgb2linear(color));
135+
}
136+
137+
Gfx::Color mixColor(Latent latent) {
138+
return linear2srgb(mix(latent));
139+
};
140+
141+
Math::Vec3f lerp(Math::Vec3f a, Math::Vec3f b, f64 t) {
142+
auto la = unmix(a);
143+
auto lb = unmix(b);
144+
145+
for (usize i = 0; i < LATENT_SIZE; i++) {
146+
la[i] = ::lerp(la[i], lb[i], t);
147+
}
148+
149+
return mix(la);
150+
}
151+
152+
Gfx::Color lerpColor(Gfx::Color a, Gfx::Color b, f64 t) {
153+
return linear2srgb(
154+
lerp(
155+
srgb2linear(a),
156+
srgb2linear(b), t));
157+
}
158+
159+
} // namespace Mixbox

src/specs/mixbox/mixbox.h

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#pragma once
2+
3+
#include <karm-gfx/color.h>
4+
5+
namespace Mixbox {
6+
7+
static constexpr auto LATENT_SIZE = 7;
8+
9+
using Latent = Array<f64, LATENT_SIZE>;
10+
11+
Latent unmix(Math::Vec3f rgb);
12+
13+
Math::Vec3f mix(Latent latent);
14+
15+
Latent unmixColor(Gfx::Color color);
16+
17+
Gfx::Color mixColor(Latent latent);
18+
19+
Math::Vec3f lerp(Math::Vec3f a, Math::Vec3f b, f64 t);
20+
21+
Gfx::Color lerpColor(Gfx::Color a, Gfx::Color b, f64 t);
22+
23+
} // namespace Mixbox

0 commit comments

Comments
 (0)