FastLED 3.10.6
Loading...
Searching...
No Matches

◆ buildWhiteAllocationFromSolveQ16()

bool fl::buildWhiteAllocationFromSolveQ16 ( const colorimetric_response::EmitterProfile & profile,
const EmitterSolveMatrixQ16 & solve,
const i32(&) white_xyz[3],
WhiteAllocationPolicy policy,
WhiteAllocationQ16 * out )

Bind from an RGB solve already adapted to the D65 working domain.

white_xyz must be adapted into that same domain. The profile supplies physical RGB column magnitudes for the per-channel rounding allowance.

Definition at line 89 of file white_allocation.cpp.hpp.

92 {
93 if (out == nullptr) {
94 return false;
95 }
96 out->policy = policy;
97 out->rgb_solve = solve;
98 solveRgbDrivesQ16(out->rgb_solve, white_xyz, out->per_white);
99
100 // Per-channel slack, so the allowance costs the same colour everywhere.
101 // Bind time, once per profile -- there is no per-pixel work here.
102 {
103 const float* xy[3] = {profile.xy_r, profile.xy_g, profile.xy_b};
104 const float luminance[3] = {profile.lum_r, profile.lum_g, profile.lum_b};
105 for (int channel = 0; channel < 3; ++channel) {
106 i32 xy_q16[2];
107 i32 luminance_q16;
108 if (!q16FromFloatBits(xy[channel][0], &xy_q16[0]) ||
109 !q16FromFloatBits(xy[channel][1], &xy_q16[1]) ||
110 !q16FromFloatBits(luminance[channel], &luminance_q16)) {
111 return false;
112 }
113 i64 column[3];
114 if (!detail::xyzColumnQ16(xy_q16, luminance_q16, column)) {
115 return false;
116 }
117 // The peak component, because that is what bounds the per-axis
118 // XYZ error a clamp of one drive unit can cause.
119 i64 peak = 0;
120 for (int axis = 0; axis < 3; ++axis) {
121 const i64 magnitude = column[axis] < 0 ? -column[axis]
122 : column[axis];
123 if (magnitude > peak) {
124 peak = magnitude;
125 }
126 }
127 // Both operands are Q16, so the quotient is in raw drive units.
128 // The numerator fits i64 even at the largest supported slack.
129 i64 scaled = (static_cast<i64>(kWhiteSlackAtUnitColumn) * 65536 +
130 peak / 2) / peak;
131 // Never zero: a column so large that the allowance rounds away
132 // would reject the rounding this exists to tolerate.
133 if (scaled < 1) {
134 scaled = 1;
135 }
136 // And never larger than the allowance it is scaling. Dim
137 // emitters have small columns, so the division wants to *grow*
138 // the slack -- at a luminance of 1e-4 it reaches 640,000 raw
139 // units, nearly ten in drive space, which accepts any drive at
140 // all and clamps it. That is the defect this function exists to
141 // remove, reinstated an order of magnitude worse.
142 //
143 // The tolerance is for rounding in the solve, which is a few raw
144 // units whatever the emitter's brightness. So this only ever
145 // narrows: 64 stays the ceiling and the column decides how far
146 // below it each channel sits. For the corpus device nothing is
147 // clipped -- green is already at 64 and the others below it --
148 // so the measurements above are unaffected.
149 if (scaled > kWhiteSlackAtUnitColumn) {
151 }
152 out->slack[channel] = static_cast<i32>(scaled);
153 }
154 }
155
156 // A white emitter the primaries cannot express at all leaves nothing for
157 // the allocation to trade against, and every bound below would divide by
158 // zero or be vacuous.
159 bool expressible = false;
160 for (int i = 0; i < 3; ++i) {
161 if (out->per_white[i] != 0) {
162 expressible = true;
163 }
164 }
165 return expressible;
166}
unsigned int xy(unsigned int x, unsigned int y)
constexpr i32 kWhiteSlackAtUnitColumn
Slack on the drive bounds, for an emitter whose XYZ column peaks at 1.
bool xyzColumnQ16(const i32(&xy)[2], i32 luminance, i64(&column)[3]) FL_NO_EXCEPT
XYZ of chromaticity xy at luminance luminance, all s16.16; false for a non-positive or out-of-simplex...
bool q16FromFloatBits(float value, i32 *out) FL_NO_EXCEPT
s16.16 of a float, rounded to nearest, computed from its IEEE-754 bits with integer arithmetic only –...
void solveRgbDrivesQ16(const EmitterSolveMatrixQ16 &matrix, const i32(&xyz)[3], i32(&drives)[3]) FL_NO_EXCEPT
One pixel: XYZ in s16.16 to three emitter drives in s16.16.
EmitterSolveMatrixQ16 rgb_solve
Inverse RGB emitter matrix.
i32 slack[3]
Per-channel drive slack, in s16.16 raw units.
WhiteAllocationPolicy policy
Which end of the interval this profile takes.
fl::i64 i64
Definition stdint.h:221

References FL_NO_EXCEPT, fl::colorimetric_response::EmitterProfile::lum_b, fl::colorimetric_response::EmitterProfile::lum_g, fl::colorimetric_response::EmitterProfile::lum_r, q16FromFloatBits(), solveRgbDrivesQ16(), xy(), fl::colorimetric_response::EmitterProfile::xy_b, fl::colorimetric_response::EmitterProfile::xy_g, fl::colorimetric_response::EmitterProfile::xy_r, and fl::detail::xyzColumnQ16().

Referenced by buildGamutMapRgbwFromSolveQ16(), and buildWhiteAllocationQ16().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: