FastLED 3.10.6
Loading...
Searching...
No Matches
gamut_map.h
Go to the documentation of this file.
1#pragma once
2
3// Gamut mapping onto the device hull (P7, #4041).
4//
5// `docs/color-gamut-algorithm-selection.md` selects OKLCh chroma compression
6// and rules out the cheaper alternatives: clipping and max-normalization
7// both land about 20 dE2000 from the reference against a budget of 0.5, and
8// no amount of clamping substitutes for the objective.
9//
10// The per-pixel path here is fully bounded and contains no iterative solver
11// in the A3/B11 sense, no division, no float, and no trigonometry:
12//
13// 1. one forward OKLab transform (three cube roots),
14// 2. one comparison against a precomputed device constant for lightness,
15// 3. eight halvings, each an inverse OKLab transform and one solve.
16//
17// Chroma is compressed by scaling (a, b) toward zero rather than by going
18// polar. Since a = C cos h and b = C sin h, scaling both by one factor is
19// exactly scaling C at fixed hue -- so hue is preserved by construction and
20// atan2, hypot, cos and sin never appear.
21
22#include "fl/gfx/device_solve.h"
24#include "fl/stl/int.h"
25#include "fl/stl/noexcept.h"
26
27namespace fl {
28
40constexpr int kGamutMapHalvings = 8;
41
60
87
95 const i32 (&white_xyz)[3],
98
103 const EmitterSolveMatrixQ16& solve, const i32 (&white_xyz)[3],
105
112void mapAndAllocateRgbwQ16(const GamutMapRgbwQ16& map, const i32 (&xyz)[3],
113 i32 (&drives)[4]) FL_NO_EXCEPT;
114
134
142 const i32 (&white1_xyz)[3],
143 const i32 (&white2_xyz)[3],
146
151 const EmitterSolveMatrixQ16& solve, const i32 (&white1_xyz)[3],
152 const i32 (&white2_xyz)[3], WhiteAllocationPolicy policy,
154
162void mapAndAllocateRgbwwQ16(const GamutMapRgbwwQ16& map, const i32 (&xyz)[3],
163 i32 (&drives)[5]) FL_NO_EXCEPT;
164
170
175
181void mapAndSolveDrivesQ16(const GamutMapQ16& map, const i32 (&xyz)[3],
182 i32 (&drives)[3]) FL_NO_EXCEPT;
183
184} // namespace fl
bool buildGamutMapRgbwFromSolveQ16(const colorimetric_response::EmitterProfile &profile, const EmitterSolveMatrixQ16 &solve, const i32(&white_xyz)[3], WhiteAllocationPolicy policy, GamutMapRgbwQ16 *out) FL_NO_EXCEPT
Bind a four-emitter map from an effective RGB solve and a white column already adapted into the same ...
bool buildGamutMapQ16(const colorimetric_response::EmitterProfile &profile, GamutMapQ16 *out) FL_NO_EXCEPT
Derive the mapper for a three-emitter profile.
void mapAndAllocateRgbwwQ16(const GamutMapRgbwwQ16 &map, const i32(&xyz)[3], i32(&drives)[5]) FL_NO_EXCEPT
One pixel: XYZ in s16.16 to five in-gamut drives – red, green, blue, white1, white2.
MapRedBlackTree< Key, T, Compare, fl::allocator_slab< char > > map
Definition map.h:283
constexpr int kGamutMapHalvings
Number of chroma halvings the mapper runs, fixed at compile time.
Definition gamut_map.h:40
void mapAndSolveDrivesQ16(const GamutMapQ16 &map, const i32(&xyz)[3], i32(&drives)[3]) FL_NO_EXCEPT
One pixel: XYZ in s16.16 to in-gamut emitter drives in s16.16.
WhiteAllocationPolicy
Which end of the feasible white interval to take (C3).
bool buildGamutMapRgbwwQ16(const colorimetric_response::EmitterProfile &profile, const i32(&white1_xyz)[3], const i32(&white2_xyz)[3], WhiteAllocationPolicy policy, GamutMapRgbwwQ16 *out) FL_NO_EXCEPT
Derive the mapper for a three-primary profile plus two white emitters.
InputGamut g FL_NO_EXCEPT
Definition rgbw.h:121
bool buildGamutMapFromSolveQ16(const EmitterSolveMatrixQ16 &solve, GamutMapQ16 *out) FL_NO_EXCEPT
Build a mapper from a bind-time solve already transformed into D65 working coordinates.
bool buildGamutMapRgbwQ16(const colorimetric_response::EmitterProfile &profile, const i32(&white_xyz)[3], WhiteAllocationPolicy policy, GamutMapRgbwQ16 *out) FL_NO_EXCEPT
Derive the mapper for a three-primary profile plus one white emitter.
void mapAndAllocateRgbwQ16(const GamutMapRgbwQ16 &map, const i32(&xyz)[3], i32(&drives)[4]) FL_NO_EXCEPT
One pixel: XYZ in s16.16 to four in-gamut drives, red, green, blue, white.
bool buildGamutMapRgbwwFromSolveQ16(const colorimetric_response::EmitterProfile &profile, const EmitterSolveMatrixQ16 &solve, const i32(&white1_xyz)[3], const i32(&white2_xyz)[3], WhiteAllocationPolicy policy, GamutMapRgbwwQ16 *out) FL_NO_EXCEPT
Bind a five-emitter map from one effective RGB solve and two white columns, all in the same D65 worki...
Base definition for an LED controller.
Definition crgb.hpp:179
WhiteAllocationQ16 allocation
Definition gamut_map.h:70
TwoWhiteAllocationQ16 allocation
Definition gamut_map.h:123
i32 max_neutral_lightness
OKLab lightness of the brightest D65 neutral this device can reach.
Definition gamut_map.h:85
i32 max_neutral_lightness
OKLab lightness of the brightest D65 neutral this device can reach.
Definition gamut_map.h:58
i32 max_neutral_lightness
OKLab lightness of the brightest D65 neutral this device can reach.
Definition gamut_map.h:132
EmitterSolveMatrixQ16 solve
Definition gamut_map.h:44
Inverse emitter matrix in s16.16, mapping XYZ to three emitter drives.
Everything the per-pixel allocation needs, derived once when a profile binds.
The same mapper for a device with a white emitter.
Definition gamut_map.h:69
Everything the per-pixel mapper needs, derived once when a profile binds.
Definition gamut_map.h:43
Everything the two-white allocation needs, derived once when a profile binds (C3, #4198).
The same mapper for a device with two white emitters.
Definition gamut_map.h:122