FastLED 3.10.6
Loading...
Searching...
No Matches
oklab_q16.cpp.hpp
Go to the documentation of this file.
1// ok no header - implementation for fl/gfx/oklab_q16.h
2
3#include "fl/gfx/oklab_q16.h"
4
6
7namespace fl {
8
9namespace {
10
11// Ottosson's OKLab matrices, quantized to s16.16 round-to-nearest. The
12// float values are in the comments so a reader can check the quantization
13// without leaving the file; the inverses are the float inverses quantized,
14// not the quantized matrices inverted.
15//
16// Quantizing the coefficients costs nothing measurable: scoring the mapper
17// end-to-end with these integers rather than float64 coefficients moves the
18// worst error from 0.152 to 0.153 dE2000, against a budget of 0.5.
19
21constexpr i32 kLmsFromXyz[3][3] = {
22 { 53675, 23718, -8446}, // +0.8190224432, +0.3619062563, -0.1288737826
23 { 2162, 60902, 2369}, // +0.0329836672, +0.9292868469, +0.0361446682
24 { 3157, 17317, 41520}, // +0.0481771996, +0.2642395249, +0.6335478258
25};
26
28constexpr i32 kOklabFromLmsRoot[3][3] = {
29 { 13792, 52011, -267}, // +0.2104542553, +0.7936177850, -0.0040720468
30 { 129630, -159160, 29530}, // +1.9779984951, -2.4285922050, +0.4505937099
31 { 1698, 51300, -52997}, // +0.0259040371, +0.7827717662, -0.8086757660
32};
33
35constexpr i32 kLmsRootFromOklab[3][3] = {
36 { 65536, 25974, 14143}, // +0.9999999985, +0.3963377922, +0.2158037581
37 { 65536, -6918, -4185}, // +1.0000000089, -0.1055613423, -0.0638541748
38 { 65536, -5864, -84639}, // +1.0000000547, -0.0894841821, -1.2914855379
39};
40
42constexpr i32 kXyzFromLms[3][3] = {
43 { 80405, -36557, 18441}, // +1.2268798734, -0.5578149966, +0.2813910502
44 { -2659, 72895, -4700}, // -0.0405757626, +1.1122868294, -0.0717110667
45 { -5005, -27623, 104001}, // -0.0763729497, -0.4214933240, +1.5869240244
46};
47
53i32 clampOklabQ16(i32 value, i32 bound) FL_NO_EXCEPT {
54 if (value > bound) {
55 return bound;
56 }
57 if (value < -bound) {
58 return -bound;
59 }
60 return value;
61}
62
69i32 dotOklabRowQ16(const i32 (&row)[3], const i32 (&v)[3]) FL_NO_EXCEPT {
70 const i64 acc = static_cast<i64>(row[0]) * static_cast<i64>(v[0])
71 + static_cast<i64>(row[1]) * static_cast<i64>(v[1])
72 + static_cast<i64>(row[2]) * static_cast<i64>(v[2]);
73 return static_cast<i32>((acc + 32768) >> 16);
74}
75
76void dotOklabMatrixQ16(const i32 (&matrix)[3][3], const i32 (&v)[3],
77 i32 (&out)[3]) FL_NO_EXCEPT {
78 out[0] = dotOklabRowQ16(matrix[0], v);
79 out[1] = dotOklabRowQ16(matrix[1], v);
80 out[2] = dotOklabRowQ16(matrix[2], v);
81}
82
90 const i64 magnitude = raw < 0 ? -static_cast<i64>(raw) : static_cast<i64>(raw);
91 const i32 root = static_cast<i32>(
92 fl::icbrt64(static_cast<u64>(magnitude) << 32));
93 return raw < 0 ? -root : root;
94}
95
110constexpr i32 kOklabQ16MaxCubeRoot = 16 * 65536;
111
113 const i32 clamped = clampOklabQ16(raw, kOklabQ16MaxCubeRoot);
114 const i64 magnitude = clamped < 0 ? -static_cast<i64>(clamped)
115 : static_cast<i64>(clamped);
116 const i64 squared = (magnitude * magnitude + 32768) >> 16;
117 const i64 cubed = (squared * magnitude + 32768) >> 16;
118 return clamped < 0 ? -static_cast<i32>(cubed) : static_cast<i32>(cubed);
119}
120
121} // namespace
122
123void xyzToOklabQ16(const i32 (&xyz)[3], i32 (&out_lab)[3]) FL_NO_EXCEPT {
124 const i32 clamped[3] = {clampOklabQ16(xyz[0], kOklabQ16MaxMagnitude),
125 clampOklabQ16(xyz[1], kOklabQ16MaxMagnitude),
126 clampOklabQ16(xyz[2], kOklabQ16MaxMagnitude)};
127 i32 lms[3];
128 dotOklabMatrixQ16(kLmsFromXyz, clamped, lms);
129 const i32 root[3] = {
130 signedCbrtQ16(lms[0]), signedCbrtQ16(lms[1]), signedCbrtQ16(lms[2])};
131 dotOklabMatrixQ16(kOklabFromLmsRoot, root, out_lab);
132}
133
134void oklabToXyzQ16(const i32 (&lab)[3], i32 (&out_xyz)[3]) FL_NO_EXCEPT {
135 const i32 clamped[3] = {clampOklabQ16(lab[0], kOklabQ16MaxMagnitude),
136 clampOklabQ16(lab[1], kOklabQ16MaxMagnitude),
137 clampOklabQ16(lab[2], kOklabQ16MaxMagnitude)};
138 i32 root[3];
139 dotOklabMatrixQ16(kLmsRootFromOklab, clamped, root);
140 const i32 lms[3] = {
141 signedCubeQ16(root[0]), signedCubeQ16(root[1]), signedCubeQ16(root[2])};
142 dotOklabMatrixQ16(kXyzFromLms, lms, out_xyz);
143}
144
145} // namespace fl
constexpr i32 kOklabQ16MaxCubeRoot
Signed cube of an s16.16 raw value, rounding to nearest at each step.
constexpr i32 kLmsRootFromOklab[3][3]
OKLab -> cube-rooted LMS.
i32 clampOklabQ16(i32 value, i32 bound) FL_NO_EXCEPT
Clamp into a symmetric domain.
constexpr i32 kLmsFromXyz[3][3]
XYZ -> LMS.
constexpr i32 kXyzFromLms[3][3]
LMS -> XYZ.
void dotOklabMatrixQ16(const i32(&matrix)[3][3], const i32(&v)[3], i32(&out)[3]) FL_NO_EXCEPT
constexpr i32 kOklabFromLmsRoot[3][3]
Cube-rooted LMS -> OKLab.
i32 dotOklabRowQ16(const i32(&row)[3], const i32(&v)[3]) FL_NO_EXCEPT
One matrix row against a clamped s16.16 vector.
i32 signedCbrtQ16(i32 raw) FL_NO_EXCEPT
Signed cube root of an s16.16 raw value.
constexpr int type_rank< T >::value
constexpr i32 kOklabQ16MaxMagnitude
Largest magnitude either direction accepts, as an s16.16 raw value (64.0).
Definition oklab_q16.h:40
void oklabToXyzQ16(const i32(&lab)[3], i32(&out_xyz)[3]) FL_NO_EXCEPT
OKLab (s16.16) -> XYZ (s16.16).
void xyzToOklabQ16(const i32(&xyz)[3], i32(&out_lab)[3]) FL_NO_EXCEPT
XYZ (s16.16, D65-relative) -> OKLab (s16.16), L then a then b.
FL_OPTIMIZE_FUNCTION constexpr u32 icbrt64(u64 x) FL_NO_EXCEPT
Definition icbrt.h:45
InputGamut g FL_NO_EXCEPT
Definition rgbw.h:121
Base definition for an LED controller.
Definition crgb.hpp:179
fl::u64 u64
Definition stdint.h:220
fl::i64 i64
Definition stdint.h:221