Skip to content

Conversation

DedeHai
Copy link
Collaborator

@DedeHai DedeHai commented Aug 30, 2025

as mentioned in #4550 there is no easy way to overlay text with the new blending modes. this addition fixes that.

I also made some slight optimisations to blendSegment() and found that the compiler is very unpredictable. I assume that is mostly due to pipelining and core register use. In general, replacing bit-field accessed variables with an alias or a pre-computed value is faster, but here it is not always the case. I tried different variations and this was the best performing one. Replacing any other repeated calculations in blendSegment() does not improve performance.

while flash size increases by a few hundred bytes, FPS improve by 3-5% depending on FX and layering used.

Summary by CodeRabbit

  • New Features

    • Added a new "Stencil" blend mode for segments to enable background-based masking effects.
  • Bug Fixes

    • Blend mode selection now reliably sends the actual mode value so chosen modes apply correctly.
    • Unified and improved blending across 1D/2D segments, including mirroring, reverse/transpose, and push behavior.
    • Background color is now respected for applicable blend modes.
  • Notes

    • Blend mode value handling simplified; non-standard codes may be accepted.

- better performance at the cost of about 700 bytes of flash
- Stencil overlay adds an FX on top but leaves the backgroundcolor transparent, this is useful for classic text overlays
Copy link
Contributor

coderabbitai bot commented Aug 30, 2025

Walkthrough

Unifies per-pixel blending into a new segblend routine, adds stencil (bm=32) support, introduces push/mirror/reverse/transpose coordinate handling, updates the UI to send explicit blend values, and simplifies JSON deserialization for segment blendMode; header docs updated to mention "stencil".

Changes

Cohort / File(s) Summary of changes
Blending core refactor
wled00/FX_fcn.cpp
Replaces per-pixel function-pointer/lambda blends with a centralized static segblend switch that computes blended colors (including stencil/bgColor handling). Adds push-offset computation, applyMirror/applyReverse gating, push-style coordinate shifts, transpose/reverse handling, and consistent per-pixel segblend usage across 1D/2D/mirrored paths.
UI blend mode option
wled00/data/index.js
Adds a “Stencil” blend mode option (value 32) and changes setBm(s) to read the option value (not selectedIndex) so non-sequential blend codes are sent in updates.
JSON bm parsing update
wled00/json.cpp
Simplifies blend mode deserialization by calling getVal(elem["bm"], seg.blendMode) directly, removing the temporary local and explicit clamp to 0–15.
Header doc tweak
wled00/FX.h
Removes a commented-out 4-bit blendMode bitfield and updates the blendMode documentation to list the new “stencil” option. No API signatures changed.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • blazoncek
  • Aircoookie
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
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

🧹 Nitpick comments (3)
wled00/FX.h (1)

459-459: Document exact bm codes, incl. Stencil (32) and Average (5).

Make the mapping explicit to avoid confusion and to reflect the UI: 0=top, 1=bottom, 2=add, 3=subtract, 4=difference, 5=average, 6=multiply, 7=divide, 8=lighten, 9=darken, 10=screen, 11=overlay, 12=hardlight, 13=softlight, 14=dodge, 15=burn, 32=stencil (uses colors[1] as background mask).

-    uint8_t   blendMode;          // segment blending modes: top, bottom, add, subtract, difference, multiply, divide, lighten, darken, screen, overlay, hardlight, softlight, dodge, burn, stencil
+    uint8_t   blendMode;          // segment blending modes (bm): 0=top,1=bottom,2=add,3=subtract,4=difference,5=average,6=multiply,7=divide,8=lighten,9=darken,10=screen,11=overlay,12=hardlight,13=softlight,14=dodge,15=burn,32=stencil (bg=colors[1])
wled00/data/index.js (1)

2362-2365: Send bm as a number, not a string.

Parse to int before sending to avoid accidental string types and keep consistency with other setters.

-	var value = gId(`seg${s}bm`).value;
-	var obj = {"seg": {"id": s, "bm": value}};
+	const value = parseInt(gId(`seg${s}bm`).value, 10);
+	const obj = {"seg": {"id": s, "bm": value}};
wled00/json.cpp (1)

305-305: Validate bm to allowed set (0–15, 32).

Removing the clamp enables arbitrary values. segblend defaults to “Top”, but guarding here prevents unexpected UI/API inputs from drifting.

-  getVal(elem["bm"], seg.blendMode);
+  if (getVal(elem["bm"], seg.blendMode)) {
+    if (seg.blendMode != 32 && seg.blendMode > 15) seg.blendMode = 0; // fallback to Top
+  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between da7f107 and 9c6ab89.

📒 Files selected for processing (4)
  • wled00/FX.h (1 hunks)
  • wled00/FX_fcn.cpp (6 hunks)
  • wled00/data/index.js (2 hunks)
  • wled00/json.cpp (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
wled00/data/**/*.{htm,html,css,js}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use tabs for indentation in web files (.htm/.html/.css/.js) under wled00/data

Files:

  • wled00/data/index.js
wled00/{*.cpp,!(html_*)*.h}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use spaces (2 per level) for C++ source and header files

Files:

  • wled00/FX.h
  • wled00/json.cpp
  • wled00/FX_fcn.cpp
🧠 Learnings (2)
📚 Learning: 2025-08-26T11:51:21.817Z
Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.

Applied to files:

  • wled00/FX_fcn.cpp
📚 Learning: 2025-06-07T15:58:42.579Z
Learnt from: willmmiles
PR: wled/WLED#4712
File: wled00/FX_fcn.cpp:992-993
Timestamp: 2025-06-07T15:58:42.579Z
Learning: In WLED segments, the backing pixel buffer is always allocated to the full physical segment size, but effect rendering only uses a subset when mirror or grouping are enabled. For 2D segments, this subset is vWidth() * vHeight(), and for 1D segments it's vLength(). Color processing operations like fade_out should iterate over this rendering subset, not the full allocated buffer.

Applied to files:

  • wled00/FX_fcn.cpp
🧬 Code graph analysis (2)
wled00/json.cpp (1)
wled00/util.cpp (2)
  • getVal (66-83)
  • getVal (66-66)
wled00/FX_fcn.cpp (1)
wled00/colors.cpp (4)
  • color_add (28-61)
  • color_add (28-28)
  • color_blend (11-21)
  • color_blend (11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_compat)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_compat)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
🔇 Additional comments (2)
wled00/data/index.js (1)

828-829: UI option for Stencil (32) looks good.

Matches FX_fcn implementation and JSON handling.

wled00/FX_fcn.cpp (1)

1345-1572: Consolidation into segblend + push/mirror logic: solid.

The restructure reduces branching and enables Stencil; coordinate transforms are gated and reused cleanly.

Copy link
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: 0

♻️ Duplicate comments (1)
wled00/FX_fcn.cpp (1)

1324-1324: Subtract mode green-channel bug fixed

The green channel now subtracts bottom from top like the others. Thanks for addressing the prior issue.

🧹 Nitpick comments (3)
wled00/FX_fcn.cpp (3)

1317-1343: segblend() consolidation: clear and fast path

Single switch-based routine reduces indirections and keeps hot code tight. Consider forcing placement/inlining for hot path:

  • Add IRAM attribute and inline hint to avoid flash stalls in tight per-pixel loops.
-static uint32_t segblend(CRGBW tcol, CRGBW bcol, CRGBW bgcol, uint8_t blendMode) {
+IRAM_ATTR static inline uint32_t segblend(CRGBW tcol, CRGBW bcol, CRGBW bgcol, uint8_t blendMode) {

1339-1340: Stencil semantics: exact color match vs. tolerance

Equality on 32-bit color is correct for a pure stencil, but can be brittle if upstream effects introduce minor channel drift. If you see unexpected “holes,” consider (optional) channel-tolerance compare.

-    case 32: return tcol.color32 == bgcol.color32 ? bcol.color32 : tcol.color32; // Stencil
+    case 32: {
+      // Optional tolerance (e.g. 0..2) to avoid miss due to rounding/dithering
+      constexpr uint8_t tol = 0;
+      bool isBG =
+        (abs(int(tC.r)-int(bgcol.r)) <= tol) &&
+        (abs(int(tC.g)-int(bgcol.g)) <= tol) &&
+        (abs(int(tC.b)-int(bgcol.b)) <= tol) &&
+        (abs(int(tC.w)-int(bgcol.w)) <= tol);
+      return isBG ? bcol.color32 : tcol.color32;
+    }

1452-1463: Push offsets computed once; clean modulo handling

Efficient offset math and sign handling. Minor nit: make offset vars int to avoid implicit unsigned wrap on negatives before the modulo.

-    unsigned offsetX = (blendingStyle == BLEND_STYLE_PUSH_UP   || blendingStyle == BLEND_STYLE_PUSH_DOWN)  ? 0 : progInv * nCols / 0xFFFFU;
-    unsigned offsetY = (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_RIGHT) ? 0 : progInv * nRows / 0xFFFFU;
+    int offsetX = (blendingStyle == BLEND_STYLE_PUSH_UP   || blendingStyle == BLEND_STYLE_PUSH_DOWN)  ? 0 : int(progInv * nCols / 0xFFFFU);
+    int offsetY = (blendingStyle == BLEND_STYLE_PUSH_LEFT || blendingStyle == BLEND_STYLE_PUSH_RIGHT) ? 0 : int(progInv * nRows / 0xFFFFU);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9c6ab89 and bfc9c5e.

📒 Files selected for processing (1)
  • wled00/FX_fcn.cpp (8 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
wled00/{*.cpp,!(html_*)*.h}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use spaces (2 per level) for C++ source and header files

Files:

  • wled00/FX_fcn.cpp
🧠 Learnings (2)
📚 Learning: 2025-08-26T11:51:21.817Z
Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.

Applied to files:

  • wled00/FX_fcn.cpp
📚 Learning: 2025-06-07T15:58:42.579Z
Learnt from: willmmiles
PR: wled/WLED#4712
File: wled00/FX_fcn.cpp:992-993
Timestamp: 2025-06-07T15:58:42.579Z
Learning: In WLED segments, the backing pixel buffer is always allocated to the full physical segment size, but effect rendering only uses a subset when mirror or grouping are enabled. For 2D segments, this subset is vWidth() * vHeight(), and for 1D segments it's vLength(). Color processing operations like fade_out should iterate over this rendering subset, not the full allocated buffer.

Applied to files:

  • wled00/FX_fcn.cpp
🧬 Code graph analysis (1)
wled00/FX_fcn.cpp (1)
wled00/colors.cpp (4)
  • color_add (28-61)
  • color_add (28-28)
  • color_blend (11-21)
  • color_blend (11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
🔇 Additional comments (8)
wled00/FX_fcn.cpp (8)

371-374: Palette blend clamp looks good

The explicit 255 cap avoids pathological loops; safe and clear.


1345-1351: Propagating bgColor for stencil-enabled blending

Plumbing secondary color as background for modes that need it is correct; keeps segblend signature minimal.


1425-1447: 2D mirroring writes: order and bounds look correct

  • Single write + mirrored writes use segblend then opacity mix; consistent with 1D path.
  • CCT mirror updates are guarded and aligned with pixel writes. LGTM.

1455-1456: applyReverse/applyMirror flags gate transforms

Good micro-optimization to avoid unnecessary per-pixel conditionals when no reverse/transpose/mirror is active.


1474-1475: Push mapping before sampling old/new segments

Correct to apply push before sampling seg/segO so clipped/fade logic sees the shifted canvas consistently.


1492-1495: Reverse/transpose only when needed

Deferred transform after sampling avoids double-mapping. Clean and readable.


1518-1531: 1D path parity with 2D: segblend + opacity

Mirroring, wrapping with offset, and segblend usage match 2D semantics. Consistency reduces edge-case drift. LGTM.


1613-1615: Gamma application guard aligns with realtime override

Skipping gamma when ARLS requests it is correct. Note: per prior learnings, effect-level gamma shaping elsewhere is intentional—no changes needed here.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Aug 31, 2025

A few more comments:
Unfortunately the compact function pointer array had to go: while nice, compact and to my surprise still fast it did not allow blending to operate on a full color, which is necessary to determine if the color is equal to the background in the new mode. I made the new blend mode be number 32 so it is out of the way in case someone finds a smart way to move back to a function array, all my attempts at that failed: it either increased flash size or made it slow. The version I settled on here is faster than the original but it increases flash use due to inlining.

The use of CRGBW instead of uint32_t reduces flash size and increases speed, although how exactly eludes me: the two aliases tC / bC don't do anything but make code more compact to read and came from a version where I passed uint32_t and then cast that: if instead of aliases the arguments are used directly, codesize increases by 250bytes, no idea why having aliases makes the compiler happy.

one more peculiarity: when I did initial tests in a different branch (heap_handling), using "O2" optimization flag on blendSegment() the code size increased by about 1.5k, the speed gain was a few percent in FPS. If I do the same on the version of this PR, flash size increases by over 10k or even 20k on C3! Is there any simple explanation for that @willmmiles ?

@willmmiles
Copy link
Member

one more peculiarity: when I did initial tests in a different branch (heap_handling), using "O2" optimization flag on blendSegment() the code size increased by about 1.5k, the speed gain was a few percent in FPS. If I do the same on the version of this PR, flash size increases by over 10k or even 20k on C3! Is there any simple explanation for that @willmmiles ?

IIRC, often big changes in code size are caused by loop unrolling.

case 0: return tcol.color32; // Top
case 1: return bcol.color32; // Bottom
case 2: return color_add(tcol, bcol, false); // Add
case 3: c.r = _subtract(tC.r, bC.r); c.g = _subtract(tC.g, bC.g); c.b = _subtract(tC.b, bC.b); c.w = _subtract(tC.w, bC.w); break; // Subtract
Copy link
Member

Choose a reason for hiding this comment

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

A bit of a nitpick, but I'd recommend using an inline metafunction or a macro to do the expansion for each channel.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

that would clean up the code significantly, I will check how to do that here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ok done.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Aug 31, 2025

here is the new blending mode in action: Black Hole FX with blur enabled over a background:
image

Copy link
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: 0

🧹 Nitpick comments (9)
wled00/FX_fcn.cpp (9)

1295-1305: Minor naming/usage mismatch: “unused” helpers are compiled in.

_top, _bottom, _add are marked “function currently unused” and aren’t referenced. Consider removing or marking static inline constexpr behind a feature flag to save a few bytes.

- static uint8_t _top       (uint8_t a, uint8_t b) { return a; } // function currently unused
- static uint8_t _bottom    (uint8_t a, uint8_t b) { return b; } // function currently unused
- static uint8_t _add       (uint8_t a, uint8_t b) { unsigned t = a + b; return t > 255 ? 255 : t; } // function currently unused
+// #define WLED_DEBUG_BLEND_FUNCS 1
+#ifdef WLED_DEBUG_BLEND_FUNCS
+static inline constexpr uint8_t _top    (uint8_t a, uint8_t) { return a; }
+static inline constexpr uint8_t _bottom (uint8_t, uint8_t b) { return b; }
+static inline constexpr uint8_t _add    (uint8_t a, uint8_t b) { unsigned t = a + b; return t > 255 ? 255 : t; }
+#endif

1320-1321: Prefer an inline function over a macro for type-safety.

APPLY_BLEND relies on outer names (tC,bC,c) and hides types. An inline helper is safer and can still inline well.

-#define APPLY_BLEND(op) c.r = op(tC.r, bC.r); c.g = op(tC.g, bC.g); c.b = op(tC.b, bC.b); c.w = op(tC.w, bC.w)
+template<typename Op>
+static inline void applyBlendOp(CRGBW& out, const CRGBW& a, const CRGBW& b, Op op) {
+  out.r = op(a.r, b.r); out.g = op(a.g, b.g); out.b = op(a.b, b.b); out.w = op(a.w, b.w);
+}

Then:

-    case 3:  APPLY_BLEND(_subtract);    break;
+    case 3:  applyBlendOp(c, tC, bC, _subtract);    break;

1322-1348: segblend(): semantics and perf look solid; add force-inline and guard CRGBW conversions.

  • Good consolidation of blend modes; stencil (32) behaves as described.
  • To keep this hot path in IRAM and reduce call overhead, consider IRAM_ATTR and always_inline.
  • If color_add() only accepts uint32_t, relying on implicit CRGBW→u32 conversions is brittle across toolchains.
-static uint32_t segblend(CRGBW tcol, CRGBW bcol, CRGBW bgcol, uint8_t blendMode) {
+static inline __attribute__((always_inline)) IRAM_ATTR uint32_t segblend(CRGBW tcol, CRGBW bcol, CRGBW bgcol, uint8_t blendMode) {
   CRGBW tC(tcol); CRGBW bC(bcol); CRGBW c;
   switch (blendMode) {
-    case 2:  return color_add(tcol, bcol, false); // Add
+    case 2:  return color_add(tcol.color32, bcol.color32, false); // Add (explicit)

Verification ask: confirm CRGBW has implicit conversion where used, or make all calls explicit (.color32) to avoid accidental overload picks.


1342-1346: Stencil equality: consider optional tolerance to handle palette/gamma drift.

Exact 32-bit equality can fail if the top color is dithered or preprocessed. If needed later, add an optional “within N per-channel” compare guarded by a flag.

Would you like a tiny helper that compares channels within a tolerance (e.g., <=1) and is compiled out by default?


1430-1453: 2D mirroring path: index math and CCT writes look correct; avoid repeated segblend bottom fetch.

You recompute segblend(c, _pixels[idx*], ...) per mirror target (necessary because bottoms differ), but _pixels[idx*] is read twice per pixel (once for blend input, once again implicitly in color_blend). Micro-opt: read once per idx and reuse.

- _pixels[indx] = color_blend(_pixels[indx], segblend(c, _pixels[indx], bgColor, blendMode), o);
+ uint32_t base = _pixels[indx];
+ uint32_t mixed = segblend(c, base, bgColor, blendMode);
+ _pixels[indx] = color_blend(base, mixed, o);

Same pattern for idxMX/idxMY/idxMM.


1457-1468: Push offsets: clean and branchless; mask-based wrap is fine.

Minor readability nit: unify comment (“shift canvas”) once at the switch header.


1554-1563: On/Off transition workaround still relies on global bri/briT; add a brief comment.

Future readers may forget why FADE is special-cased vs others. One-line rationale would help.


1299-1319: Divide/Dodge/Burn edge cases are safe.

No divide-by-zero due to guards; semantics are consistent with your chosen definitions. Consider constexpr to help constant-fold.


1322-1348: Unit test coverage for bm=32 (stencil) and regressions on bm=3 suggested.

Given prior subtract-mode bug, add minimal tests (or a debug scene) to assert:

  • Subtract uses bottom-minus-top across all channels.
  • Stencil returns bottom when top==bg, otherwise top.

I can provide a small harness that feeds CRGBW tuples through segblend() and checks results; want me to drop that in test/?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bfc9c5e and ae76ae8.

📒 Files selected for processing (1)
  • wled00/FX_fcn.cpp (9 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
wled00/{*.cpp,!(html_*)*.h}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use spaces (2 per level) for C++ source and header files

Files:

  • wled00/FX_fcn.cpp
🧠 Learnings (3)
📓 Common learnings
Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.
📚 Learning: 2025-08-26T11:51:21.817Z
Learnt from: DedeHai
PR: wled/WLED#4798
File: wled00/FX.cpp:7531-7533
Timestamp: 2025-08-26T11:51:21.817Z
Learning: In WLED PR #4798, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.

Applied to files:

  • wled00/FX_fcn.cpp
📚 Learning: 2025-06-07T15:58:42.579Z
Learnt from: willmmiles
PR: wled/WLED#4712
File: wled00/FX_fcn.cpp:992-993
Timestamp: 2025-06-07T15:58:42.579Z
Learning: In WLED segments, the backing pixel buffer is always allocated to the full physical segment size, but effect rendering only uses a subset when mirror or grouping are enabled. For 2D segments, this subset is vWidth() * vHeight(), and for 1D segments it's vLength(). Color processing operations like fade_out should iterate over this rendering subset, not the full allocated buffer.

Applied to files:

  • wled00/FX_fcn.cpp
🧬 Code graph analysis (1)
wled00/FX_fcn.cpp (1)
wled00/colors.cpp (4)
  • color_add (28-61)
  • color_add (28-28)
  • color_blend (11-21)
  • color_blend (11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_compat)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_compat)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
🔇 Additional comments (6)
wled00/FX_fcn.cpp (6)

372-375: Good safety clamp on palette blends.

Covers overflow in transition palette blending; no behavior concerns.


1350-1391: blendSegment(): clipping window math is correct; single-pixel segments fallback to FADE is a nice edge-case fix.

No issues; good use of dw/dh and inverted ranges for OUTSIDE_IN.


1460-1516: Reverse/transpose gating only when needed: good for perf; group expansion bounds are correct.

Use of groupLength() and min(...) avoids overruns. Looks good.


1531-1536: 1D mirroring path mirrors before primary write; offset wrap is correct.

No functional concerns; mirrors respect offset and segment bounds.


1575-1577: Restore global blendingStyle: good defensive reset.

Prevents leakage into overlay passes.


1618-1620: Gamma guard avoids work on black; confirm realtime/gamma combo.

Condition c > 0 && !(realtimeMode && arlsDisableGammaCorrection) is correct. Verify that “pure white with CCT-only segments” still passes as >0 (it does).

If you want, I can craft a quick bench to compare FPS with/without the c > 0 guard on typical scenes.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Sep 3, 2025

changed to draft, this PR needs to wait until WIP on transparency is done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants