One pixel: XYZ in s16.16 to four drives, in the order red, green, blue, white, at whichever end of the feasible interval the profile's policy names.
False when no white level keeps the RGB drives in range, which means the target is outside the device hull – the gamut mapper's job, not this one's. drives is not written in that case.
Cost note, recorded rather than optimized away on a guess: this performs up to six s16.16 divisions per pixel, and is the only stage in the pipeline that divides per pixel at all. Each bound can instead be compared by cross-multiplication, leaving one division for the chosen level, at the price of i64 multiplies that are not obviously cheaper on an 8-bit target. Nobody has measured which wins, so the straightforward version is what ships.
Definition at line 177 of file white_allocation.cpp.hpp.
179 {
180 i32 at_zero[3];
182
183
184
185
186
187
188
189
190
191
192
193
195 i64 high = kWhiteFullDrive;
196 for (int i = 0; i < 3; ++i) {
197 const i32 slope = allocation.
per_white[i];
198 if (slope != 0) {
199
200 const i64 first = divideWhiteQ16(at_zero[i], slope);
201 const i64 second = divideWhiteQ16(at_zero[i] - kWhiteFullDrive, slope);
202 const i64 upper = slope > 0 ? first : second;
203 const i64 lower = slope > 0 ? second : first;
204 if (upper < high) {
205 high = upper;
206 }
207 if (lower > low) {
208 low = lower;
209 }
210 }
else if (at_zero[i] < -allocation.
slack[i] ||
211 at_zero[i] > kWhiteFullDrive + allocation.
slack[i]) {
212
213 return false;
214 }
215 }
216
217
218
219
220 if (high < 0) {
221 high = 0;
222 }
223 if (low > high) {
224 return false;
225 }
226
227
228
229 const i32 level = static_cast<i32>(
232 for (int i = 0; i < 3; ++i) {
234 const i32 channel_slack = allocation.
slack[i];
235 if (drive < -channel_slack || drive > kWhiteFullDrive + channel_slack) {
236 return false;
237 }
239 }
243 drives[3] = level < 0 ? 0
245 return true;
246}
i32 scaleWhiteQ16(i32 value, i32 factor) FL_NO_EXCEPT
Scale an s16.16 value by an s16.16 factor, rounding to nearest.
constexpr i32 kWhiteFullDrive
Full drive, as an s16.16 raw value.
@ RgbPreferred
As little as the target allows, which for most targets is none.
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.
References FL_NO_EXCEPT, fl::WhiteAllocationQ16::per_white, fl::WhiteAllocationQ16::policy, fl::WhiteAllocationQ16::rgb_solve, RgbPreferred, fl::WhiteAllocationQ16::slack, and solveRgbDrivesQ16().
Referenced by buildGamutMapRgbwFromSolveQ16(), mapAndAllocateRgbwQ16(), fl::anonymous_namespace{gamut_map.cpp.hpp}::RgbwFeasible::operator()(), and processPixelWideLinearQ16().