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

◆ mapAndAllocateRgbwQ16()

void fl::mapAndAllocateRgbwQ16 ( const GamutMapRgbwQ16 & map,
const i32(&) xyz[3],
i32(&) drives[4] )

One pixel: XYZ in s16.16 to four in-gamut drives, red, green, blue, white.

Same shape as the three-emitter path – one forward OKLab transform, one lightness comparison, then eight halvings – but every feasibility test goes through the white-preferred allocation, so the hull it maps onto is the device's real one.

Definition at line 543 of file gamut_map.cpp.hpp.

544 {
545 if (allocateEmitterDrivesQ16(map.allocation, xyz, drives)) {
546 // Already inside the device's hull -- which, with a white emitter,
547 // is a good deal larger than the RGB one.
548 return;
549 }
550
551 i32 lab[3];
552 xyzToOklabQ16(xyz, lab);
553 i32 lightness = lab[0];
554 if (lightness < 0) {
555 lightness = 0;
556 }
557
558 // Same above-cap interval clamp as the RGB path (#4245). The white
559 // emitter moves the cap but does not change the shape: above it the
560 // feasible chroma still misses zero.
561 if (lightness > map.max_neutral_lightness) {
562 i32 low_edge = 0;
563 i32 high_edge = 0;
564 if (feasibleChromaInterval(lab, lightness, RgbwFeasible{map.allocation},
565 &low_edge, &high_edge)) {
566 i32 candidate_xyz[3];
567 chromaCandidateXyz(lab, lightness, clampChromaFactor(low_edge, high_edge),
568 candidate_xyz);
569 if (allocateEmitterDrivesQ16(map.allocation, candidate_xyz, drives)) {
570 return;
571 }
572 }
573
574 // Same walk-down as the RGB path: dropping straight to the cap when
575 // the probes stop landing is a step, not a mapping.
576 const i32 reachable = highestReachableLightness(
577 lab, map.max_neutral_lightness, lightness,
578 RgbwFeasible{map.allocation});
579 i32 edge_low = 0;
580 i32 edge_high = 0;
581 if (reachable > map.max_neutral_lightness &&
582 feasibleChromaInterval(lab, reachable, RgbwFeasible{map.allocation},
583 &edge_low, &edge_high)) {
584 i32 edge_xyz[3];
585 chromaCandidateXyz(lab, reachable,
586 clampChromaFactor(edge_low, edge_high), edge_xyz);
587 if (allocateEmitterDrivesQ16(map.allocation, edge_xyz, drives)) {
588 return;
589 }
590 }
591 lightness = map.max_neutral_lightness;
592 }
593
594 const i32 factor =
595 largestFeasibleChroma(lab, lightness, RgbwFeasible{map.allocation});
596 i32 mapped_xyz[3];
597 chromaCandidateXyz(lab, lightness, factor, mapped_xyz);
598 if (allocateEmitterDrivesQ16(map.allocation, mapped_xyz, drives)) {
599 return;
600 }
601 // The accepted candidate can fall a few ULP outside on the final
602 // re-solve. Fall back to the neutral at this lightness, which the
603 // lightness bound guarantees is reachable.
604 const i32 neutral_lab[3] = {lightness, 0, 0};
605 i32 neutral_xyz[3];
606 oklabToXyzQ16(neutral_lab, neutral_xyz);
607 if (!allocateEmitterDrivesQ16(map.allocation, neutral_xyz, drives)) {
608 drives[0] = 0;
609 drives[1] = 0;
610 drives[2] = 0;
611 drives[3] = 0;
612 }
613}
i32 clampChromaFactor(i32 low, i32 high) FL_NO_EXCEPT
The target's chroma clamped into [low, high], in factor space.
void chromaCandidateXyz(const i32(&lab)[3], i32 lightness, i32 factor, i32(&out_xyz)[3]) FL_NO_EXCEPT
XYZ of the candidate at factor of the target's chroma.
i32 largestFeasibleChroma(const i32(&lab)[3], i32 lightness, Feasible feasible) FL_NO_EXCEPT
Largest chroma factor the hull accepts, by a fixed count of halvings.
i32 highestReachableLightness(const i32(&lab)[3], i32 cap, i32 requested, Feasible feasible) FL_NO_EXCEPT
The highest lightness at or below requested whose feasible chroma interval the probe scan can still f...
bool feasibleChromaInterval(const i32(&lab)[3], i32 lightness, Feasible feasible, i32 *out_low, i32 *out_high) FL_NO_EXCEPT
The feasible chroma interval at the target's own lightness, as factors of the target's chroma.
MapRedBlackTree< Key, T, Compare, fl::allocator_slab< char > > map
Definition map.h:283
void oklabToXyzQ16(const i32(&lab)[3], i32(&out_xyz)[3]) FL_NO_EXCEPT
OKLab (s16.16) -> XYZ (s16.16).
bool allocateEmitterDrivesQ16(const WhiteAllocationQ16 &allocation, const i32(&xyz)[3], i32(&drives)[4]) FL_NO_EXCEPT
One pixel: XYZ in s16.16 to four drives, in the order red, green, blue, white, at whichever end of th...
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.

References allocateEmitterDrivesQ16(), FL_NO_EXCEPT, oklabToXyzQ16(), and xyzToOklabQ16().

Referenced by processPixelWideLinearQ16().

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