Skip to content

fix color jump if changing mid transition#5601

Open
DedeHai wants to merge 3 commits into
wled:mainfrom
DedeHai:fix_midtransition_colorchange
Open

fix color jump if changing mid transition#5601
DedeHai wants to merge 3 commits into
wled:mainfrom
DedeHai:fix_midtransition_colorchange

Conversation

@DedeHai
Copy link
Copy Markdown
Collaborator

@DedeHai DedeHai commented May 11, 2026

This improves the color changes while a FADE transition is going on.

Summary by CodeRabbit

  • Bug Fixes
    • Smoother visual transitions when changes occur during an active transition.
    • Prevented visible color and brightness jumps by maintaining consistent blending mid-transition.
    • Reduced discontinuities when updating effects, palettes, or brightness while a transition is in progress.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

Walkthrough

startTransition() now preserves visual continuity for in-flight transitions by restoring or blending the old segment from the transition-start state, capturing current blended colors when no old segment is created, and conditionally resetting palette-blend tracking and timers.

Changes

Mid-Transition State Management

Layer / File(s) Summary
Docs and transition context
wled00/FX_fcn.cpp
Added comments clarifying _t holds "from" values and _oldSegment is created for non-FADE transitions / FX changes.
Segment Copy Transition Restart
wled00/FX_fcn.cpp
When startTransition() is called during an ongoing transition with segmentCopy and no _oldSegment, the transition timer/duration is restarted, _prevPaletteBlends is reset, and _oldSegment is initialized from the transition-start palette/colors; if _progress>0, _oldSegment colors are blended toward current transition colors and the transition may be stopped if _oldSegment becomes inactive.
Mid-Transition Color State Capture
wled00/FX_fcn.cpp
When startTransition() is invoked mid-transition without creating _oldSegment, the current blended visual state is captured into _t->_colors and stored from-state values (_palette, _bri, _cct) are refreshed; timer restart and _prevPaletteBlends reset occur only for TRANSITION_FADE when _oldSegment is null.
Brightness Calculation Formatting
wled00/FX_fcn.cpp
Whitespace/formatting changes in currentBri() non-FADE active-transition branch; behavior unchanged.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • wled/WLED#4841: Both PRs modify Segment::startTransition and reset _prevPaletteBlends when handling active segmentCopy transitions.

Suggested labels

effect

Suggested reviewers

  • blazoncek
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix color jump if changing mid transition' directly corresponds to the main change: addressing a visual artifact (color jump) that occurs when segment colors change during an active FADE transition.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added the effect label May 11, 2026
@DedeHai
Copy link
Copy Markdown
Collaborator Author

DedeHai commented May 11, 2026

@blazoncek I looked at your commit blazoncek@40d9812
but porting it did not fully solve the issue. I came up with a similar but slightly different solution that in my tests performed better.

coderabbitai[bot]

This comment was marked as resolved.

@DedeHai DedeHai added optimization re-working an existing feature to be faster, or use less memory and removed effect labels May 11, 2026
@blazoncek
Copy link
Copy Markdown
Contributor

A credit would be due. Don't you think?

@coderabbitai coderabbitai Bot added the effect label May 12, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wled00/FX_fcn.cpp`:
- Around line 301-324: When promoting an in-flight fade into a segmentCopy
transition (the branch where segmentCopy && !_t->_oldSegment), refresh the
transition's stored "from" visual state so it reflects the current blended
frame: for each color set _t->_colors[i] to the current blend (use color_blend16
with _t->_colors[i], colors[i], and _t->_progress), and set _t->_bri =
currentBri() and _t->_cct = currentCCT() before restarting _t->_start/_t->_dur
and creating _t->_oldSegment; this ensures beginDraw(), currentBri(), and
currentCCT() animate from the actual mid-fade values rather than the original
start values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d26c0df1-c4fb-45ce-a2c4-775a180bdc4e

📥 Commits

Reviewing files that changed from the base of the PR and between a7a9ce6 and 7b91abd.

📒 Files selected for processing (1)
  • wled00/FX_fcn.cpp

Comment thread wled00/FX_fcn.cpp
Comment on lines 301 to +324
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->_start = millis(); // restart transition timer
_t->_dur = dur;
_t->_prevPaletteBlends = 0;
_t->_prevPaletteBlends = 0; // reset palette blends
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];
// if already partway through a FADE transition, set old segment's colors to current blend to avoid jumping back to original colors
if (_t->_progress > 0)
for (unsigned i = 0; i < NUM_COLORS; i++) _t->_oldSegment->colors[i] = color_blend16(_t->_colors[i], colors[i], _t->_progress);
else
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();
}
} else if (_t->_progress > 0) {
// already in a transition: capture the current visual blend as the new "from" state so the incoming change does not cause a visible jump.
// _palT already holds the intermediate blended palette and will continue blending toward the new target (see beginDraw()), so no palette action needed.
// initial version by @blazoncek (https://github.com/blazoncek/WLED/commit/40d9812)
for (unsigned i = 0; i < NUM_COLORS; i++) _t->_colors[i] = color_blend16(_t->_colors[i], colors[i], _t->_progress);
_t->_palette = palette; // update "original" palette (reduces jump in SAVE_RAM builds)
_t->_bri = currentBri(); // update "original" brightness note: _t->_progress is updated in updateTransitionProgress() so still valid here
_t->_cct = currentCCT(); // update "original" CCT (reduces jump)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Refresh _t’s blended state before restarting a late segmentCopy transition.

When this branch upgrades an in-flight fade into a segmentCopy transition, it restarts _t->_start / _t->_dur but leaves _t->_colors, _t->_bri, and _t->_cct at the original transition start. beginDraw(), currentBri(), and currentCCT() then animate from stale values, so a mid-fade mode change can still snap brightness/CCT — and sometimes colors — backwards on the first frame.

💡 Suggested fix
   if (isInTransition()) {
     if (segmentCopy && !_t->_oldSegment) {
       // already in transition but segment copy requested and not yet created
+      uint32_t blendedColors[NUM_COLORS];
+      for (unsigned i = 0; i < NUM_COLORS; i++) {
+        blendedColors[i] = (_t->_progress > 0)
+          ? color_blend16(_t->_colors[i], colors[i], _t->_progress)
+          : _t->_colors[i];
+      }
+      _t->_palette = palette;
+      _t->_bri     = currentBri();
+      _t->_cct     = currentCCT();
+      for (unsigned i = 0; i < NUM_COLORS; i++) _t->_colors[i] = blendedColors[i];
+
       _t->_oldSegment = new(std::nothrow) Segment(*this); // store/copy current segment settings
       _t->_start = millis(); // restart transition timer
       _t->_dur   = dur;
       _t->_prevPaletteBlends = 0; // reset palette blends
       if (_t->_oldSegment) {
         _t->_oldSegment->palette = _t->_palette;          // restore original palette and colors (from start of transition)
-        // if already partway through a FADE transition, set old segment's colors to current blend to avoid jumping back to original colors
-        if (_t->_progress > 0)
-          for (unsigned i = 0; i < NUM_COLORS; i++) _t->_oldSegment->colors[i] = color_blend16(_t->_colors[i], colors[i], _t->_progress);
-        else
-          for (unsigned i = 0; i < NUM_COLORS; i++) _t->_oldSegment->colors[i] = _t->_colors[i];
+        for (unsigned i = 0; i < NUM_COLORS; i++) _t->_oldSegment->colors[i] = _t->_colors[i];
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/FX_fcn.cpp` around lines 301 - 324, When promoting an in-flight fade
into a segmentCopy transition (the branch where segmentCopy &&
!_t->_oldSegment), refresh the transition's stored "from" visual state so it
reflects the current blended frame: for each color set _t->_colors[i] to the
current blend (use color_blend16 with _t->_colors[i], colors[i], and
_t->_progress), and set _t->_bri = currentBri() and _t->_cct = currentCCT()
before restarting _t->_start/_t->_dur and creating _t->_oldSegment; this ensures
beginDraw(), currentBri(), and currentCCT() animate from the actual mid-fade
values rather than the original start values.

@DedeHai DedeHai removed the effect label May 12, 2026
@blazoncek
Copy link
Copy Markdown
Contributor

BTW both our fixes are still flawed to some extent. Question is if it is worth investing much time in edge cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

optimization re-working an existing feature to be faster, or use less memory

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants