|
FastLED 3.10.6
|
| bool fl::allocateTwoWhiteDrivesQ16 | ( | const TwoWhiteAllocationQ16 & | allocation, |
| const i32(&) | xyz[3], | ||
| i32(&) | drives[5] ) |
One pixel: XYZ in s16.16 to five drives – red, green, blue, white1, white2 – at whichever end of the feasible total the profile's policy names.
The method, and why it is not a search. Writing the total s = w1 + w2 and substituting w2 = s - w1 leaves the RGB drives as
(d0 - s * per_white2) - w1 * difference
which is the one-white shape with a shifted target. So at any fixed total the feasible w1 is again an interval, bounded by five lower and five upper bounds: three from the RGB drives, and two more because w1 and w2 = s - w1 are each a drive in their own right. Every one of those bounds is affine in s, so "some `w1` exists at this total" is exactly the conjunction of the pairwise inequalities lower_k(s) <= upper_j(s), each linear in s and each solvable for one s bound. Intersecting them gives the feasible totals in closed form – no bisection, no vertex enumeration, no per-pixel iteration (A3/B11).
That the interval has to be found rather than assumed is the whole point. Achievable totals form [s_lo, s_hi], which need not start at zero: a bright target is unreachable with the primaries alone, so a bisection seeded at zero has no feasible starting point and reports such targets unreachable. The first attempt at this made exactly that assumption (#4198).
At the chosen total the split is not free, which is measurement rather than assumption. The reference settles a free split by minimizing the sum of squares of the RGB drives, and an earlier revision did the same here; the feasible splits at the extreme total turn out to be a single point, so there was nothing for the rule to choose. Width measured 0.0 over 4000 random targets on the corpus's cool/warm device and 951 on a device built to make one drive's constraint parallel to w1 + w2 – the shape that could have produced an optimal edge. Two whites of the same colour are the exception, and there the reference's own tie-break is the end this takes.
False when no total keeps every drive 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: up to 25 s16.16 divisions per pixel against the one-white path's six. Whether cross-multiplying the pairwise comparisons – which would leave one division and a great many i64 multiplies – wins on an 8-bit target is unmeasured, so the straightforward version is what ships.
Definition at line 394 of file white_allocation.cpp.hpp.
References fl::TwoWhiteAllocationQ16::difference, FL_NO_EXCEPT, fl::TwoWhiteAllocationQ16::per_white2, fl::TwoWhiteAllocationQ16::policy, fl::TwoWhiteAllocationQ16::rgb_solve, RgbPreferred, solveRgbDrivesQ16(), and type_rank< T >::value.
Referenced by buildGamutMapRgbwwFromSolveQ16(), mapAndAllocateRgbwwQ16(), fl::anonymous_namespace{gamut_map.cpp.hpp}::RgbwwFeasible::operator()(), and processPixelWideLinearQ16().
Here is the call graph for this function:
Here is the caller graph for this function: