From 9dbd5f89b6d50ab48257bc20ab49a9fb6c8b334f Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Wed, 18 Jun 2025 22:16:58 +0200 Subject: [PATCH 1/3] Tackle CCT issue caused by segment blending - wled#4734 --- wled00/FX.h | 5 ++++- wled00/FX_fcn.cpp | 36 +++++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/wled00/FX.h b/wled00/FX.h index bc3eec767..82213179b 100755 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -802,7 +802,7 @@ class Segment { friend class WS2812FX; }; -// main "strip" class (104 bytes) +// main "strip" class (108 bytes) class WS2812FX { typedef uint16_t (*mode_ptr)(); // pointer to mode function typedef void (*show_callback)(); // pre show callback @@ -829,6 +829,7 @@ class WS2812FX { cctFromRgb(false), // true private variables _pixels(nullptr), + _pixelCCT(nullptr), _suspend(false), _brightness(DEFAULT_BRIGHTNESS), _length(DEFAULT_LED_COUNT), @@ -857,6 +858,7 @@ class WS2812FX { ~WS2812FX() { d_free(_pixels); + d_free(_pixelCCT); // just in case d_free(customMappingTable); _mode.clear(); _modeData.clear(); @@ -1004,6 +1006,7 @@ class WS2812FX { private: uint32_t *_pixels; + uint8_t *_pixelCCT; std::vector _segments; volatile bool _suspend; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index e7c27be10..4aecbae41 100755 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1224,11 +1224,6 @@ void WS2812FX::service() { unsigned frameDelay = FRAMETIME; if (!seg.freeze) { //only run effect function if not frozen - int oldCCT = BusManager::getSegmentCCT(); // store original CCT value (actually it is not Segment based) - // when correctWB is true we need to correct/adjust RGB value according to desired CCT value, but it will also affect actual WW/CW ratio - // when cctFromRgb is true we implicitly calculate WW and CW from RGB values - if (cctFromRgb) BusManager::setSegmentCCT(-1); - else BusManager::setSegmentCCT(seg.currentCCT(), correctWB); // Effect blending uint16_t prog = seg.progress(); seg.beginDraw(prog); // set up parameters for get/setPixelColor() (will also blend colors and palette if blend style is FADE) @@ -1249,7 +1244,6 @@ void WS2812FX::service() { Segment::modeBlend(false); // unset semaphore } if (seg.isInTransition() && frameDelay > FRAMETIME) frameDelay = FRAMETIME; // force faster updates during transition - BusManager::setSegmentCCT(oldCCT); // restore old CCT for ABL adjustments } seg.next_time = nowUp + frameDelay; @@ -1324,6 +1318,7 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { const unsigned progress = topSegment.progress(); const unsigned progInv = 0xFFFFU - progress; uint8_t opacity = topSegment.currentBri(); // returns transitioned opacity for style FADE + uint8_t cct = topSegment.currentCCT(); Segment::setClippingRect(0, 0); // disable clipping by default @@ -1396,6 +1391,7 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { const int baseY = topSegment.startY + y; size_t indx = XY(baseX, baseY); // absolute address on strip _pixels[indx] = color_blend(_pixels[indx], blend(c, _pixels[indx]), o); + if (_pixelCCT) _pixelCCT[indx] = cct; // Apply mirroring if (topSegment.mirror || topSegment.mirror_y) { const int mirrorX = topSegment.start + width - x - 1; @@ -1406,6 +1402,11 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { if (topSegment.mirror) _pixels[idxMX] = color_blend(_pixels[idxMX], blend(c, _pixels[idxMX]), o); if (topSegment.mirror_y) _pixels[idxMY] = color_blend(_pixels[idxMY], blend(c, _pixels[idxMY]), o); if (topSegment.mirror && topSegment.mirror_y) _pixels[idxMM] = color_blend(_pixels[idxMM], blend(c, _pixels[idxMM]), o); + if (_pixelCCT) { + if (topSegment.mirror) _pixelCCT[idxMX] = cct; + if (topSegment.mirror_y) _pixelCCT[idxMY] = cct; + if (topSegment.mirror && topSegment.mirror_y) _pixelCCT[idxMM] = cct; + } } }; @@ -1477,10 +1478,12 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { indxM += topSegment.offset; // offset/phase if (indxM >= topSegment.stop) indxM -= length; // wrap _pixels[indxM] = color_blend(_pixels[indxM], blend(c, _pixels[indxM]), o); + if (_pixelCCT) _pixelCCT[indxM] = cct; } indx += topSegment.offset; // offset/phase if (indx >= topSegment.stop) indx -= length; // wrap _pixels[indx] = color_blend(_pixels[indx], blend(c, _pixels[indx]), o); + if (_pixelCCT) _pixelCCT[indx] = cct; }; // if we blend using "push" style we need to "shift" canvas to left/right/ @@ -1588,6 +1591,9 @@ void WS2812FX::show() { size_t diff = showNow - _lastShow; size_t totalLen = getLengthTotal(); + _pixelCCT = static_cast(d_malloc(totalLen * sizeof(uint8_t))); // allocate CCT buffer if necessary + if (_pixelCCT) memset(_pixelCCT, 127, totalLen); // set neutral (50:50) CCT + if (realtimeMode == REALTIME_MODE_INACTIVE || useMainSegmentOnly || realtimeOverride > REALTIME_OVERRIDE_NONE) { // clear frame buffer for (size_t i = 0; i < totalLen; i++) _pixels[i] = BLACK; // memset(_pixels, 0, sizeof(uint32_t) * getLengthTotal()); @@ -1606,7 +1612,23 @@ void WS2812FX::show() { if (newBri != _brightness) BusManager::setBrightness(newBri); // paint actuall pixels - for (size_t i = 0; i < totalLen; i++) BusManager::setPixelColor(getMappedPixelIndex(i), realtimeMode && arlsDisableGammaCorrection ? _pixels[i] : gamma32(_pixels[i])); + // WARNING: as WLED doesn't handle CCT on pixel level but on Segment level instead + // we need to determine to which segment a pixel belongs (may belong to several!!!) + // and then set appropriate CCT from that segment for a particular pixel. + int oldCCT = Bus::getCCT(); // store original CCT value (since it is global) + for (size_t i = 0; i < totalLen; i++) { + // when cctFromRgb is true we implicitly calculate WW and CW from RGB values (cct==-1) + // when correctWB is true setSegmentCCT() will convert CCT into K with which we can then + // correct/adjust RGB value according to desired CCT value, it will still affect actual WW/CW ratio + if (_pixelCCT) { + if (i == 0 || _pixelCCT[i-1] != _pixelCCT[i]) BusManager::setSegmentCCT(cctFromRgb ? -1 : _pixelCCT[i], correctWB); + } + BusManager::setPixelColor(getMappedPixelIndex(i), realtimeMode && arlsDisableGammaCorrection ? _pixels[i] : gamma32(_pixels[i])); + } + Bus::setCCT(oldCCT); // restore old CCT for ABL adjustments + + d_free(_pixelCCT); + _pixelCCT = nullptr; // some buses send asynchronously and this method will return before // all of the data has been sent. From e2fd1559d223bacbc780fe3cf11f7d17a2d04bcb Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Thu, 19 Jun 2025 11:48:56 +0200 Subject: [PATCH 2/3] Optimise CCT buffer --- wled00/FX_fcn.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 4aecbae41..ce2b9976c 100755 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1591,7 +1591,11 @@ void WS2812FX::show() { size_t diff = showNow - _lastShow; size_t totalLen = getLengthTotal(); - _pixelCCT = static_cast(d_malloc(totalLen * sizeof(uint8_t))); // allocate CCT buffer if necessary + // WARNING: as WLED doesn't handle CCT on pixel level but on Segment level instead + // we need to keep track of each pixel's CCT when blending segments (if CCT is present) + // and then set appropriate CCT from that pixel during paint (see below). + if ((hasCCTBus() || correctWB) && !cctFromRgb) + _pixelCCT = static_cast(d_malloc(totalLen * sizeof(uint8_t))); // allocate CCT buffer if necessary if (_pixelCCT) memset(_pixelCCT, 127, totalLen); // set neutral (50:50) CCT if (realtimeMode == REALTIME_MODE_INACTIVE || useMainSegmentOnly || realtimeOverride > REALTIME_OVERRIDE_NONE) { @@ -1612,16 +1616,14 @@ void WS2812FX::show() { if (newBri != _brightness) BusManager::setBrightness(newBri); // paint actuall pixels - // WARNING: as WLED doesn't handle CCT on pixel level but on Segment level instead - // we need to determine to which segment a pixel belongs (may belong to several!!!) - // and then set appropriate CCT from that segment for a particular pixel. int oldCCT = Bus::getCCT(); // store original CCT value (since it is global) + // when cctFromRgb is true we implicitly calculate WW and CW from RGB values (cct==-1) + if (cctFromRgb) BusManager::setSegmentCCT(-1); for (size_t i = 0; i < totalLen; i++) { - // when cctFromRgb is true we implicitly calculate WW and CW from RGB values (cct==-1) // when correctWB is true setSegmentCCT() will convert CCT into K with which we can then // correct/adjust RGB value according to desired CCT value, it will still affect actual WW/CW ratio - if (_pixelCCT) { - if (i == 0 || _pixelCCT[i-1] != _pixelCCT[i]) BusManager::setSegmentCCT(cctFromRgb ? -1 : _pixelCCT[i], correctWB); + if (_pixelCCT) { // cctFromRgb already exluded at allocation + if (i == 0 || _pixelCCT[i-1] != _pixelCCT[i]) BusManager::setSegmentCCT(_pixelCCT[i], correctWB); } BusManager::setPixelColor(getMappedPixelIndex(i), realtimeMode && arlsDisableGammaCorrection ? _pixels[i] : gamma32(_pixels[i])); } From 10d1098403c6783c030c07e11e791320944b7e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Tue, 24 Jun 2025 11:08:10 +0200 Subject: [PATCH 3/3] Fix comment Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- wled00/FX_fcn.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index ce2b9976c..6c1048204 100755 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1615,7 +1615,7 @@ void WS2812FX::show() { uint8_t newBri = estimateCurrentAndLimitBri(_brightness, _pixels); if (newBri != _brightness) BusManager::setBrightness(newBri); - // paint actuall pixels + // paint actual pixels int oldCCT = Bus::getCCT(); // store original CCT value (since it is global) // when cctFromRgb is true we implicitly calculate WW and CW from RGB values (cct==-1) if (cctFromRgb) BusManager::setSegmentCCT(-1);