From 5a8d5e4d9bb2a354637015f991162da057e59976 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 22 Apr 2026 09:56:34 +0200 Subject: [PATCH 1/3] make per-segment on/off and brightness behave the same as global --- wled00/FX_fcn.cpp | 50 +++++++++++++++++++++++++++++++---------------- wled00/led.cpp | 14 +++++++++++-- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index de3ebfea4b..ca8b5b5475 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -288,19 +288,29 @@ void Segment::startTransition(uint16_t dur, bool segmentCopy) { } if (isInTransition()) { if (segmentCopy && !_t->_oldSegment) { - // already in transition but segment copy requested and not yet created - _t->_oldSegment = new(std::nothrow) Segment(*this); // store/copy current segment settings - _t->_start = millis(); // restart countdown - _t->_dur = dur; - _t->_prevPaletteBlends = 0; - if (_t->_oldSegment) { - _t->_oldSegment->palette = _t->_palette; // restore original palette and colors (from start of transition) - for (unsigned i = 0; i < NUM_COLORS; i++) _t->_oldSegment->colors[i] = _t->_colors[i]; - DEBUGFX_PRINTF_P(PSTR("-- Updated transition with segment copy: S=%p T(%p) O[%p] OP[%p]\n"), this, _t, _t->_oldSegment, _t->_oldSegment->pixels); - if (!_t->_oldSegment->isActive()) stopTransition(); + if (blendingStyle != TRANSITION_FADE) { + // transition without a copy means we are in a segment brightness transition + // need to cancel the transition to not interfere with the next one (or global brightness change) + stopTransition(); + } else { + // already in FADE transition but segment copy requested and not yet created (for example color change during brightness transition) + // create segment copy and restart timer so currentBri() can interpolate correctly + _t->_oldSegment = new(std::nothrow) Segment(*this); // store/copy current segment settings + _t->_start = millis(); // restart countdown + _t->_dur = dur; + _t->_prevPaletteBlends = 0; + if (_t->_oldSegment) { + _t->_oldSegment->palette = _t->_palette; // restore original palette and colors (from start of transition) + for (unsigned i = 0; i < NUM_COLORS; i++) _t->_oldSegment->colors[i] = _t->_colors[i]; + DEBUGFX_PRINTF_P(PSTR("-- Updated transition with segment copy: S=%p T(%p) O[%p] OP[%p]\n"), this, _t, _t->_oldSegment, _t->_oldSegment->pixels); + if (!_t->_oldSegment->isActive()) stopTransition(); + } + return; } + } else { + // already in transition and no segment copy requested, just continue + return; } - return; } // no previous transition running, start by allocating memory for segment copy @@ -354,9 +364,11 @@ uint8_t Segment::currentBri() const { unsigned prog = progress(); unsigned curBri = on ? opacity : 0; if (prog < 0xFFFFU) { - // this will blend opacity in new mode if style is FADE (single effect call) - if (blendingStyle == TRANSITION_FADE) curBri = (prog * curBri + _t->_bri * (0xFFFFU - prog)) / 0xFFFFU; - else curBri = Segment::isPreviousMode() ? _t->_bri : curBri; + // this will blend opacity in new mode if style is FADE (single effect call) + if (blendingStyle == TRANSITION_FADE || !_t->_oldSegment) + curBri = (prog * curBri + _t->_bri * (0xFFFFU - prog)) / 0xFFFFU; + else + curBri = std::max((unsigned)curBri, (unsigned)_t->_bri); // non-FADE on/off transition: use the higher of old and new brightness so it is correct for both turn-on and turn-off } return curBri; } @@ -530,7 +542,7 @@ Segment &Segment::setCCT(uint16_t k) { Segment &Segment::setOpacity(uint8_t o) { if (opacity != o) { //DEBUG_PRINTF_P(PSTR("- Starting opacity transition: %d\n"), o); - startTransition(strip.getTransition(), blendingStyle != TRANSITION_FADE); // start transition prior to change + startTransition(strip.getTransition(), false); // opacity change always fades (no segment copy needed) opacity = o; stateChanged = true; // send UDP/WS broadcast } @@ -1588,10 +1600,12 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { c_a = color_blend16(c_a, segO->getPixelColorRaw(x + y*oCols), progInv); } else if (blendingStyle != TRANSITION_FADE) { // if we have global brightness change (not On/Off change) we will ignore transition style and just fade brightness (see led.cpp) - // workaround for On/Off transition + // workaround for global On/Off transition // (bri != briT) && !bri => from On to Off // (bri != briT) && bri => from Off to On if ((briOld == 0 || bri == 0) && ((!clipped && (bri != briT) && !bri) || (clipped && (bri != briT) && bri))) c_a = BLACK; + // per-segment on/off: the area belonging to the off-state segment must be black + if (segO && !seg->on) c_a = BLACK; } // map it into frame buffer x = c; // restore coordiates if we were PUSHing @@ -1660,10 +1674,12 @@ void WS2812FX::blendSegment(const Segment &topSegment) const { c_a = color_blend16(c_a, segO->getPixelColorRaw(i), progInv); } else if (blendingStyle != TRANSITION_FADE) { // if we have global brightness change (not On/Off change) we will ignore transition style and just fade brightness (see led.cpp) - // workaround for On/Off transition + // workaround for global On/Off transition // (bri != briT) && !bri => from On to Off // (bri != briT) && bri => from Off to On if ((briOld == 0 || bri == 0) && ((!clipped && (bri != briT) && !bri) || (clipped && (bri != briT) && bri))) c_a = BLACK; + // per-segment on/off: the area belonging to the off-state segment must be black + if (segO && !seg->on) c_a = BLACK; } // map into frame buffer i = k; // restore index if we were PUSHing diff --git a/wled00/led.cpp b/wled00/led.cpp index 35f5003679..d793f592ef 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -130,10 +130,20 @@ void stateUpdated(byte callMode) { } else { if (transitionActive) { briOld = briT; - } else if (bri != briOld || stateChanged) +/* } else if (bri != briOld || stateChanged) strip.setTransitionMode(true); // force all segments to transition mode transitionActive = true; - transitionStartTime = now; + transitionStartTime = now;*/ + + } else if (bri != briOld) + strip.setTransitionMode(true); // force all segments to transition mode on global brightness change + if (bri != briOld) { + transitionActive = true; // global brightness changed, (re)start global transition + transitionStartTime = now; + } else if (jsonTransitionOnce) { + strip.setTransition(transitionDelay); + jsonTransitionOnce = false; + } } stateChanged = false; } From 65c2f83955d72dff72793ad57a97ded7ad6ad0a8 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 22 Apr 2026 10:20:49 +0200 Subject: [PATCH 2/3] fix rabbit suggestions --- wled00/FX_fcn.cpp | 12 +++++++++--- wled00/led.cpp | 13 +++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index ca8b5b5475..ccc380cdcf 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -287,17 +287,19 @@ void Segment::startTransition(uint16_t dur, bool segmentCopy) { return; } if (isInTransition()) { + const uint8_t visibleBri = currentBri(); if (segmentCopy && !_t->_oldSegment) { if (blendingStyle != TRANSITION_FADE) { // transition without a copy means we are in a segment brightness transition - // need to cancel the transition to not interfere with the next one (or global brightness change) + // need to cancel the transition to not interfere with the new one (or global brightness change) stopTransition(); } else { - // already in FADE transition but segment copy requested and not yet created (for example color change during brightness transition) + // already in FADE transition but segment copy requested and not yet created (for example a SWIPE color change during brightness transition) // create segment copy and restart timer so currentBri() can interpolate correctly _t->_oldSegment = new(std::nothrow) Segment(*this); // store/copy current segment settings _t->_start = millis(); // restart countdown _t->_dur = dur; + _t->_bri = visibleBri; _t->_prevPaletteBlends = 0; if (_t->_oldSegment) { _t->_oldSegment->palette = _t->_palette; // restore original palette and colors (from start of transition) @@ -308,7 +310,11 @@ void Segment::startTransition(uint16_t dur, bool segmentCopy) { return; } } else { - // already in transition and no segment copy requested, just continue + // already in transition; restart from the currently visible brightness + _t->_start = millis(); + _t->_dur = dur; + _t->_bri = visibleBri; + _t->_prevPaletteBlends = 0; return; } } diff --git a/wled00/led.cpp b/wled00/led.cpp index d793f592ef..f13f4cceba 100644 --- a/wled00/led.cpp +++ b/wled00/led.cpp @@ -128,17 +128,10 @@ void stateUpdated(byte callMode) { applyFinalBri(); strip.trigger(); } else { - if (transitionActive) { - briOld = briT; -/* } else if (bri != briOld || stateChanged) - strip.setTransitionMode(true); // force all segments to transition mode - transitionActive = true; - transitionStartTime = now;*/ - - } else if (bri != briOld) - strip.setTransitionMode(true); // force all segments to transition mode on global brightness change if (bri != briOld) { - transitionActive = true; // global brightness changed, (re)start global transition + if (transitionActive) briOld = briT; // re-targeting mid-transition: start interpolation from current in-flight value + strip.setTransitionMode(true); // force all segments to transition mode + transitionActive = true; transitionStartTime = now; } else if (jsonTransitionOnce) { strip.setTransition(transitionDelay); From cd33b1308eb051127c885a50e29d6ffaeb6415e8 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Wed, 22 Apr 2026 10:29:30 +0200 Subject: [PATCH 3/3] another useful fix by the rabbit --- wled00/FX_fcn.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index ccc380cdcf..d1a824774a 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -370,8 +370,9 @@ uint8_t Segment::currentBri() const { unsigned prog = progress(); unsigned curBri = on ? opacity : 0; if (prog < 0xFFFFU) { - // this will blend opacity in new mode if style is FADE (single effect call) - if (blendingStyle == TRANSITION_FADE || !_t->_oldSegment) + // this will blend opacity in new mode if style is FADE (single effect call) + const bool onOffTransition = _t->_oldSegment && (_t->_oldSegment->on != on); + if (blendingStyle == TRANSITION_FADE || !_t->_oldSegment || !onOffTransition) curBri = (prog * curBri + _t->_bri * (0xFFFFU - prog)) / 0xFFFFU; else curBri = std::max((unsigned)curBri, (unsigned)_t->_bri); // non-FADE on/off transition: use the higher of old and new brightness so it is correct for both turn-on and turn-off