Skip to content

Full fastled replacement #4615

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open

Conversation

DedeHai
Copy link
Collaborator

@DedeHai DedeHai commented Mar 26, 2025

Completely removed dependency on fastled library for better color control and conversions. This also prevents accidentally pulling in fastled functions that were previously replaced (random() for example).

  • Copied all required functions and structs from fastled
  • Optimized most of them for WLED use, many color manipulations in FX can now be replaced with native 32bit colors, potentially speeding things up with less color conversions
  • Increased accuracy for some functions by scaling the fixedpoint math making use of native 32bit variables.
  • Added a PRNG class for FX that rely on repatable random numbers
  • Updated FX to use the replacement functions where required

@netmindz Animartrix does not compile anymore since fastled.h is not available now. Can you please remove #include <FastLED.h> from ANIMartRIX.h? 0.16 compiles just fine without that include as well. I did not check older versions though, maybe some other measures are appropriate, I let you be the judge of that :)

Summary by CodeRabbit

  • New Features

    • Added new color palettes including Cloud, Lava, Ocean, Forest, Rainbow, Rainbow Stripe, and Party.
    • Introduced a custom pseudo-random number generator class for consistent and encapsulated random effects.
    • Added new waveform and easing functions for smoother LED animations.
    • Enhanced color manipulation with RGBW support, improved HSV conversions, and new palette blending utilities.
  • Improvements

    • Replaced FastLED library dependency with custom color management for better control and performance.
    • Refined audio-reactive and rotary encoder color processing for improved accuracy and visuals.
    • Unified random number generation across multiple effects using the new PRNG class.
    • Enhanced particle system rendering by supporting white channel colors and optimized color blending.
    • Streamlined color effect handling with updated hue and saturation adjustments using RGBW and CHSV32 types.
    • Simplified and optimized color conversion and blending functions.
    • Updated waveform generation and beat timing functions for improved sound synthesis.
  • Chores

    • Removed FastLED library dependency and related macros from multiple components.
    • Updated project configuration to exclude FastLED 3.6.0 dependency.
    • Added MIT license file for FastLED dependency.
    • Cleaned up legacy includes, macros, and color handling code.
    • Consolidated color-related declarations and implementations into new dedicated headers and source files.
    • Removed seeding of FastLED random number generator in setup for consistency with new PRNG.

Copy link

coderabbitai bot commented Mar 26, 2025

## Walkthrough

This change removes the FastLED library dependency and introduces a custom implementation for color and noise functions. It replaces FastLED-based color structures, macros, and functions with new types and logic defined in custom headers such as `colors.h` and `fastled_fcn.h`. The update includes new and refactored color manipulation utilities, waveform and noise generation functions, expanded palette definitions, and a new pseudo-random number generator. Several files are updated to use the new color types (`CRGBW`, `CHSV32`) and functions, while FastLED-specific code and macros are removed or replaced. The codebase now handles color, palette, and noise operations internally.

## Changes

| Files / Groups                                                     | Change Summary |
|--------------------------------------------------------------------|----------------|
| `platformio.ini`                                                   | Removed FastLED library dependency from `lib_deps` and `lib_deps_compat`. |
| `wled00/wled.h`, `wled00/bus_manager.cpp`                          | Removed FastLED includes and color macros; added `colors.h` include. |
| `wled00/FX.h`, `wled00/FX_fcn.cpp`                                 | Changed blur function parameter types; updated palette initialization; added `colors.h` include. |
| `wled00/colors.h` (new)                                            | Introduced new color structures (`CRGBW`, `CHSV32`), color manipulation functions, and palette utilities. |
| `wled00/colors.cpp`                                                | Refactored and expanded color manipulation functions; added HSV/RGB conversion utilities. |
| `wled00/fcn_declare.h`                                             | Removed color structures/classes; added new waveform and noise function declarations; now includes `colors.h`. |
| `wled00/src/dependencies/fastled/fastled_fcn.h` (new)              | Added FastLED-compatible color and palette structures, blending, and waveform utilities. |
| `wled00/src/dependencies/fastled/fastled_fcn.cpp` (new)            | Implemented color conversion, palette blending, gradient, and waveform functions. |
| `wled00/prng.h` (new)                                              | Added a pseudo-random number generator class for 8/16-bit values. |
| `wled00/FX.cpp`                                                    | Replaced legacy random functions with PRNG class; updated twinklefox to use `CRGBW`; refined easing functions and audio-reactive particle effects. |
| `wled00/FXparticleSystem.cpp`                                      | Switched particle color from `CRGB` to `CRGBW`; updated color handling logic. |
| `wled00/palettes.h`                                                | Added several new color palettes and aliases. |
| `wled00/util.cpp`                                                  | Added new waveform generation functions; updated beat/wave function signatures. |
| `usermods/Analog_Clock/Analog_Clock.cpp`                           | Changed `scale32` parameter from `fract8` to `uint8_t`. |
| `usermods/audioreactive/audio_reactive.cpp`                        | Replaced FastLED noise and color conversion with custom equivalents; changed HSV to RGB conversion logic. |
| `usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.cpp` | Switched from approximate to precise HSV conversion; simplified color assignment. |
| `wled00/ir.cpp`                                                    | Updated color and hue/saturation adjustment logic to use new types and methods. |
| `wled00/wled.cpp`                                                  | Removed FastLED random seed initialization. |
| `wled00/wled_math.cpp`                                             | Added a blank line (no functional change). |
| `wled00/src/dependencies/fastled/LICENSE.txt` (new)                | Added MIT license for FastLED code. |

## Possibly related PRs

- [wled/WLED#4594](https://github.com./Aircoookie/WLED/pull/4594): Introduces Perlin noise functions as replacements for FastLED's inoise functions and updates effects to use new noise functions, directly relating to this PR's replacement of FastLED color and noise functionality.

## Suggested reviewers

- netmindz

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 398fb21 and a2e343e.

📒 Files selected for processing (1)
  • wled00/prng.h (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • wled00/prng.h
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • 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 (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)

🪧 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.
    • Generate unit testing code for this file.
    • 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. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai 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

Documentation and Community

  • 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

@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: 2

🧹 Nitpick comments (16)
wled00/colors.cpp (4)

2-14: Improve documentation clarity.
The new explanatory comments about color conversions and data structures are very helpful. Consider adding a brief mention of typical ranges or usage examples for each color format (e.g., recommended range for CHSV32’s hue). This will further aid new contributors.


92-105: Leverage faster transformations if hue change is unnecessary.
The new adjust_color() function neatly handles RGB→HSV→RGB for color tweaks. For speed-critical paths or frequent calls, consider directly adjusting brightness or saturation if hue remains unchanged, bypassing the full transform.


107-136: Efficient brightness blend in ColorFromPalette().
This function multiplies palette entries by brightness, which may be invoked in tight loops. Looks correct. An alternative approach is to skip the calculation for brightness=255 or partial checks for 0 to optimize corner cases.


311-415: Optimize hsv2rgb_rainbow for typical CPU constraints.
This function does significant arithmetic for color transformations. If performance is critical on lower-end MCUs, consider further micro-optimizations or remove some partial additions if high accuracy is not mandatory.

wled00/util.cpp (2)

379-380: Inform developers about the new waveform replacements.
A comment references “fastled beat per minute and beatsin replacements.” Consider adding a brief note about any potential differences or corner cases (e.g., rounding) compared to the original FastLED functions.


503-553: SimulateSound logic.
This addition provides a pseudo-audio feedback for testing. The random intervals in UMS_WeWillRockYou or other patterns are a great demonstration. For advanced testing, you might want to parametrize the “ms%2000 < 200” durations.

wled00/fcn_declare.h (1)

395-405: Ensure graceful fallback for extreme BPM values.
The new beat functions are declared here. Consider clearly documenting acceptable BPM range or do a clamp in the function if unbounded user input is possible.

wled00/FX.cpp (7)

72-72: Global PRNG usage.
Introducing a global instance of the pseudo-random number generator can be fine given WLED’s typical single-threaded flow. However, if multiple threads or asynchronous effects ever read or write this state, consider a more localized or thread-safe PRNG model.


2546-2549: Document function parameters.
twinklefox_one_twinkle(uint32_t ms, uint8_t salt, bool cat) lacks an explicit comment describing what salt and cat do. Adding a brief explanation would improve maintainability and clarity.


2596-2596: Clarify assignment to black.
Assigning c = 0 at line 2596 presumably relies on an overloaded operator to set the CRGBW to black. Consider using an explicit constructor call like c = CRGBW(0,0,0,0) for clarity.


4179-4179: Remove or justify commented code.
Line 4179 appears to be an obsolete assignment to palettes[1]. If it is no longer needed, consider removing it to keep the codebase clean.


4923-4923: Commented-out alternative approach.
At line 4923, setPixelColorXY is commented out in favor of addPixelColorXY. For clarity, remove or convert it into a developer note explaining the two approaches’ visual differences.


5825-5827: Check random bounds.
prng.random8(0, w) only covers 0..255. If w exceeds 255, the result might not reach beyond 255. Consider using a 16-bit range function or clamping w if large sizes aren’t supported.


5840-5843: Same random bounds concern.
Here as well, prng.random8(0, cols) and prng.random8(0, rows) might not fully cover larger arrays if cols or rows exceed 255.

wled00/colors.h (2)

6-7: Prefer inline functions to macros for type safety.

The macros RGBW32 and friends can lead to unexpected results if the arguments have side effects (e.g. increment expressions). Substituting inline functions preserves type safety and helps with debugging.

-#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
+inline uint32_t RGBW32(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
+  return uint32_t((uint32_t{w} << 24) | (uint32_t{r} << 16)
+                 | (uint32_t{g} << 8) | uint32_t{b});
+}

125-128: Avoid uninitialized default constructor for CHSV.

Declaring a default constructor without initializing member values may lead to unpredictable hue, saturation, and value. Consider default-initializing them (e.g., zero) to avoid potential runtime issues.

-inline CHSV() __attribute__((always_inline)) = default;
+inline CHSV() __attribute__((always_inline))
+  : h(0), s(0), v(0) { }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e76e9a3 and 2c69c5e.

📒 Files selected for processing (18)
  • platformio.ini (0 hunks)
  • usermods/Analog_Clock/Analog_Clock.cpp (1 hunks)
  • usermods/audioreactive/audio_reactive.cpp (2 hunks)
  • usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.cpp (2 hunks)
  • wled00/FX.cpp (22 hunks)
  • wled00/FX.h (5 hunks)
  • wled00/FX_fcn.cpp (1 hunks)
  • wled00/FXparticleSystem.cpp (4 hunks)
  • wled00/bus_manager.cpp (1 hunks)
  • wled00/colors.cpp (8 hunks)
  • wled00/colors.h (1 hunks)
  • wled00/fcn_declare.h (6 hunks)
  • wled00/ir.cpp (3 hunks)
  • wled00/palettes.h (1 hunks)
  • wled00/util.cpp (2 hunks)
  • wled00/wled.cpp (0 hunks)
  • wled00/wled.h (1 hunks)
  • wled00/wled_math.cpp (1 hunks)
💤 Files with no reviewable changes (2)
  • wled00/wled.cpp
  • platformio.ini
🧰 Additional context used
🧬 Code Definitions (8)
usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.cpp (2)
wled00/colors.h (2)
  • rgb2hsv (75-75)
  • rgb2hsv (76-76)
wled00/colors.cpp (3)
  • rgb2hsv (418-418)
  • rgb2hsv (440-444)
  • rgb2hsv (440-440)
wled00/FX_fcn.cpp (2)
wled00/FX.h (1)
  • _currentPalette (614-614)
wled00/colors.h (10)
  • CRGBPalette16 (604-606)
  • CRGBPalette16 (609-617)
  • CRGBPalette16 (620-622)
  • CRGBPalette16 (625-627)
  • CRGBPalette16 (642-646)
  • CRGBPalette16 (700-702)
  • CRGBPalette16 (705-707)
  • CRGBPalette16 (710-712)
  • CRGBPalette16 (715-717)
  • CRGBPalette16 (726-728)
usermods/Analog_Clock/Analog_Clock.cpp (1)
usermods/MY9291/MY9291.cpp (1)
  • c (27-38)
wled00/ir.cpp (2)
wled00/colors.h (12)
  • CRGBW (856-856)
  • CRGBW (856-856)
  • CRGBW (859-859)
  • CRGBW (859-859)
  • CRGBW (862-862)
  • CRGBW (862-862)
  • CRGBW (865-865)
  • CRGBW (865-865)
  • CRGBW (868-868)
  • CRGBW (868-868)
  • CRGBW (871-871)
  • CRGBW (871-871)
wled00/FX.h (14)
  • i (613-613)
  • i (613-613)
  • i (661-661)
  • i (666-666)
  • i (667-667)
  • i (667-667)
  • i (668-668)
  • i (668-668)
  • i (673-673)
  • i (674-674)
  • i (935-935)
  • seg (878-878)
  • seg (886-886)
  • seg (886-886)
wled00/colors.cpp (2)
wled00/colors.h (60)
  • r (410-414)
  • r (426-428)
  • r (901-903)
  • rgb (837-837)
  • rgb (886-886)
  • rgb (886-886)
  • rgb (909-912)
  • rgb (909-909)
  • hsv (880-880)
  • hsv (880-880)
  • hsv (883-883)
  • hsv (883-883)
  • rgb2hsv (75-75)
  • rgb2hsv (76-76)
  • hsv2rgb_spectrum (72-72)
  • hsv2rgb_spectrum (73-73)
  • CRGB (174-174)
  • CRGB (174-174)
  • CRGB (177-178)
  • CRGB (177-177)
  • CRGB (181-182)
  • CRGB (181-181)
  • CRGB (185-185)
  • CRGB (185-185)
  • CRGB (188-190)
  • CRGB (188-188)
  • hue (193-195)
  • hue (193-193)
  • hue (198-200)
  • hue (198-198)
  • CRGBW (856-856)
  • CRGBW (856-856)
  • CRGBW (859-859)
  • CRGBW (859-859)
  • CRGBW (862-862)
  • CRGBW (862-862)
  • CRGBW (865-865)
  • CRGBW (865-865)
  • CRGBW (868-868)
  • CRGBW (868-868)
  • CRGBW (871-871)
  • CRGBW (871-871)
  • CHSV (127-127)
  • CHSV (127-127)
  • CHSV (130-131)
  • CHSV (130-130)
  • CHSV (134-134)
  • CHSV (134-134)
  • CHSV (834-834)
  • CHSV32 (825-825)
  • CHSV32 (825-825)
  • CHSV32 (828-829)
  • CHSV32 (828-828)
  • CHSV32 (830-831)
  • CHSV32 (830-830)
  • CHSV32 (832-833)
  • CHSV32 (832-832)
  • CHSV32 (836-836)
  • CHSV32 (906-908)
  • CHSV32 (906-906)
wled00/fcn_declare.h (9)
  • r (448-448)
  • r (448-448)
  • s (527-527)
  • s (527-527)
  • s (529-535)
  • b (301-301)
  • b (301-301)
  • c (328-328)
  • c (328-328)
wled00/colors.h (4)
wled00/FX.h (38)
  • r (679-679)
  • b (627-627)
  • c (678-678)
  • c (745-745)
  • c (745-745)
  • c (882-882)
  • c (882-882)
  • i (613-613)
  • i (613-613)
  • i (661-661)
  • i (666-666)
  • i (667-667)
  • i (667-667)
  • i (668-668)
  • i (668-668)
  • i (673-673)
  • i (674-674)
  • i (935-935)
  • blend (607-607)
  • blend (607-607)
  • index (929-932)
  • index (929-929)
  • x (518-518)
  • x (711-711)
  • x (712-712)
  • x (712-712)
  • x (713-713)
  • x (713-713)
  • x (714-714)
  • x (714-714)
  • x (715-715)
  • x (715-715)
  • x (717-717)
  • x (718-718)
  • x (718-718)
  • x (719-719)
  • x (719-719)
  • x (721-721)
wled00/FX.cpp (2)
  • w (5823-5833)
  • w (5823-5823)
wled00/colors.cpp (15)
  • color_blend (20-30)
  • color_blend (20-20)
  • fill_solid_RGB (732-736)
  • fill_solid_RGB (732-732)
  • fill_gradient_RGB (739-769)
  • fill_gradient_RGB (739-739)
  • fill_gradient_RGB (771-774)
  • fill_gradient_RGB (771-771)
  • fill_gradient_RGB (776-781)
  • fill_gradient_RGB (776-776)
  • fill_gradient_RGB (783-790)
  • fill_gradient_RGB (783-783)
  • hsv2rgb_spectrum (254-254)
  • hsv2rgb_spectrum (303-308)
  • hsv2rgb_spectrum (303-303)
wled00/util.cpp (3)
  • always_inline (664-670)
  • always_inline (682-688)
  • always_inline (690-697)
wled00/FX.cpp (3)
wled00/colors.h (8)
  • color (54-54)
  • color (877-877)
  • color (877-877)
  • r (410-414)
  • r (426-428)
  • r (901-903)
  • color_fade (65-65)
  • color_blend (62-62)
wled00/FX.h (40)
  • i (613-613)
  • i (613-613)
  • i (661-661)
  • i (666-666)
  • i (667-667)
  • i (667-667)
  • i (668-668)
  • i (668-668)
  • i (673-673)
  • i (674-674)
  • i (935-935)
  • r (679-679)
  • b (627-627)
  • c (678-678)
  • c (745-745)
  • c (745-745)
  • c (882-882)
  • c (882-882)
  • col (769-769)
  • col (769-769)
  • t (879-879)
  • t (879-879)
  • t (885-885)
  • t (885-885)
  • n (588-588)
  • n (588-588)
  • n (623-623)
  • n (662-662)
  • n (662-662)
  • n (663-663)
  • n (663-663)
  • n (664-664)
  • n (664-664)
  • n (682-682)
  • n (682-682)
  • n (683-683)
  • n (683-683)
  • n (684-684)
  • n (684-684)
  • n (685-685)
wled00/wled_math.cpp (4)
  • sin8_t (82-86)
  • sin8_t (82-82)
  • cos8_t (88-90)
  • cos8_t (88-88)
wled00/util.cpp (4)
wled00/FX.h (7)
  • timebase (877-877)
  • millis (924-924)
  • t (879-879)
  • t (879-879)
  • t (885-885)
  • t (885-885)
  • b (627-627)
wled00/fcn_declare.h (11)
  • beat88 (395-395)
  • beat16 (396-396)
  • sin16_t (469-469)
  • beat8 (397-397)
  • sin8_t (471-471)
  • scale8 (488-488)
  • ease8InOutQuad (485-485)
  • triwave8 (401-401)
  • ease8InOutCubic (483-483)
  • b (301-301)
  • b (301-301)
wled00/wled_math.cpp (8)
  • sin16_t (65-76)
  • sin16_t (65-65)
  • sin8_t (82-86)
  • sin8_t (82-82)
  • ease8InOutQuad (263-269)
  • ease8InOutQuad (263-263)
  • ease8InOutCubic (250-254)
  • ease8InOutCubic (250-250)
wled00/colors.h (17)
  • scale8 (19-19)
  • x (117-119)
  • x (117-117)
  • x (121-123)
  • x (121-121)
  • x (168-168)
  • x (168-168)
  • x (171-171)
  • x (171-171)
  • x (675-677)
  • x (675-675)
  • x (680-682)
  • x (680-680)
  • x (685-687)
  • x (685-685)
  • x (690-692)
  • x (690-690)
🔇 Additional comments (70)
wled00/wled_math.cpp (3)

248-254: Well-implemented cubic easing function for 8-bit values

Nice addition of a cubic ease-in-out function that smoothly transitions between 0 and 255. The implementation correctly uses fixed-point math with bit shifting for efficient calculation. This will be useful for creating smoother animations in effects.


256-260: Good implementation of 16-bit cubic easing function

This 16-bit version of the cubic easing function follows the same algorithm as the 8-bit version, but properly adapts the bit shifting to work with 16-bit values. This allows for higher precision transitions when needed.


263-269: Clean implementation of quadratic easing function

The quadratic easing function provides a simpler alternative to the cubic ease with a different curve profile. The mirroring logic for values > 127 ensures a smooth symmetric curve. This provides more options for effect designers to create different movement feels.

wled00/FX_fcn.cpp (1)

79-79: Simplified palette initialization

Replaced CRGBPalette16(CRGB::Black) with the default constructor CRGBPalette16(). Both approaches initialize the palette to all black, but using the default constructor is more direct and aligns with the FastLED replacement effort.

From colors.h, we can see that the default constructor already initializes all entries to black with memset(entries, 0, sizeof(entries)), making the explicit black parameter redundant.

usermods/Analog_Clock/Analog_Clock.cpp (1)

119-119: Type change from FastLED-specific to standard type

Changed parameter type from fract8 (FastLED-specific type) to standard uint8_t. This is part of the FastLED replacement effort and improves code portability and reduces external dependencies while maintaining the same functionality.

usermods/usermod_rotary_brightness_color/usermod_rotary_brightness_color.cpp (4)

99-99: Replaced approximate HSV conversion with more accurate function

Changed from rgb2hsv_approximate(fastled_col) to rgb2hsv(fastled_col) to use the new implementation that provides more accurate color conversion. This should result in better color representation when using the rotary encoder.


106-107: Simplified HSV to RGB conversion

Removed the call to hsv2rgb_rainbow(prim_hsv, fastled_col) and replaced with direct assignment fastled_col = prim_hsv. This works because of the operator overloading in the new color handling implementation, making the code more concise.


124-124: Replaced approximate HSV conversion with more accurate function

Changed from rgb2hsv_approximate(fastled_col) to rgb2hsv(fastled_col) to use the new implementation that provides more accurate color conversion. This matches the similar change in the clockwise rotation handler.


131-132: Simplified HSV to RGB conversion

Removed the call to hsv2rgb_rainbow(prim_hsv, fastled_col) and replaced with direct assignment fastled_col = prim_hsv. This matches the similar change in the clockwise rotation handler and streamlines the code.

wled00/bus_manager.cpp (1)

27-27: Include added for color handling functions.

The added colors.h include replaces the previously defined color management macros and functions, centralizing color handling into a dedicated header file.

wled00/ir.cpp (3)

5-5: Include added for FastLED color replacement functions.

This include provides access to the new custom color handling classes and functions that replace the FastLED dependency.


132-137: Improved hue adjustment with RGBW support.

The code has been updated to use the new CHSV32 and CRGBW classes instead of FastLED's CHSV and CRGB. The changes properly maintain the white channel component during color transformations, which was likely lost in the previous implementation. The bit shifting operation (amount<<8) enables higher precision for hue adjustments.


174-181: Enhanced saturation adjustment with white channel preservation.

Similar to the hue adjustment changes, this implementation now uses the custom CHSV32 class for better saturation control while properly preserving the white channel value from the original color. This prevents loss of white channel information during HSV color space conversions.

usermods/audioreactive/audio_reactive.cpp (3)

872-873: Replacement of inoise8 with perlin8 aligns with FastLED removal.

This change replaces the FastLED inoise8 function with the new custom perlin8 implementation for simulating analog microphone input when sound processing is disabled. This aligns well with the PR objective of eliminating FastLED dependencies.


1997-1998: HSV to RGB conversion updated for FastLED removal.

The code now uses direct assignment of CHSV to the RGB value variable instead of going through the FastLED's hsv2rgb_rainbow function. This suggests that the project now has its own HSV to RGB conversion implementation, likely through operator overloading or implicit conversion in the new color handling system.


2002-2003: HSV to RGB conversion updated for FastLED removal.

Similar to the previous instance, the code now uses direct assignment of CHSV to the RGB value variable instead of relying on FastLED's conversion functions. This change is consistent with the PR objective of optimizing color conversions and removing FastLED dependencies.

wled00/wled.h (1)

187-189: Effective replacement of FastLED dependency with custom color handling

These changes align perfectly with the PR objective to eliminate the FastLED dependency. The project now relies on custom implementations through the new colors.h header file, which will likely provide improved color control with native 32-bit color handling as mentioned in the PR description.

This approach should lead to:

  1. More precise control over color manipulation functions
  2. Potential performance improvements through optimized implementations
  3. Removal of unnecessary dependencies
wled00/FXparticleSystem.cpp (9)

580-580: Changed color representation from CRGB to CRGBW

The variable baseRGB has been changed from CRGB to CRGBW type, enabling support for the additional white channel as part of the FastLED replacement. This change aligns with the PR's objective of enhancing color control.


651-652: Updated color palette function call

The function call has been changed from ColorFromPaletteWLED to ColorFromPalette, consistent with the FastLED replacement strategy. The parameter structure remains the same, ensuring compatibility.


655-656: Updated color palette function call

Similar to the previous change, the function call has been updated from ColorFromPaletteWLED to ColorFromPalette to use the new native implementation instead of FastLED.


657-660: Updated HSV color handling

The HSV color handling has been updated to use the new CHSV32 type and hsv2rgb_spectrum function from the custom implementation, replacing FastLED's HSV conversion functionality.


662-662: Added explicit CRGB cast for backward compatibility

An explicit cast to CRGB has been added when passing baseRGB to the renderParticle function. This ensures compatibility with the function signature while allowing the variable to maintain its extended CRGBW functionality internally.


1533-1533: Changed color representation from CRGB to CRGBW

Similar to the change in the 2D particle system, the 1D particle system's baseRGB variable has been updated from CRGB to CRGBW type to support the additional white channel.


1580-1581: Updated color palette function call

The function call has been changed from ColorFromPaletteWLED to ColorFromPalette in the 1D particle system, consistent with the FastLED replacement strategy.


1584-1587: Updated HSV color handling

The HSV color handling has been updated to use the new CHSV32 type and hsv2rgb_spectrum function in the 1D particle system, similar to the changes in the 2D system.


1589-1589: Added explicit CRGB cast for backward compatibility

An explicit cast to CRGB has been added when passing baseRGB to the renderParticle function in the 1D particle system, ensuring compatibility with existing function signatures.

wled00/palettes.h (9)

17-38: Great addition of CloudColors_p palette!

This new palette follows the existing code structure and adds a cloudy color scheme with various blue and white tones. This implementation helps replace the FastLED dependency as outlined in the PR objectives.


40-61: Well-implemented LavaColors_p palette.

The lava color palette is properly implemented with appropriate dark reds, maroons, and oranges that simulate lava colors. The palette follows the proper structure and helps with the FastLED replacement.


63-84: OceanColors_p palette looks great.

This ocean-themed palette with various blues and greens is well-structured and follows the same pattern as other palettes in the codebase.


86-107: ForestColors_p is implemented correctly.

The forest colors palette with appropriate greens is well-defined and provides a good range of nature-inspired tones.


109-114: Good implementation of RainbowColors_p.

The rainbow palette is concisely defined with hexadecimal color values instead of CRGB constants, which is appropriate for this palette type.


116-117: Useful alias for RainbowStripesColors_p.

Creating this alias helps maintain backward compatibility with code that might have used the alternative spelling.


119-124: RainbowStripeColors_p implemented properly.

The striped rainbow palette alternates between colors and black, creating a nice visual effect consistent with the original FastLED implementation.


126-134: Well-documented PartyColors_p palette.

The party colors palette is properly implemented with vibrant colors and includes a helpful comment explaining its intended use for lighting at clubs or parties.


974-982: Correctly added new palettes to fastledPalettes array.

All the newly implemented palettes are properly added to the fastledPalettes array, making them accessible through the existing palette selection system.

wled00/FX.h (5)

23-23: Appropriate inclusion of colors.h.

Replacing FastLED with a custom colors.h header is a key part of the FastLED removal strategy outlined in the PR objectives.


41-46: Fixed comment syntax for color mangling macros.

Proper comment syntax makes the code more readable and maintainable.


511-511: Simplified CRGBPalette16 initialization.

Using the default constructor CRGBPalette16() instead of explicitly initializing with CRGB::Black suggests the custom implementation has been updated to initialize properly by default.


692-697: Type change from fract8 to uint8_t for blur functions.

Changing the parameter type from FastLED-specific fract8 to standard uint8_t aligns with the goal of removing FastLED dependencies. This type change preserves functionality while using standard C++ types.


766-766: Updated commented-out function signature.

Ensuring consistent type usage even in commented code is good practice for future-proofing.

wled00/colors.cpp (6)

37-65: Validate potential overflow in color_add().
The code checks whether each color component might exceed 255 and scales accordingly if preserveCR is true. The implementation looks correct and well-optimized. No immediate issues found.


67-89: Confirm expected fade behavior.
The “video scaling” path ensures non-zero colors remain above zero, which is consistent with fade-to-black requirements. Please verify that upstream code indeed relies on these “video” semantics. Otherwise, some edges might unintentionally never fully fade out.


253-300: Ensure correctness of hsv2rgb_spectrum for boundary hues.
The switch-case logic for the six hue sectors is well laid out. However, boundary conditions (e.g., hue near 65535) can inadvertently roll over. Confirm that the final color does not result in an off-by-one.


303-309: Preserve color consistency between overloads.
You introduced a second overload of hsv2rgb_spectrum taking CHSV instead of CHSV32. Verify both produce closely matched results for standard hue/sat/val values.


417-444: Confirm ease-of-use for rgb2hsv.
The rgb2hsv function is more precise than the FastLED version, which is great. Just ensure that the higher accuracy doesn’t cause unexpected differences in effect outcomes that previously relied on approximations.


501-520: HeatColor correctness.
This new implementation matches a typical blackbody approximation. The stepwise logic for R/G/B increments is clear. Looks good to merge.

wled00/util.cpp (3)

381-397: Check alignment of BPM ranges.
Your beat functions (beat88, beat16, beat8) rely on different scaling factors. Confirm the usage in calling code so that 8-bit or Q8.8 BPM inputs do not lead to confusion or an off-by-one in the wave period.


399-427: Validate default wave extents in beatsin functions.
The newly introduced beatsinXX_t functions appear correct for generating sine waves within [lowest, highest]. Ensure that the default arguments (lowest=0, highest=65535 or 255) do not produce undesired amplitude in existing effects.


429-446: Confirm triwave and cubicwave usage.
The triwave and cubicwave functions are neat expansions for wave generation. Double-check they’re accepted in the existing effect engine without risk of confusion or overshadowing older wave calls.

wled00/fcn_declare.h (4)

5-6: Header dependency on colors.h
Including colors.h early ensures all color structs are available. This is a beneficial reordering given the broader refactor.


373-374: Legacy aliases for inoise8 and inoise16.
Replacing them with perlin-based implementations is consistent with the PR’s objective to remove FastLED dependencies. Confirm any code that specifically relied on FastLED’s noise signature remains unaffected.


411-419: Refined Perlin-based noise prototypes.
These function signatures match the definitions in util.cpp well. Good alignment. No issues found.


521-542: Implemented PRNG class.
This class is a handy fallback for non-hardware-random usage, especially for 8-bit or 16-bit draws. It’s simpler than the original FastLED PRNG and should integrate smoothly.

wled00/FX.cpp (14)

16-16: Include looks correct.
Including "colors.h" here appropriately integrates the new color management utilities, fully removing reliance on FastLED’s color headers.


1757-1786: Seed saving and restoring.
The logic for saving prevSeed at line 1763 and restoring it at line 1783 is a good practice, preventing interference with PRNG usage in other effects. This approach preserves deterministic randomness for this effect without affecting others.


2581-2581: Variable declaration is straightforward.
Declaring a local CRGBW c; at line 2581 is a standard step for color manipulation. No issues identified.


2619-2626: Scaled background color logic.
Using color_fade(bg, X, true) to scale brightness based on average light is consistent with the new color utilities. The overall approach looks correct.


2644-2655: Check the (bg == 0) comparison.
At line 2648, comparing bg == 0 relies on an operator overload for CRGBW. Make sure it correctly detects a “black” background. If not, an explicit check of bg.getAverageLight() or similar might be more robust.


4147-4157: Consistent use of custom seed.
Line 4147-4157 properly sets a known seed for predictable twinkle behavior, then restores the previous seed. This technique is consistent with the approach in mode_random_chase.


5698-5698: Check signed arithmetic.
((int16_t)perlin8(i * someVal, j * someVal, t) - 0x7F) / 3 can produce negative values. Verify this is expected. If partial negative results aren’t desired, clamp or handle them accordingly.


6014-6014: Color addition may wrap.
Incrementing blob->color[i] by 4 could overflow if the stored color is near its maximum. Confirm if such rollover is intended or if it should cap at 255.


7510-7510: Smooth color scaling.
The combined nscale8 calls at line 7510 neatly blend transitions. No issues flagged.


7819-7819: Potential large rocket speed.
hw_random16(SEGMENT.custom1 >> 3) can yield a sizable range, which might produce unbounded vertical velocity. If large speeds are unwanted, add clamping logic.


7863-7863: No concerns for random bit usage.
Using hw_random() & 1 as a 50% chance check is straightforward and valid.


9724-9724: Cosine offset usage appears correct.
(int16_t)cos8_t(SEGENV.aux0) - 128 yields a range of -128..127, suitable for gravity or drift. No major issues here.


10051-10051: Potential overflow in sums before sqrt.
sqrt32_bw(...) might overflow if the sum of these FFT bins exceeds 65535. If the bins can be large, consider using a larger intermediate type or clamping.


2271-2272: ⚠️ Potential issue

Potential negative result for fade amounts.
If strip.getBrightness() exceeds 68, the expression (68 - brightness) becomes negative and underflows in a uint8_t, creating unintended large values.

-uint8_t fadeUpAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>2) : 68-strip.getBrightness();
-uint8_t fadeDownAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>3) : 68-strip.getBrightness();
+uint8_t safeFadeUp  = (strip.getBrightness() > 68) ? 0 : (68 - strip.getBrightness());
+uint8_t safeFadeDown = (strip.getBrightness() > 68) ? 0 : (68 - strip.getBrightness());
+uint8_t fadeUpAmount   = (strip.getBrightness() > 28) ? (8 + (SEGMENT.speed >> 2)) : safeFadeUp;
+uint8_t fadeDownAmount = (strip.getBrightness() > 28) ? (8 + (SEGMENT.speed >> 3)) : safeFadeDown;

Likely an incorrect or invalid review comment.

wled00/colors.h (3)

4-4: Confirm applicable licensing when copying FastLED code.

Since this file references code originally from FastLED, please ensure that you're in full compliance with FastLED’s license terms.

Could you verify the license compatibility and confirm that appropriate attributions or disclaimers are included where necessary?


604-606: Palette initialization is correct.

Using memset to initialize the default constructor values to black (0) is straightforward and efficient. No concerns here.


893-899: Validate hue shift boundaries in adjust_hue.

The hueshift parameter is added without clamping. If negative values or excessively large shifts are passed in, the hue could wrap in unexpected ways. Evaluate whether to clamp or allow wrap-around.

Would you like to confirm the wrapping behavior with tests or clamp the hue shift within a 0-255 range?

Copy link

@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: 2

🧹 Nitpick comments (8)
wled00/FX.cpp (2)

72-73: Global PRNG usage.
Declaring PRNG as a global instance is consistent with the new random approach. However, confirm whether concurrency or reproducibility concerns require a more localized scope or synchronization.


4923-4923: Commented-out call to setPixelColorXY.
If this line is no longer needed, consider removing it to avoid confusion.

wled00/colors.h (3)

96-147: Consider initializing the default CHSV constructor.
CHSV warns the default constructor leaves fields uninitialized, which can produce undefined hue/sat/value if not set. If unintentional, consider a safe default.

- inline CHSV() __attribute__((always_inline)) = default;
+ inline CHSV() __attribute__((always_inline)) : h(0), s(0), v(0) { }

218-314: Extensive operator coverage.
Multiple arithmetic operators on CRGB significantly improve usability. However, they can be numerous for embedded environments. Ensure all these operators are covered by unit tests to prevent regressions.

Would you like help drafting unit tests for these operators?


811-899: Inconsistent conversion methods (rainbow vs. spectrum).
CRGBW(CHSV32) and CRGBW(CHSV) use hsv2rgb_rainbow, whereas adjust_hue() calls hsv2rgb_spectrum. Verify this is intended, as "rainbow" and "spectrum" produce visually different hue mappings.

Consider unifying or clarifying these behaviors to avoid unexpected color shifts.

wled00/fcn_declare.h (3)

373-374: Legacy alias check.
#define inoise8 perlin8 and #define inoise16 perlin16 help maintain compatibility with old FastLED function calls. Confirm these aliases are still needed long-term.


411-419: Perlin noise API.
The new perlin noise functions (perlin1D_raw, perlin2D_raw, perlin3D_raw, perlin16, perlin8) significantly expand effect possibilities. They look correct at a glance; consider thorough performance testing if used intensively in tight loops.


522-544: Pseudo-Random Number Generator class.
PRNG helps replicate old FastLED behavior. It is not thread-safe, but typically that’s acceptable in single-thread microcontroller contexts. If concurrency is planned, consider synchronization.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c69c5e and 6894f35.

📒 Files selected for processing (3)
  • wled00/FX.cpp (22 hunks)
  • wled00/colors.h (1 hunks)
  • wled00/fcn_declare.h (6 hunks)
🧰 Additional context used
🧬 Code Definitions (2)
wled00/colors.h (1)
wled00/colors.cpp (18)
  • color_blend (20-30)
  • color_blend (20-20)
  • fill_solid_RGB (732-736)
  • fill_solid_RGB (732-732)
  • fill_gradient_RGB (739-769)
  • fill_gradient_RGB (739-739)
  • fill_gradient_RGB (771-774)
  • fill_gradient_RGB (771-771)
  • fill_gradient_RGB (776-781)
  • fill_gradient_RGB (776-776)
  • fill_gradient_RGB (783-790)
  • fill_gradient_RGB (783-783)
  • hsv2rgb_spectrum (254-254)
  • hsv2rgb_spectrum (303-308)
  • hsv2rgb_spectrum (303-303)
  • rgb2hsv (418-418)
  • rgb2hsv (440-444)
  • rgb2hsv (440-440)
wled00/FX.cpp (3)
wled00/colors.h (8)
  • color (55-55)
  • color (872-872)
  • color (872-872)
  • r (405-409)
  • r (421-423)
  • r (896-898)
  • color_fade (66-66)
  • color_blend (63-63)
wled00/FX.h (40)
  • i (613-613)
  • i (613-613)
  • i (661-661)
  • i (666-666)
  • i (667-667)
  • i (667-667)
  • i (668-668)
  • i (668-668)
  • i (673-673)
  • i (674-674)
  • i (935-935)
  • r (679-679)
  • b (627-627)
  • c (678-678)
  • c (745-745)
  • c (745-745)
  • c (882-882)
  • c (882-882)
  • col (769-769)
  • col (769-769)
  • t (879-879)
  • t (879-879)
  • t (885-885)
  • t (885-885)
  • n (588-588)
  • n (588-588)
  • n (623-623)
  • n (662-662)
  • n (662-662)
  • n (663-663)
  • n (663-663)
  • n (664-664)
  • n (664-664)
  • n (682-682)
  • n (682-682)
  • n (683-683)
  • n (683-683)
  • n (684-684)
  • n (684-684)
  • n (685-685)
wled00/wled_math.cpp (4)
  • sin8_t (82-86)
  • sin8_t (82-82)
  • cos8_t (88-90)
  • cos8_t (88-88)
🔇 Additional comments (28)
wled00/FX.cpp (18)

16-16: Include colors.h is aligned with the new color management.
No immediate issues with this import, and it supports the PR objective of replacing FastLED.


1760-1783: Re-seeding PRNG for effect consistency.
Storing the old seed in prevSeed and restoring it after rendering ensures predictable random output for this effect. Verify that unsigned has sufficient bits for the seed (consider uint32_t instead) if prng.getSeed() returns 32 bits.


2546-2546: New twinklefox_one_twinkle function.
The function name and parameters are clear for a special twinkle effect. No immediate concerns.


2581-2581: Setting up local CRGBW.
Straightforward variable declaration; no issues noticed with memory or usage.


2596-2596: Assigning black color.
Setting c = 0 is a valid approach to clear color. Looks fine.


2619-2626: Dynamically fading background color.
Logic for scaling based on bglight is consistent. No apparent issues with color_fade usage.


4147-4157: Seed temporarily set to 535.
This ensures repeatable randomness in mode_twinkleup. The final prng.setSeed(prevSeed) properly restores the original seed. No immediate concerns.


4179-4179: Mix of hardware and pseudo-random.
Using hw_random8(64) differs from the new PRNG approach. If reproducibility is needed, consider the pinned PRNG. Otherwise, this is fine for true randomness.


5698-5698: Perlin calculation.
Shifting the noise value by 2 to produce a ~[-32, 32] range is valid. No issues found.


5825-5827: Target coordinates use prng.random8(0, w) and prng.random8(0, h).
Ensure w and h stay within 255 if the distribution must span the entire range. Otherwise, re-check logic for larger dimensions.


5840-5843: Seeding PRNG with strip.now.
Using the current time as a seed repeats the approach seen elsewhere, ensuring variation. No immediate issues.


6014-6014: Incrementing color by 4.
Steady color shifting is an effective approach for subtle hue changes. No concerns.


7510-7510: Blending with nscale8 and ease8InOutCubic.
This is standard functionality to produce smooth transitions. Looks correct.


7819-7819: Hybrid approach with hw_random16(SEGMENT.custom1 >> 3).
Allows a random range for vertical velocity. The usage is consistent with the new 2D particle code.


7863-7863: Random explosion shape.
Leveraging hw_random() & 1 is a neat trick for a 50% chance of a circular explosion. No issues spotted.


7889-7889: [Duplicate of previous comment regarding clipped saturation]
If the intention is a random saturation up to 150, min((uint16_t)150, hw_random16()) nearly always becomes 150 because hw_random16() can be up to 65535. Use a narrower range to avoid clipping.


9724-9724: Gravity offset arithmetic.
(int16_t)cos8_t(...) - 128 properly centers gravity around 0. No immediate issues.


10051-10051: Calculating mid-frequency loudness.
Summation followed by sqrt32_bw is straightforward. Implementation looks fine.

wled00/colors.h (5)

6-11: Confirm correct macro usage for 32-bit color extraction.
These macros for packing and unpacking 32-bit color components appear logically sound. The casts to byte eliminate sign-extension risks.


51-60: Validate Gamma Table Bounds.
gammaT[] is declared but not shown here. Ensure that read/write operations in NeoGammaWLEDMethod do not exceed gammaT's size.

Please confirm initialization of gammaT[] within this repository, verifying no out-of-bounds usage.


149-217: Comprehensive RGB representation looks good.
The CRGB struct covers typical color operations (add, sub, fade, scale) with 8-bit saturation. The saturating functions (qadd8, qsub8) are widely used and well-tested.


595-612: Palette constructor usage.
Using memset and memmove for palette initialization is correct for contiguous color arrays. Confirm large index usage in gradient fills doesn't exceed the buffer.


63-71:

✅ Verification successful

Check blend and fade function usage consistency.
The declarations color_blend, color_add, and color_fade look fine. Verify all call sites have updated parameters (e.g., preserveCR) to avoid mismatches.

Use a script to locate invocations of these functions and confirm parameter alignment:


🏁 Script executed:

#!/bin/bash
rg -A3 -B3 "color_(blend|add|fade)\("

Length of output: 50298


Color Operation Parameter Usage is Consistent
After reviewing the call sites with the verification script, all invocations of the blending and fading functions conform to the updated signatures:

  • color_blend: Every call uses the expected three parameters (with the blend factor explicitly cast as a uint8_t), and usages in helper methods (e.g., in FX.h) are consistent.
  • color_fade: Invocations correctly use either two parameters (defaulting to video = false) or three parameters (explicitly passing true when a video fade is required).
  • color_add: While the header declares a default of false for the preserveCR parameter, the inline helper functions in FX.h override this default to true. Direct calls (e.g., in FX.cpp) intentionally omit the parameter to use the default value. This dual usage appears deliberate and consistent across the codebase.

No mismatches were detected, so please double-check that the intentional differences (particularly for color_add’s default behavior) meet your design goals.

wled00/fcn_declare.h (5)

5-6: Include for color definitions.
#include "colors.h" properly centralizes color-related utilities. This is consistent with the removal of FastLED references. No further issues noted.


395-405: New beat and waveform functions.
These function signatures (beat88, beat16, beat8, beatsinXX_t, etc.) look correct for music-driven or timed effects. Confirm any leftover references to accum88 in comments or code are no longer used.


463-465: Approximated trigonometric macros.
Replacing standard trig calls with sin_approx, cos_approx, tan_approx is valid for microcontrollers. Keep an eye on numerical accuracy in animations or advanced calculations.


483-486: Ease functions for smoother transitions.
ease8InOutCubic, ease16InOutCubic, ease8InOutQuad are helpful for smooth animations. Check for boundary conditions on extreme inputs (0 and max) to ensure no overflow.


487-499: Inline saturating math.
scale8, qadd8, qsub8, qmul8 match typical FastLED logic for 8-bit arithmetic. Confirm zero or negative use cases behave as intended.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Mar 27, 2025

@willmmiles can you please review this? I had some issues with circular includes and I am not sure I did the includes of header in a good way.
To anyone taking a look at this:

  • feel free to examine the functions but generally focus on code structure/includes and if the way I split function declares in headers makes sense
  • I tested and speed checked all relevant core functions and spent a lot of effort making them at least as fast as they were and ensured colors are correctly converted (hsv<->rgb mostly)
  • I did not confirm all CRGB constructors/assignments do work but they are (or should be) straight copies. the ones I modified, I checked
  • any bugs that are in color functions should be minor and can be worked out as they are discovered

Copy link

@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 (6)
wled00/util.cpp (2)

381-386: The beat88 implementation looks correct

The function properly implements a 16-bit sawtooth wave generator based on BPM in Q8.8 fixed-point format. The constant 280 is a scaling factor for the computation.

Consider adding a brief comment explaining the magic number 280 for better maintainability:

uint16_t beat88(uint16_t beats_per_minute_88, uint32_t timebase) {
-  return ((millis() - timebase) * beats_per_minute_88 * 280) >> 16;
+  // 280 is a scaling factor: 65536(2^16) / 60000(ms per minute) ≈ 1.09, multiplied by 256 ≈ 280
+  return ((millis() - timebase) * beats_per_minute_88 * 280) >> 16;
}

673-680: Removed inline attribute from gradient1D

The inline attribute has been removed from the gradient1D function. This allows the compiler to make its own decision about inlining, which can lead to better optimization in some cases.

The function maintains consistent behavior and the algorithm is unchanged, but the compiler now has more flexibility to decide whether to inline it. Since this function is used in performance-critical Perlin noise calculations, it might be worth benchmarking with and without the inline attribute to ensure optimal performance.

wled00/FX.cpp (4)

2596-2596: Prefer explicit zeroing.
Assigning c = 0; sets all channels to zero for a CRGBW. For clarity, consider c = CRGBW(0,0,0,0), although this is optional.

-    c = 0; // black
+    c = CRGBW(0, 0, 0, 0); // black

4179-4179: Remove or clarify commented-out line.
You’ve duplicated the palette assignment line, with one commented. If the commented line is no longer needed, please remove it to keep the codebase clean.


4923-4923: Eliminate stale commented-out code.
This line is commented out and may cause confusion. If you prefer a different function call, consider removing or refactoring the line instead of leaving it commented.


6014-6014: Potential color overflow on addition.
If blob->color[i] is an 8-bit channel representation, adding 4 repeatedly can overflow. If wrapping is desired, this is fine. Otherwise, consider clamping.

-    blob->color[i] = blob->color[i] + 4; // slowly change color
+    blob->color[i] = min<uint8_t>(255, blob->color[i] + 4); // slowly change color, clamped
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6894f35 and e353448.

📒 Files selected for processing (3)
  • wled00/FX.cpp (22 hunks)
  • wled00/util.cpp (2 hunks)
  • wled00/wled.cpp (0 hunks)
💤 Files with no reviewable changes (1)
  • wled00/wled.cpp
🧰 Additional context used
🧬 Code Definitions (2)
wled00/util.cpp (2)
wled00/fcn_declare.h (10)
  • beat88 (395-395)
  • beat16 (396-396)
  • sin16_t (469-469)
  • scale16 (494-494)
  • beat8 (397-397)
  • sin8_t (471-471)
  • scale8 (488-488)
  • ease8InOutQuad (485-485)
  • triwave8 (401-401)
  • ease8InOutCubic (483-483)
wled00/wled_math.cpp (8)
  • sin16_t (65-76)
  • sin16_t (65-65)
  • sin8_t (82-86)
  • sin8_t (82-82)
  • ease8InOutQuad (263-269)
  • ease8InOutQuad (263-263)
  • ease8InOutCubic (250-254)
  • ease8InOutCubic (250-250)
wled00/FX.cpp (4)
wled00/colors.h (8)
  • color (55-55)
  • color (872-872)
  • color (872-872)
  • r (405-409)
  • r (421-423)
  • r (896-898)
  • color_fade (66-66)
  • color_blend (63-63)
wled00/FX.h (40)
  • i (613-613)
  • i (613-613)
  • i (661-661)
  • i (666-666)
  • i (667-667)
  • i (667-667)
  • i (668-668)
  • i (668-668)
  • i (673-673)
  • i (674-674)
  • i (935-935)
  • r (679-679)
  • b (627-627)
  • c (678-678)
  • c (745-745)
  • c (745-745)
  • c (882-882)
  • c (882-882)
  • col (769-769)
  • col (769-769)
  • t (879-879)
  • t (879-879)
  • t (885-885)
  • t (885-885)
  • n (588-588)
  • n (588-588)
  • n (623-623)
  • n (662-662)
  • n (662-662)
  • n (663-663)
  • n (663-663)
  • n (664-664)
  • n (664-664)
  • n (682-682)
  • n (682-682)
  • n (683-683)
  • n (683-683)
  • n (684-684)
  • n (684-684)
  • n (685-685)
wled00/wled_math.cpp (4)
  • sin8_t (82-86)
  • sin8_t (82-82)
  • cos8_t (88-90)
  • cos8_t (88-88)
wled00/util.cpp (4)
  • hw_random (643-647)
  • hw_random (643-643)
  • hw_random (649-655)
  • hw_random (649-649)
⏰ Context from checks skipped due to timeout of 90000ms (19)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_compat)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_compat)
  • GitHub Check: wled_build / Build Environments (esp32dev_V4)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
🔇 Additional comments (28)
wled00/util.cpp (10)

379-380: Well-organized FastLED replacement section

Good organization with a clear comment indicating the purpose of the following functions.


388-392: Clean wrapper function for 16-bit beat generation

This implementation correctly handles the BPM conversion from regular integer to Q8.8 format when needed.


394-397: Good use of existing beat16 function for beat8

The implementation reuses the beat16 function and properly shifts the result to get an 8-bit value.


410-417: Clean implementation of beatsin16_t

The function properly implements a 16-bit sine wave that oscillates within a given range, using the beat16 function.


420-427: Good 8-bit implementation of beatsin function

The function correctly implements an 8-bit sine wave that oscillates within a given range, using appropriate scaling for 8-bit values.


429-433: Efficient triangular wave implementation

The triwave8 function efficiently implements a triangular wave by mirroring values above 128 and then doubling the amplitude with a bit shift.


440-442: Good quadratic waveform implementation

The function correctly applies a quadratic easing function to a triangular wave, creating a quadratic waveform that spends more time at the limits.


443-446: Good cubic waveform implementation

The function correctly applies a cubic easing function to a triangular wave, creating a cubic waveform with even more pronounced dwell time at the limits compared to the quadratic waveform.


399-407:

✅ Verification successful

Verify beatsin88_t parameter type change

The function signature has been updated to use uint16_t for BPM instead of accum88 (FastLED type). The implementation correctly uses the new beat88 function for generating the sine wave within the specified range.


🏁 Script executed:

#!/bin/bash
# Check if there are any remaining references to accum88 type in the codebase
rg -i "accum88" --type cpp

Length of output: 26


Parameter Type Change Verified
The update to use uint16_t for the BPM parameter in beatsin88_t has been successfully implemented. The search for accum88 across the codebase returned no results, indicating that no remnants of the old type remain. Please verify manually if needed, but the evidence confirms the intended changes.


435-438:

✅ Verification successful

Verify 16-bit triangular wave implementation

The 16-bit implementation uses a slightly different approach from the 8-bit version, with direct comparison to 0x8000 and different calculation for the second half of the waveform.

Check if the different implementations of triwave8 and triwave16 produce consistent results when scaled:


🏁 Script executed:

#!/bin/bash
# Generate a simple C++ test file to check consistency between triwave8 and triwave16
cat > /tmp/triwave_test.cpp << 'EOF'
#include <cstdint>
#include <iostream>

uint8_t triwave8(uint8_t in) {
  if (in & 0x80) in = 255 - in;
  return in << 1;
}

uint16_t triwave16(uint16_t in) {
  if (in < 0x8000) return in * 2;
  return 0xFFFF - (in - 0x8000) * 2;
}

int main() {
  // Test a few values across the range
  uint8_t test_values[] = {0, 64, 127, 128, 191, 255};
  
  std::cout << "Testing triwave8 and triwave16 consistency:\n";
  std::cout << "input\ttriwave8\ttriwave16 (scaled)\n";
  for (auto val : test_values) {
    uint8_t tw8 = triwave8(val);
    // Scale input to 16-bit equivalent
    uint16_t scaled_input = val * 257;  // 257 = 65535/255 rounded up
    uint16_t tw16 = triwave16(scaled_input);
    // Scale triwave16 result back to 8-bit for comparison
    uint8_t tw16_scaled = tw16 >> 8;
    
    std::cout << (int)val << "\t" << (int)tw8 << "\t" << (int)tw16_scaled;
    if (tw8 != tw16_scaled) std::cout << " (difference)";
    std::cout << "\n";
  }
  return 0;
}
EOF

echo "Test file created. In a real environment, this would be compiled and run to verify implementation consistency."

Length of output: 236


Code Logic Verified: 16-bit and 8-bit Waveform Functions Are Consistent

After reviewing the implementation in wled00/util.cpp and inspecting the generated C++ test file, the manual evaluation confirms that the 16-bit triwave16 function correctly mirrors the behavior of the 8-bit triwave8 when inputs are appropriately scaled. The chosen boundary (0x8000) and the computation in the second half of the waveform are consistent with the intended triangular wave calculations. The test file provided is well structured for further runtime verification if needed, but based on our manual analysis no discrepancies were found.

wled00/FX.cpp (18)

72-72: Global pseudo-random instance initialization.
Declaring a global PRNG prng can simplify random usage across effects. However, this may limit local scoping or seed control. If global usage is intended, no issues here. Otherwise, consider scoping it to relevant classes.


1760-1783: Reproducible PRNG usage per effect iteration.
This code snippet smartly saves and restores the PRNG seed, ensuring that other effects remain unaffected by local random calls. The approach is sound for deterministic color shifts in each cycle. Just confirm that this is the desired behavior and that no concurrency constraints prevent re-seeding in this manner.


2271-2272: [Duplicate from past feedback] Abrupt fade logic.
Using significantly different fade amounts based on brightness (e.g., 68 - strip.getBrightness()) can cause noticeable jumps in fade behavior. If abruptness is unintentional, consider smoother transitions.


2546-2546: Local static function usage.
Defining twinklefox_one_twinkle as static is a good way to limit linkage. If you plan to reuse it elsewhere, consider moving it into a relevant namespace or header.


2581-2581: No issues with CRGBW variable instantiation.
Declaring CRGBW c; here is straightforward. No concerns.


2619-2627: Useful conditional fade logic.
The choice to fade the background color differently for each brightness range gives a dynamic feel. Ensure that these thresholds (64 and 16) match actual usage requirements. Looks fine overall.


2644-2655: [Duplicate from past feedback] Potential overflow in color blending.
Multiplying deltabright by 8 may exceed 255 if deltabright can reach values >31, causing wrap-around. If required, clamp or scale appropriately to avoid unexpected results.


4147-4158: Fixed PRNG seed each pass.
Resetting the PRNG to a constant seed (535) ensures consistent twinkle patterns every loop, which may be intentional. If true randomness is desired, consider using a time-varying or hardware-based seed.


5698-5698: Ensure sign-extension and range are correct.
Casting to int16_t, subtracting 127, then shifting right by 2 produces negative values for smaller inputs. If that’s intentional, no problem. Otherwise, confirm you aren’t inadvertently going below zero.


5825-5827: Check random8(0, w/h) boundary assumptions.
prng.random8(0, w) yields values in [0, w). Confirm that this range won’t exceed array or pixel boundaries for posX/posY = w or h.


5840-5843: Seeding PRNG with strip.now.
Using strip.now to seed the PRNG triggers predictable patterns if now increments uniformly each iteration. This might be desired for repeatability or might reduce randomness. Confirm your intention.


7510-7510: Potential color addition overflow.
Combining scaled PixelA and PixelB may exceed 255 in each channel if it doesn't automatically saturate. Confirm the underlying addition saturates or clamp if needed.


7819-7819: Check range of computations for rocket speed.
(SEGMENT.custom3) + hw_random16(SEGMENT.custom1 >> 3) + 5 might become large if custom3 or SEGMENT.custom1 is high. Ensure this speed remains valid for your effect.


7863-7863: 50% chance approach.
if (hw_random() & 1) is a straightforward way to pick a random bit. This is correct if you truly want a 50/50 branching.


7889-7889: [Duplicate from past feedback] min((uint16_t)150, hw_random16()) often yields 150.
If your aim is to randomize saturation up to 150, use something like hw_random16(151) instead.


9724-9724: Valid negative/positive range for gravity.
cos8_t() returns 0–255, so subtracting 128 yields -128–127, which could be valid if negative gravity is intended. Looks okay if that’s your design.


10051-10051: Check for integer overflow in sqrt32_bw.
Summing multiple FFT bins and passing to sqrt32_bw could risk overflow if bin sums are large. Confirm your typical input ranges stay well within safe bounds of 32-bit integer math.


16-16:

❓ Verification inconclusive

Ensure no circular include issues.
Including "colors.h" here is likely necessary, but please confirm there are no circular include chains. In particular, verify that colors.h does not indirectly include FX.h or wled.h, which could cause subtle build or linkage problems.

Run a quick search within the codebase to confirm that colors.h does not include FX.h or wled.h:


🏁 Script executed:

#!/bin/bash
fd colors.h --exec cat | rg -A 15 -B 15 "include"

Length of output: 49


Circular Include Verification: Manual Check Recommended

It appears that colors.h does not directly include "FX.h" or "wled.h", as the automated search did not return any matches. However, since the initial script produced no output—which might be due to limitations in the automated scan—please manually verify that there are no indirect circular dependencies (for example, via other headers included within colors.h).

  • File: wled00/FX.cpp (line 16) includes "colors.h"
  • Action: Confirm that colors.h and any headers it might include do not indirectly include FX.h or wled.h.

@netmindz netmindz requested review from netmindz and willmmiles March 27, 2025 07:18
@netmindz
Copy link
Member

Updated AnimARTrix - e6b7509b391cab97b59b8eb19580e67d4203ae7d

Copy link

@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 (2)
wled00/fcn_declare.h (2)

463-465: Macro usage for approximations is acceptable, but consider clarity.
Redefining sin_t, cos_t, and tan_t via macros is a quick way to switch to approximate versions; however, it can hide the “approx” detail from future maintainers. If clarity is a priority, rename them to something like sinApprox(), etc.

-#define sin_t sin_approx
-#define cos_t cos_approx
-#define tan_t tan_approx
+inline float sin_t(float x) { return sin_approx(x); }
+inline float cos_t(float x) { return cos_approx(x); }
+inline float tan_t(float x) { return tan_approx(x); }

510-531: Custom PRNG nicely complements hardware RNG.
The pseudo-random generator (PRNG) is useful for reproducible randomness in effects. Note that it lacks checks for invalid range inputs (e.g., min > lim). If that scenario is possible, consider adding an assertion or clamp. Otherwise, this is a lightweight alternative to hw_random.

 uint16_t random16(uint16_t min, uint16_t lim) {
+  if (lim <= min) {
+    // handle error or clamp
+    return min; 
+  }
   uint16_t delta = lim - min;
   return random16(delta) + min;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e353448 and b49fdab.

📒 Files selected for processing (1)
  • wled00/fcn_declare.h (5 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
wled00/fcn_declare.h (5)
wled00/FX.h (16)
  • i (613-613)
  • i (613-613)
  • i (661-661)
  • i (666-666)
  • i (667-667)
  • i (667-667)
  • i (668-668)
  • i (668-668)
  • i (673-673)
  • i (674-674)
  • i (935-935)
  • t (879-879)
  • t (879-879)
  • t (885-885)
  • t (885-885)
  • b (627-627)
wled00/util.cpp (3)
  • always_inline (664-670)
  • always_inline (682-688)
  • always_inline (690-697)
wled00/colors.h (13)
  • always_inline (267-270)
  • always_inline (267-267)
  • always_inline (273-277)
  • always_inline (273-273)
  • always_inline (280-283)
  • always_inline (280-280)
  • always_inline (286-290)
  • always_inline (286-286)
  • always_inline (400-402)
  • always_inline (579-581)
  • always_inline (583-585)
  • always_inline (587-589)
  • always_inline (591-593)
wled00/bus_manager.h (5)
  • b (124-124)
  • b (124-124)
  • b (193-199)
  • b (193-193)
  • b (240-240)
wled00/bus_wrapper.h (2)
  • b (337-337)
  • b (337-337)
⏰ Context from checks skipped due to timeout of 90000ms (19)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (esp32dev_V4)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_compat)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_compat)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full)
🔇 Additional comments (5)
wled00/fcn_declare.h (5)

5-6: Include placement looks good.
The addition of #include "colors.h" is appropriate given the removal of color structures from this file. It cleanly centralizes color definitions, reducing duplication.


395-405: Consider unit testing boundary inputs for beat/wave functions.
The new beat and wave function declarations appear consistent with typical FastLED-style utilities. However, verify how they handle edge cases (e.g., beats_per_minute = 0 or extremely large values). Automated tests would ensure correctness for any out-of-range inputs.

Please confirm whether the calling code validates input ranges or whether we need to add checks within these functions. If necessary, I can generate a test script or additional code to confirm safe behavior near boundary values.


483-486: Easing API addition is beneficial for creating smooth animations.
The new easing function declarations (ease8InOutCubic, ease16InOutCubic, ease8InOutQuad) align well with typical LED animations. No issues found here.


487-490: Scale functions mirror familiar FastLED semantics.
The scale8 variants correctly handle integer multiplication. Always confirm these are used in performance-critical paths and documented to avoid confusion about their integer-based saturations.


491-496: Fast saturating arithmetic looks correct.
These inline definitions (qadd8, qsub8, qmul8, etc.) are standard saturating operations from FastLED. They are typically used for color and brightness manipulations and appear correct.

// it can be useful to avoid back and forth conversions between uint32_t and fastled CRGB
struct CRGBW {
union {
uint32_t color32; // Access as a 32-bit value (0xWWRRGGBB)
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this technically violates strict aliasing and is undefined behavior, but whatever ^^

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it is a bit problematic (ambiguous conversions), but it allows direct comparison, constructor etc. from 32bit colors and speed is key in colors as its done for every pixel. Is there a better way that ensures speed?

Copy link
Contributor

Choose a reason for hiding this comment

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

c++20 has std::bit_cast, gcc has -fno-strict-aliasing (which may be applied selectively per compilation unit), and there is memcpy which may be optimized out. C would also allow this code without changes.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

from what I understand: a union does not violate strict aliasing.

Copy link
Contributor

@TripleWhy TripleWhy Apr 13, 2025

Choose a reason for hiding this comment

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

not simply by existing, no. but it does if you read a member that isn't the last one you wrote

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

do you have an example for what you mean? and how is it not an issue in fastled?

Copy link
Contributor

Choose a reason for hiding this comment

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

example from https://en.cppreference.com/w/cpp/language/union:

union S
{
    std::int32_t n;     // occupies 4 bytes
    std::uint16_t s[2]; // occupies 4 bytes
    std::uint8_t c;     // occupies 1 byte
};                      // the whole union occupies 4 bytes
 
int main()
{
    S s = {0x12345678}; // initializes the first member, s.n is now the active member
    // At this point, reading from s.s or s.c is undefined behavior,
    // but most compilers define it.
    std::cout << std::hex << "s.n = " << s.n << '\n';
 
    s.s[0] = 0x0011; // s.s is now the active member
    // At this point, reading from s.n or s.c is undefined behavior,
    // but most compilers define it.
    std::cout << "s.c is now " << +s.c << '\n' // 11 or 00, depending on platform
              << "s.n is now " << s.n << '\n'; // 12340011 or 00115678
}

In CRGB this isn't an issue because it defines 3 ways to access each channel, but all of them access the same memory and are of the same type. In CRGBW you alias uint32_t with uint8_t[4]. Which is probably fine, but if you access one after writing the other, it's technically UB. As noted in the above example, the compiler may define this to be fine, but I couldn't find any official statement for gcc. It may therefore even change from version to version.

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, thanks for the explanation. I just read that byte access is exempt from strict aliasing, so should really not be an issue.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@TripleWhy the definition from above is purely theoretical as it does not deal with physical data allocation (it does say they share same space). It may be undefined behaviour (and even implemented as such) but reading from a memory location is always defined. However, interpretation of that memory location is not and hence undefined behaviour in the standard IMO.

For practical purposes it may take a few experiments to correctly order union members to get the access needed. Unfortunately that would not be (universally) portable due to undefined theoretical definition.

That's the way I see unions.

- resolves licensing issue
- moved PRNG into its own space
- prettified some code
Copy link

@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: 2

🔭 Outside diff range comments (5)
wled00/FX_fcn.cpp (5)

152-189: 🛠️ Refactor suggestion

Improve memory error checks in allocateData() and deallocateData().

These functions properly handle memory allocation and deallocation but consider clarifying the debug messages for memory failure cases:

  1. Indicate how many bytes were requested vs. how many remain.
  2. Ensure that partial allocations or repeated calls to deallocation can’t leave the system in a partially inconsistent memory state.

316-427: 🛠️ Refactor suggestion

Confirm correct brightness and mode blend behaviors.

Functions updateTransitionProgress(), currentBri(), and currentMode() handle various blending styles. Be sure the new fade/push/swipe styles do not lead to unexpected flickering, especially if intervals between frames are longer. Also, confirm that bridging from “old effect” to “new effect” retains consistent color states.


561-598: 🛠️ Refactor suggestion

Enforce defensive checks in color and CCT updates.

When setColor(), setCCT(), and setOpacity() are invoked in quick succession, the code starts transitions internally. Validate that repeated calls do not accumulate extra allocations or cause the effect logic to get stuck in an indeterminate transition state.


845-938: 🛠️ Refactor suggestion

Consider refactoring Pinwheel logic for maintainability.

The pinwheel effect is quite involved, using Bresenham-based draws. While powerful, it can be challenging to debug. Splitting out the steps (ray generation, block filling, partial line expansions) into smaller helper functions might boost readability and reduce the chance of subtle indexing bugs.


1279-1311: 🛠️ Refactor suggestion

Validate blur logic on boundary pixels.

The blur() function can carry over partial color from adjacent pixels. If the segment boundaries are at the ends of the strip or matrix edges, confirm no out-of-range reads occur. Also, check repeated calls to blur do not cause color drift or require an oppositional unblur step to preserve detail.

♻️ Duplicate comments (3)
wled00/FX.cpp (3)

2645-2655: Potential overflow when multiplying by 8.
(Multiplying deltabright by 8 may exceed 255 and wrap around.)


5841-5844: Potential dimension wrap-around in random8(0, cols).


7890-7890: min((uint16_t)150, hw_random16()) yields 150 often.

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

16-16: Evaluate conditional include usage for "colors.h".

Since "colors.h" may be large and specialized, consider only including it where necessary to reduce compilation dependencies and improve build efficiency.


95-115: Avoid potential name collisions in copy & move constructors.

The copy and move constructors copy internal pointers (e.g., name, data) and set them to nullptr in the original object. This is correct for single ownership but be sure no other code references these pointers post-move. If a segment is duplicated before a move, ensure that references to the original are not inadvertently invalidated.


209-255: Validate fallback cases in loadPalette().

The switch statement handles wide-ranging pal values, including random and custom palettes. Ensure boundary checks remain consistent with other files that use strip.customPalettes. Also, if _randomPalette is empty or incorrectly generated, confirm that the code gracefully falls back to a known palette.


429-445: Evaluate performance in currentColor().

During transitions, currentColor() conditionally blends old and new color states. In performance-critical contexts (e.g., with many segments or frequent updates), repeated calls may add overhead. Consider caching partial computations if feasible, or verifying that repeated blending won't degrade framerate significantly.


489-503: Monitor timing in handleRandomPalette().

The logic triggers a new random palette when _lastPaletteChange is exceeded by randomPaletteChangeTime. Verify no collisions or race conditions if multiple segments request random palette changes simultaneously. If “harmonic random palette” is used, confirm it’s seeded properly to ensure diverse outcomes.


648-658: Clarify custom palette range in setPalette().

If user-supplied pal indexes a custom palette that is out of range, we unconditionally reset to 0. Consider logging or providing an error message to help users debug. This might reduce confusion if they supply an invalid palette ID programmatically.


807-839: Validate circular expansions in M12_pArc.

Generating circular arcs can be CPU-intensive for large radii. Check if frequent calls degrade performance. For extremely large segments, consider a more traditional Bresenham circle approach or caching partial results to improve performance, especially if arcs must be redrawn frequently.


1006-1038: Review anti-aliased version for float overhead.

setPixelColor(float i, uint32_t col, bool aa) uses floating-point. If performance constraints are strict, consider moving to fixed-point or precomputed scaling. On some microcontrollers, frequent float usage can hamper rendering speed or cause excessive memory usage.


1313-1364: Confirm uniform color distribution in color_wheel() and color_from_palette().

For color_wheel(), the code inverts pos and breaks it into three ranges. Confirm the transitions at 85 and 170 remain correct. For color_from_palette(), watch out for integer rounding in palette index mapping for large segments.

wled00/PRNG.h (3)

3-5: Add user-facing documentation for the PRNG class.

Briefly describing the usage and characteristics of this pseudo-random number generator (e.g., seedable, range-limited, etc.) would help new contributors or maintainers quickly understand how it’s intended to be used in effects.


12-17: Evaluate the internal LCG-like approach for uniform random distribution.

random16() uses multiplication and XOR shifting. While simple and usually sufficient, it’s worth checking if the distribution is acceptable for effects needing high randomness fidelity. Also consider a known approach like Xorshift* or PCG if uniform distribution is crucial.


21-23: Simplify or unify 8-bit random methods.

random8() is effectively random16() >> 8, but parameter overloads do extra multiplications. You could unify the logic to maintain code consistency, e.g., calling random16(lim << 8) >> 8 for uniform distribution, as long as performance remains acceptable.

wled00/palettes.h (2)

16-37: Use consistent naming patterns for palette constants.

CloudColors_p is introduced with color values bridging dark to light blues. The name is consistent with other palettes, but watch for user confusion if these are “Cloud” or “Sky” palettes. Possibly rename to “SkyCloudColors” or keep consistent underscores if it’s required by your style guide.


568-607: Evaluate representation of extra white channel in CRGBPalette16.

All palette definitions are purely RGB. If you anticipate using RGBW strips or a separate white channel, consider how these color definitions might be blended or extended to incorporate W. Alternatively, keep them strictly RGB if separate logic handles white channel injection (CCT or autowhite).

wled00/src/dependencies/fastled/fastled_fcn.h (1)

123-205: Scrutinize CRGB’s multi-constructor approach.

CRGB can be built from channels, 32-bit color codes, or from CHSV. Large expansions happen in hsv2rgb_rainbow(). If performance is a concern, consider caching partial results or adopting inlined transformations to cut function call overhead. Also ensure no memory misalignment when reading/writing raw[3].

wled00/src/dependencies/fastled/fastled_fcn.cpp (1)

101-106: Consider supporting the white channel in isRGBW mode.

Currently, rgbdata[3] is commented out in hsv2rgb_rainbow, leaving the white channel uninitialized if isRGBW is true. This can lead to inconsistencies. Consider explicitly setting rgbdata[3] to zero or computing a white component if needed.

    if(isRGBW) {
      rgbdata[0] = b;
      rgbdata[1] = g;
      rgbdata[2] = r;
-     //rgbdata[3] = 0; // white
+     rgbdata[3] = 0; // ensure a known value
    } ...
wled00/colors.h (2)

17-22: Possible macro collisions or unintended expansions.

The macros RGBW32, R(c), G(c), B(c), and W(c) may collide with similarly named macros or cause multiple evaluations of arguments. Consider prefixed or inline functions instead for safer usage.


159-166: Confirm ignoring the white channel when converting to CHSV32.

The assignment and constructor from CRGBW to CHSV32 discard the white component. Verify that ignoring w is intended. If preserving overall brightness is required, consider factoring in w during conversion to HSV.

wled00/FX.cpp (3)

2620-2627: Consider clarifying the fade factor.
Using 86 yields roughly one-third, but 85 is a closer approximation of 1/3 if that is the intended ratio.

-    bg = color_fade(bg, 86, true);
+    bg = color_fade(bg, 85, true);

4180-4180: Remove or justify commented-out code.
If this old assignment to palettes[1] is no longer needed, consider removing it rather than leaving a commented snippet.

-    //palettes[1] = CRGBPalette16(CHSV(baseI+hw_random8(64), 255, hw_random8(128,255)), ...

4924-4924: Remove or clarify commented line.
The commented-out SEGMENT.setPixelColorXY(...) line appears to be unused.

-      //SEGMENT.setPixelColorXY(dx, dy, grad ? color_fade(color, (255-rate), true) : color);
wled00/fcn_declare.h (4)

5-6: Consider splitting color-related functionality into a dedicated header or file.
Including "colors.h" here introduces a dependency on color-specific logic in a file that mostly declares general-purpose functions. If "colors.h" contains extensive color-handling classes or utilities, placing it in a separate header or compilation unit might improve maintainability and modularity.


395-401: Add documentation or comments clarifying the newly introduced beat functions.
These declarations (e.g., beat88, beat16, beat8, beatsin88_t, beatsin16_t, beatsin8_t) appear to be essential for effects timing or wave generation. It would be beneficial to include brief comments explaining their parameters, expected ranges, any known constraints (e.g., integer overflow), and typical usage scenarios. This helps future maintainers and eases unit test coverage.


459-461: Use inline functions instead of macros for sin_t, cos_t, and tan_t if possible.
Defining #define sin_t sin_approx macros can lead to unexpected expansions and can hamper debugging or IDE assistance. Replacing them with inline functions like inline float sin_t(float x) { return sin_approx(x); } would preserve the intent while avoiding macro side effects.


491-491: Remove or justify the commented standard Math block if no longer needed.
The large block of commented-out math functions above (lines 481-490) might cause confusion if this approximation-based approach is now preferred. Consider removing them or adding a short comment clarifying whether they’re retained as a fallback or reference only.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b49fdab and 47773b4.

📒 Files selected for processing (14)
  • wled00/FX.cpp (22 hunks)
  • wled00/FX.h (4 hunks)
  • wled00/FX_fcn.cpp (2 hunks)
  • wled00/PRNG.h (1 hunks)
  • wled00/colors.cpp (6 hunks)
  • wled00/colors.h (1 hunks)
  • wled00/fcn_declare.h (4 hunks)
  • wled00/ir.cpp (3 hunks)
  • wled00/palettes.h (1 hunks)
  • wled00/src/dependencies/fastled/LICENSE.txt (1 hunks)
  • wled00/src/dependencies/fastled/fastled_fcn.cpp (1 hunks)
  • wled00/src/dependencies/fastled/fastled_fcn.h (1 hunks)
  • wled00/util.cpp (3 hunks)
  • wled00/wled_math.cpp (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • wled00/wled_math.cpp
  • wled00/src/dependencies/fastled/LICENSE.txt
🚧 Files skipped from review as they are similar to previous changes (4)
  • wled00/ir.cpp
  • wled00/FX.h
  • wled00/util.cpp
  • wled00/colors.cpp
🧰 Additional context used
🧬 Code Graph Analysis (6)
wled00/src/dependencies/fastled/fastled_fcn.h (1)
wled00/src/dependencies/fastled/fastled_fcn.cpp (11)
  • hsv2rgb_rainbow (9-9)
  • fill_solid_RGB (136-140)
  • fill_solid_RGB (136-136)
  • fill_gradient_RGB (143-173)
  • fill_gradient_RGB (143-143)
  • fill_gradient_RGB (175-178)
  • fill_gradient_RGB (175-175)
  • fill_gradient_RGB (180-185)
  • fill_gradient_RGB (180-180)
  • fill_gradient_RGB (187-194)
  • fill_gradient_RGB (187-187)
wled00/src/dependencies/fastled/fastled_fcn.cpp (2)
wled00/colors.cpp (3)
  • optimize (96-104)
  • optimize (253-299)
  • optimize (310-329)
wled00/src/dependencies/fastled/fastled_fcn.h (16)
  • hue (167-169)
  • hue (167-167)
  • hue (172-174)
  • hue (172-172)
  • r (378-382)
  • r (394-396)
  • CRGB (148-148)
  • CRGB (148-148)
  • CRGB (151-152)
  • CRGB (151-151)
  • CRGB (155-156)
  • CRGB (155-155)
  • CRGB (159-159)
  • CRGB (159-159)
  • CRGB (162-164)
  • CRGB (162-162)
wled00/colors.h (3)
wled00/src/dependencies/fastled/fastled_fcn.h (26)
  • r (378-382)
  • r (394-396)
  • hue (167-169)
  • hue (167-167)
  • hue (172-174)
  • hue (172-172)
  • always_inline (11-11)
  • always_inline (12-12)
  • always_inline (13-13)
  • always_inline (14-14)
  • always_inline (15-15)
  • always_inline (16-16)
  • always_inline (17-17)
  • always_inline (18-18)
  • always_inline (240-243)
  • always_inline (240-240)
  • always_inline (246-250)
  • always_inline (246-246)
  • always_inline (253-256)
  • CHSV (101-101)
  • CHSV (101-101)
  • CHSV (104-105)
  • CHSV (104-104)
  • CHSV (108-108)
  • CHSV (108-108)
  • hsv2rgb_rainbow (52-52)
wled00/colors.cpp (5)
  • color_blend (20-30)
  • color_blend (20-20)
  • hsv2rgb_spectrum (253-253)
  • hsv2rgb_spectrum (302-307)
  • hsv2rgb_spectrum (302-302)
wled00/src/dependencies/fastled/fastled_fcn.cpp (1)
  • hsv2rgb_rainbow (9-9)
wled00/FX.cpp (3)
wled00/colors.h (6)
  • color (31-31)
  • color (130-130)
  • color (130-130)
  • r (154-156)
  • color_fade (42-42)
  • color_blend (39-39)
wled00/wled_math.cpp (4)
  • sin8_t (82-86)
  • sin8_t (82-82)
  • cos8_t (88-90)
  • cos8_t (88-88)
wled00/PRNG.h (4)
  • min (20-20)
  • min (20-20)
  • min (23-23)
  • min (23-23)
wled00/fcn_declare.h (1)
wled00/FX.h (1)
  • timebase (870-870)
wled00/FX_fcn.cpp (2)
wled00/FX.h (1)
  • _currentPalette (607-607)
wled00/src/dependencies/fastled/fastled_fcn.h (10)
  • CRGBPalette16 (572-574)
  • CRGBPalette16 (577-585)
  • CRGBPalette16 (588-590)
  • CRGBPalette16 (593-595)
  • CRGBPalette16 (610-614)
  • CRGBPalette16 (668-670)
  • CRGBPalette16 (673-675)
  • CRGBPalette16 (678-680)
  • CRGBPalette16 (683-685)
  • CRGBPalette16 (694-696)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
🔇 Additional comments (35)
wled00/FX_fcn.cpp (7)

80-89: Confirm default palette consistency across the codebase.

The static member _currentPalette is initialized to a default-constructed CRGBPalette16(). Make sure all references to _currentPalette (e.g. in loadPalette()) handle the initial state consistently, and verify that global transitions to other palettes remain coherent when the system first boots.


198-208: Expand validation in resetIfRequired().

resetIfRequired() re-initializes segment data if reset is set. This logic is simple and useful, but if the reset flag is triggered frequently or from other code paths, confirm that the effect states, transitions, or upstream references aren't unexpectedly cut short.


257-296: Ensure mode transitions handle concurrent changes smoothly.

startTransition() and stopTransition() rely on dynamic allocations and store transitional state in _t. Verify concurrency safety if transitions are triggered rapidly in succession (e.g., from network commands). Also, ensure memory for _segT._dataT is freed even if transitions are aborted early.


504-559: Assess geometry impacts on large 2D setups.

setGeometry() handles an extensive 2D coordinate system. For bigger matrices or custom LED maps, confirm it efficiently handles out-of-range inputs, preventing memory over-access. Also, ensure clear() calls remain minimal to avoid flickering if geometry changes frequently.


609-646: Check defaults for new modes in setMode().

The code loads default speeds, intensities, and other parameters from extractModeDefaults(). Ensure new or custom modes do not rely on undefined entries. Also, watch out for array out-of-bounds if advanced modes expand their default parameters beyond the baseline expected here.


675-744: Ensure correct handling for mirror and reverse.

The logic in virtualWidth(), virtualHeight(), and virtualLength() accounts for mirror and reverse. For advanced 2D or multiple segments layered, double-check that partial repeats or overlap do not produce off-by-one pixel artifacts (particularly if segment end boundaries vary).


953-961: Scrutinize push-based blending.

When _modeBlend == false but blendingStyle is BLEND_STYLE_PUSH_LEFT|_RIGHT, we adjust indices by a fraction. In large segments or with multi-effects, ensure no integer overflow occurs and that the inlined arithmetic is correct for typical transitions.

wled00/PRNG.h (1)

9-10: Verify that overwriting the seed won't disrupt ongoing randomness.

When setSeed() is called mid-runtime, confirm no conflicts arise if other effects or modules are concurrently using the PRNG. For long-running sessions, ensure seed changes do not inadvertently produce repetitive or patterned outputs.

wled00/palettes.h (3)

6-15: Confirm licensing and referencing for extracted palettes.

The file references FastLED under the MIT license. Ensure these references and the included license file remain correct and that any further modifications to these palettes are tracked. This helps maintain compliance with open-source obligations.


108-114: Validate gradient indexing for RainbowColors_p.

The array uses 16 discrete steps to render a rainbow. If changes are made to extend or reorder these steps, confirm that the color wrap-around remains correct. Also, consider verifying the performance of quick palette transitions if every line references this array.


994-1053: Verify fastledPalettes[] and gGradientPalettes[] indexing logic.

The arrays store references to known palette data structures (e.g., RainbowColors_p, Sunset_Real_gp). If code looks up palettes by index, ensure no out-of-bounds conditions occur or no mismatch with enumerations referencing them. This is especially important if new palettes are appended.

wled00/src/dependencies/fastled/fastled_fcn.h (1)

70-121: Confirm consistent CHSV assumptions across the codebase.

The CHSV struct allows assignment from hue, saturation, value. It’s important that h is stored in 8 bits with an assumed 0–255 range but used as 16 bits in hsv2rgb_rainbow by shifting h << 8. Verify no undesired wrap-around if h is near max values.

wled00/src/dependencies/fastled/fastled_fcn.cpp (4)

3-4: Document the original license details more explicitly.

While referencing that this code is originally from FastLED is good, it may be helpful to replicate the relevant licensing text from the original source, if the license requires it. Ensure compliance with MIT license requirements by including the original copyright statement.

Are you certain you have included all required license text from the FastLED repository in /src/dependencies/fastled/LICENSE.txt?


9-62: Validate handling of saturation=0 within hsv2rgb_rainbow.

When s == 0, the code sets r, g, and b to 255 before scaling by brightness, effectively producing pure white at maximum brightness. This can cause unexpected results if a user expects grayscale levels. Verify that this approach meets your intended design requirements.

Please confirm whether the behavior for s = 0 aligns with your project's color logic or if a gray-level approach is needed, for example:

- r = 255; g = 255; b = 255;
+ r = g = b = v; // produce grayscale

Also applies to: 66-100


136-185: Check for potential out-of-bounds in fill_gradient_RGB.

fill_gradient_RGB writes color data from startpos to endpos inclusively. There's no explicit array size check in this function. Ensure callers always pass valid startpos and endpos within the array bounds to prevent potential out-of-bounds writes.


217-259: LGTM on the easing and waveform functions.

The cubic, quadratic, and triangular wave generators are straightforward and commonly used in LED animation. The bitwise operations appear correct. Good job on keeping them inlined for performance.

wled00/colors.h (3)

13-14: Reiterate the MIT license details in the code.

The comment mentions that some functions derive from FastLED under MIT. Confirm that the notice includes all required disclaimers from the original FastLED source. This helps maintain licensing compliance.


95-107: Union usage might conflict with strict aliasing in some compilers.

While commonly used in color libraries, reading one union member (e.g. color32) after writing another (e.g. b, g, r, w) may trigger undefined behavior under strict aliasing rules. Consider confirming that your build settings or compiler version allow this usage, or use memcpy or std::bit_cast (C++20) for guaranteed well-defined behavior.


146-151: Ensure safe hue adjustments in adjust_hue.

hsv.h += (hueshift << 8) can overflow if hueshift is large. While wrapping might be intentional for cyclical color transitions, ensure it is tested and documented for all valid range values.

wled00/FX.cpp (16)

16-17: Header inclusion looks good!
No issues found in these new includes—the stable references to colors.h and prng.h are in line with the overall removal of the FastLED dependency.


73-74: Global static PRNG usage looks fine.
Given a single-threaded environment, this approach is acceptable and clarifies the random generation.


1761-1784: Consistent PRNG usage with seed restoration
Storing the old seed, using a new seed locally for randomization, and then restoring the old seed helps keep the rest of the system’s random usage coherent. The pattern of randomizing color channels with prng.random8(6) is clear and looks intentional.


2547-2547: Ensure SEGENV.aux0 is never zero.
The expression ms / SEGENV.aux0 could trigger a divide-by-zero if SEGENV.aux0 happens to be 0.

Would you like to confirm that SEGENV.aux0 is always nonzero or add a check for safety?


2582-2582: Definition of local color variable.
The new CRGBW c; line is straightforward, and usage is consistent downstream.


2597-2597: Assigning black is correct.
Setting c = 0; to represent black is valid for CRGBW.


4148-4158: Twinkleup's PRNG usage is consistent.
Storing and restoring the seed supports reproducible twinkle patterns without disrupting global randomness.


5699-5699: Perlin-based shift is correct.
Using a signed shift from perlin8() for bump mapping seems valid.


5826-5828: Check for dimension range in random8(0, w/h).
If w or h exceed 255, passing them to prng.random8(...) will wrap around.

Please confirm that w and h remain ≤ 255 or handle bigger sizes as needed.


6015-6015: Check for color wrap-around.
blob->color[i] += 4; may overflow if the color channel is near its maximum range.

Verify this behavior is intended or consider a clamp.


7511-7511: Blending of PixelA and PixelB is well-structured.
No issues found with the eased nscale addition.


7820-7820: Random rocket velocity.
Logic for vertical speed plus random offset is consistent with the effect’s design.


7864-7864: 50% chance for circular explosion looks intentional.
No issues.


9725-9725: Cosine-based gravity shift is valid.
xgravity = cos8_t(...) - 128; is consistent with existing pattern logic.


10052-10052: Calculating mids using sqrt32_bw is sound.
No immediate concerns.


2272-2273: ⚠️ Potential issue

Prevent underflow in fadeUpAmount and fadeDownAmount.
If strip.getBrightness() exceeds 68, then 68 - strip.getBrightness() underflows in an 8-bit variable.

Use a clamp to avoid negative values:

-  uint8_t fadeUpAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>2) : 68-strip.getBrightness();
-  uint8_t fadeDownAmount = strip.getBrightness()>28 ? 8 + (SEGMENT.speed>>3) : 68-strip.getBrightness();
+  uint8_t baseUp   = (strip.getBrightness() <= 68) ? (68 - strip.getBrightness()) : 0;
+  uint8_t baseDown = (strip.getBrightness() <= 68) ? (68 - strip.getBrightness()) : 0;
+  uint8_t fadeUpAmount =
+      (strip.getBrightness() > 28 ? (8 + (SEGMENT.speed >> 2)) : baseUp);
+  uint8_t fadeDownAmount =
+      (strip.getBrightness() > 28 ? (8 + (SEGMENT.speed >> 3)) : baseDown);

Likely an incorrect or invalid review comment.

Comment on lines +568 to +782
CRGBPalette16(const CRGB& c1, const CRGB& c2, const CRGB& c3, const CRGB& c4) {
fill_gradient_RGB(&(entries[0]), 16, c1, c2, c3, c4);
}

// Creates a palette from a gradient palette in PROGMEM.
//
// Gradient palettes are loaded into CRGBPalettes in such a way
// that, if possible, every color represented in the gradient palette
// is also represented in the CRGBPalette, this may not preserve original
// color spacing, but will try to not omit small color bands.

CRGBPalette16(TProgmemRGBGradientPalette_bytes progpal) {
*this = progpal;
}

CRGBPalette16& operator=(TProgmemRGBGradientPalette_bytes progpal) {
TRGBGradientPaletteEntryUnion* progent = (TRGBGradientPaletteEntryUnion*)(progpal);
TRGBGradientPaletteEntryUnion u;

// Count entries
int count = 0;
do {
u.dword = *(const uint32_t*)(progent + count);
++count;
} while (u.index != 255);

int lastSlotUsed = -1;

u.dword = *(const uint32_t*)(progent);
CRGB rgbstart(u.r, u.g, u.b);

int indexstart = 0;
int istart8 = 0;
int iend8 = 0;
while (indexstart < 255) {
++progent;
u.dword = *(const uint32_t*)(progent);
int indexend = u.index;
CRGB rgbend(u.r, u.g, u.b);
istart8 = indexstart / 16;
iend8 = indexend / 16;
if (count < 16) {
if ((istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) {
istart8 = lastSlotUsed + 1;
if (iend8 < istart8) {
iend8 = istart8;
}
}
lastSlotUsed = iend8;
}
fill_gradient_RGB(&(entries[0]), istart8, rgbstart, iend8, rgbend);
indexstart = indexend;
rgbstart = rgbend;
}
return *this;
}

// Creates a palette from a gradient palette in dynamic (heap) memory.
CRGBPalette16& loadDynamicGradientPalette(TDynamicRGBGradientPalette_bytes gpal) {
TRGBGradientPaletteEntryUnion* ent = (TRGBGradientPaletteEntryUnion*)(gpal);
TRGBGradientPaletteEntryUnion u;

// Count entries
unsigned count = 0;
do {
u = *(ent + count);
++count;
} while (u.index != 255);

int lastSlotUsed = -1;

u = *ent;
CRGB rgbstart(u.r, u.g, u.b);

int indexstart = 0;
int istart8 = 0;
int iend8 = 0;
while (indexstart < 255) {
++ent;
u = *ent;
int indexend = u.index;
CRGB rgbend(u.r, u.g, u.b);
istart8 = indexstart / 16;
iend8 = indexend / 16;
if (count < 16) {
if ((istart8 <= lastSlotUsed) && (lastSlotUsed < 15)) {
istart8 = lastSlotUsed + 1;
if (iend8 < istart8) {
iend8 = istart8;
}
}
lastSlotUsed = iend8;
}
fill_gradient_RGB(&(entries[0]), istart8, rgbstart, iend8, rgbend);
indexstart = indexend;
rgbstart = rgbend;
}
return *this;
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Review palette loading and gradient expansions in CRGBPalette16.

The constructor overloads and operator= handle multiple sources (arrays, PROGMEM, dynamic gradient). Make sure that partial or invalid gradient data does not cause out-of-bounds reads. Also, watch out for repeated calls to fill_gradient_RGB() that might degrade performance on low-end MCUs when creating or modifying palettes frequently.

@DedeHai
Copy link
Collaborator Author

DedeHai commented Apr 15, 2025

@Aircoookie any thoughts on how I fixed the licensing issue?

  • moved some of the FastLED stuff into its own folder, adding MIT license there
  • referenced that license in all places where FastLED functions/palettes were added within WLED code

Copy link

@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 (3)
wled00/prng.h (3)

12-18: Consider adding a note about the algorithm's period.

The random number generation algorithm uses a 16-bit seed, which means it has a maximum period of 2^16 (65,536) values before repeating. While this is likely sufficient for most visual effects, it might be worth documenting this limitation for future reference.


19-23: Add input validation for the range-based methods.

The range-based methods (random16(min, lim) and random8(min, lim)) don't check if min < lim. Consider adding validation to handle edge cases where min >= lim to prevent unexpected behavior.

-  uint16_t random16(uint16_t min, uint16_t lim) { uint16_t delta = lim - min; return random16(delta) + min; }
+  uint16_t random16(uint16_t min, uint16_t lim) { 
+    if (min >= lim) return min;
+    uint16_t delta = lim - min; 
+    return random16(delta) + min; 
+  }

-  uint8_t random8(uint8_t min, uint8_t lim) { uint8_t delta = lim - min; return random8(delta) + min; }
+  uint8_t random8(uint8_t min, uint8_t lim) { 
+    if (min >= lim) return min;
+    uint8_t delta = lim - min; 
+    return random8(delta) + min; 
+  }

19-20: Add validation for zero limit in bounded random methods.

The bounded random methods don't handle the case where lim = 0, which could lead to unexpected behavior. Consider adding a check to return 0 in this case.

-  uint16_t random16(uint16_t lim) { return ((uint32_t)random16() * lim) >> 16; }
+  uint16_t random16(uint16_t lim) { 
+    if (lim == 0) return 0;
+    return ((uint32_t)random16() * lim) >> 16; 
+  }

-  uint8_t random8(uint8_t lim) { return (uint8_t)(((uint16_t)random8() * lim) >> 8); }
+  uint8_t random8(uint8_t lim) { 
+    if (lim == 0) return 0;
+    return (uint8_t)(((uint16_t)random8() * lim) >> 8); 
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 47773b4 and cb9ba98.

📒 Files selected for processing (1)
  • wled00/prng.h (1 hunks)
🔇 Additional comments (1)
wled00/prng.h (1)

1-24: Well-structured PRNG implementation as FastLED replacement.

This new PRNG class implementation provides a good replacement for FastLED's random number generation capabilities with a clean, self-contained design. The class properly handles both 16-bit and 8-bit random values with range-bounded variants, which is important for deterministic visual effects.

Copy link

@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

♻️ Duplicate comments (2)
wled00/FX.cpp (2)

2272-2273: Abrupt fade logic.
This logic was previously flagged as potentially causing abrupt fade jumps. The same concern remains.


2645-2645: Potential overflow in blend factor.
The expression (uint8_t)(deltabright * 8) can exceed 255, causing wrap-around.

Also applies to: 2648-2648, 2656-2656

🧹 Nitpick comments (6)
wled00/FX.cpp (6)

73-74: Restrict scope of the global PRNG.
Since it’s only used within this file, consider making it static in an unnamed namespace for better encapsulation:

-static PRNG prng(hw_random());
+namespace {
+  static PRNG prng(hw_random());
+}

2547-2547: Clarify parameter naming.
bool cat is not immediately intuitive. Consider renaming to something more descriptive (e.g. isCatEye, enableCascade, etc.).


2597-2597: Prefer symbolic constant for readability.
Use BLACK or a named constant to clarify intent instead of c = 0;.

-    c = 0; // black
+    c = BLACK; // black

2620-2620: Use named constants or enumerations for fade factors.
Magic numbers 16, 64, and 86 might benefit from descriptive names to improve readability.

Also applies to: 2623-2623, 2625-2625, 2627-2627


4924-4924: Remove or justify commented-out code.
If this line is no longer needed, removing it would reduce clutter.


6015-6015: Potential overflow in color increment.
Incrementing by 4 might cause channel overflow depending on the data type. Consider clamping if you want a maximum limit.

-if (SEGENV.step < strip.now) blob->color[i] += 4; // slowly change color
+if (SEGENV.step < strip.now) {
+  blob->color[i] = min((uint8_t)(blob->color[i] + 4), 255);
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb9ba98 and 62943ec.

📒 Files selected for processing (6)
  • wled00/FX.cpp (21 hunks)
  • wled00/FX.h (4 hunks)
  • wled00/FXparticleSystem.cpp (4 hunks)
  • wled00/fcn_declare.h (4 hunks)
  • wled00/wled.cpp (0 hunks)
  • wled00/wled.h (1 hunks)
💤 Files with no reviewable changes (1)
  • wled00/wled.cpp
✅ Files skipped from review due to trivial changes (1)
  • wled00/wled.h
🚧 Files skipped from review as they are similar to previous changes (1)
  • wled00/FX.h
🧰 Additional context used
🧬 Code Graph Analysis (2)
wled00/FXparticleSystem.cpp (2)
wled00/colors.cpp (5)
  • ColorFromPalette (107-135)
  • ColorFromPalette (107-107)
  • hsv2rgb_spectrum (253-253)
  • hsv2rgb_spectrum (302-307)
  • hsv2rgb_spectrum (302-302)
wled00/colors.h (3)
  • ColorFromPalette (44-44)
  • hsv2rgb_spectrum (49-49)
  • hsv2rgb_spectrum (50-50)
wled00/FX.cpp (4)
wled00/fcn_declare.h (19)
  • r (445-445)
  • r (445-445)
  • b (302-302)
  • b (302-302)
  • c (329-329)
  • c (329-329)
  • sin8_t (468-468)
  • hw_random (425-425)
  • hw_random (425-425)
  • hw_random (426-426)
  • hw_random (427-427)
  • hw_random16 (428-428)
  • hw_random16 (428-428)
  • hw_random16 (429-429)
  • hw_random16 (429-429)
  • hw_random16 (430-430)
  • hw_random16 (430-430)
  • cos8_t (469-469)
  • sqrt32_bw (479-479)
wled00/colors.cpp (4)
  • color_fade (70-88)
  • color_fade (70-70)
  • color_blend (20-30)
  • color_blend (20-20)
wled00/wled_math.cpp (6)
  • sin8_t (82-86)
  • sin8_t (82-82)
  • cos8_t (88-90)
  • cos8_t (88-88)
  • sqrt32_bw (225-246)
  • sqrt32_bw (225-225)
wled00/src/dependencies/fastled/fastled_fcn.cpp (2)
  • ease8InOutCubic (219-223)
  • ease8InOutCubic (219-219)
⏰ Context from checks skipped due to timeout of 90000ms (19)
  • 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 (esp32_wrover)
  • 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 (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp32dev_V4)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_compat)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_160)
  • GitHub Check: wled_build / Build Environments (nodemcuv2_compat)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
  • GitHub Check: wled_build / Build Environments (esp8266_2m)
  • GitHub Check: wled_build / Build Environments (nodemcuv2)
🔇 Additional comments (20)
wled00/FXparticleSystem.cpp (4)

580-580: Verify CRGBW usage vs. CRGB.
Line 580 introduces CRGBW baseRGB, but subsequent rendering calls convert it back to CRGB, discarding the white channel. If the white component is not actually used, consider reverting to CRGB to reduce overhead or extending the pipeline to incorporate the white channel.

Please confirm whether ignoring the white channel is intentional.


651-662: Color computation logic looks good.
These lines properly retrieve color from the palette, optionally adjust saturation, and convert back to RGB before rendering. The two-step conversion (RGB → HSV → RGB) is a typical way to refine saturation. No immediate issues are found.


1532-1532: Check CRGBW usage in 1D rendering.
Similar to the 2D case, CRGBW baseRGB is declared but effectively used as RGB upon rendering. If you plan to leverage the white channel later, this is fine; otherwise, consider using CRGB to avoid confusion.


1579-1588: Palette-to-HSV-to-RGB conversion is valid.
This approach allows you to apply 1D-specific saturation changes. Converting the final result to CRGB ensures consistency with the rest of the rendering flow. Everything looks consistent, and no performance pitfalls stand out for typical animation use cases.

wled00/FX.cpp (12)

1761-1784: Validate random seed switching for concurrency or multi-effect usage.
Re-seeding and restoring the PRNG seed could cause unexpected results if multiple segments run concurrently or in quick succession and share the same PRNG instance.

Do confirm that effect calls are synchronized or single-threaded, so that temporarily changing the global PRNG seed won’t impact other effects.


2582-2582: Local color variable looks good.
Using CRGBW c; is consistent with the function’s return type.


4148-4149: Fixed seed 535 may create repetitive patterns.
If that is the purpose (e.g. a predictable twinkle pattern), it’s fine. Otherwise, consider a dynamic seed.

Also applies to: 4152-4152, 4154-4155, 4158-4158


4180-4180: No immediate concerns with hardware-based random palette generation.
Usage of hw_random8(...) here appears correct for producing partially random color entries.


5699-5699: Check sign-shift behavior.
Shifting negative values relies on implementation-defined arithmetic shifts. Verify that the effect ( ±32 ) behaves as intended.


5826-5827: Validate upper range in prng.random8(0, ...).
If w or h can exceed 255, random8(0, w) will be truncated to 8 bits.


5841-5841: Repeated seed calls with strip.now.
Setting the seed multiple times could yield identical sequences if strip.now changes slowly. Ensure this is the intended behavior.

Also applies to: 5843-5844


7511-7511: Verify color channel addition for overflow.
PixelA.nscale8(...) + PixelB.nscale8(...) may exceed per-channel bounds. Confirm additive blending is desired or clamp the result.


7864-7864: Simple 50% coin toss logic is fine.
Using hw_random() & 1 as a random boolean is concise and acceptable here.


9730-9730: Confirm negative gravity range is intended.
(int16_t)cos8_t(SEGENV.aux0) - 128 yields [-128..127]. Ensure that negative values for xgravity are correct for your physical simulation.


10084-10084: Check for invalid or overflow sums on FFT bins.
Summing multiple audio bins could exceed expectations or be zero. Confirm sqrt32_bw handles those edge cases gracefully.


16-17:

✅ Verification successful

Ensure no circular header dependencies.
Including both colors.h and prng.h here appears valid, but please confirm neither header indirectly includes the other.


🏁 Script executed:

#!/bin/bash
# Description: Check for circular includes among colors.h and prng.h

set -e
echo "Searching for includes of prng.h in colors.h and vice versa..."
rg '#include "prng.h"' -A 3 -B 3
rg '#include "colors.h"' -A 3 -B 3

Length of output: 2695


Circular dependency check passed.
The search results confirm that there is no instance of colors.h including prng.h or vice versa. Both headers are included in their respective files as intended (e.g., in wled00/FX.cpp), so no circular dependency issue exists.

wled00/fcn_declare.h (4)

5-6: Good refactoring - centralized color handling

Including the new colors.h header is a good approach for modularizing the codebase. This change aligns with the PR's objective to replace the FastLED library with custom color handling implementations.


396-402: Improved API design with standardized types

The new beat function declarations now use standard integer types (uint16_t) instead of the previous fixed-point types, which makes the API more consistent and easier to understand.


460-463: Consolidated trigonometric function macros

Good cleanup of the trigonometric function macros. This consolidation improves code clarity by clearly defining the approximation implementations to be used.


492-492: LGTM

Added whitespace improves readability by separating code sections.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com.>
Copy link

@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 (2)
wled00/FX.cpp (2)

2272-2273: Possible abrupt fade logic.

Using large jumps (e.g., jump from 40 to 8 when brightness crosses 28) may cause abrupt transitions in fade behavior. If this is unintentional, consider a smoother curve or clamp.


2656-2656: Potential overflow when blending.

Multiplying deltabright by 8 can exceed 255, causing wrap-around in the uint8_t cast. To avoid unexpected color blending, consider clamping to 255.

-SEGMENT.setPixelColor(i, color_blend(bg, c, uint8_t(deltabright * 8)));
+uint16_t blendVal = deltabright * 8;
+if (blendVal > 255) blendVal = 255;
+SEGMENT.setPixelColor(i, color_blend(bg, c, (uint8_t)blendVal));
🧹 Nitpick comments (2)
wled00/FX.cpp (2)

4180-4180: Remove commented-out code.

This line is redundant as it's identical to the uncommented line below it. Consider removing it entirely to improve code readability.

-//palettes[1] = CRGBPalette16(CHSV(baseI+hw_random8(64), 255, hw_random8(128,255)), CHSV(baseI+128, 255, hw_random8(128,255)), CHSV(baseI+hw_random8(92), 192, hw_random8(128,255)), CHSV(baseI+hw_random8(92), 255, hw_random8(128,255)));

4924-4924: Remove commented-out code.

Since active code is now using addPixelColorXY, this commented line is no longer needed. Consider removing it entirely to improve code readability.

-//SEGMENT.setPixelColorXY(dx, dy, grad ? color_fade(color, (255-rate), true) : color); // use addPixelColorXY for different look
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1cb1ee and 398fb21.

📒 Files selected for processing (1)
  • wled00/FX.cpp (20 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (12)
  • GitHub Check: wled_build / Build Environments (esp32dev)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_16MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32_wrover)
  • GitHub Check: wled_build / Build Environments (lolin_s2_mini)
  • GitHub Check: wled_build / Build Environments (esp32c3dev)
  • GitHub Check: wled_build / Build Environments (esp32s3dev_8MB_opi)
  • GitHub Check: wled_build / Build Environments (esp32s3_4M_qspi)
  • GitHub Check: wled_build / Build Environments (usermods)
  • GitHub Check: wled_build / Build Environments (esp32dev_V4)
  • GitHub Check: wled_build / Build Environments (esp32_eth)
  • GitHub Check: wled_build / Build Environments (esp8266_2m_160)
  • GitHub Check: wled_build / Build Environments (esp01_1m_full_160)
🔇 Additional comments (13)
wled00/FX.cpp (13)

16-17: New includes support the FastLED replacement strategy.

These new include files are essential components of the FastLED replacement strategy described in the PR objectives. The colors.h implements custom color handling, while prng.h provides the pseudo-random number generator functionality.


73-73: Initializing PRNG with hardware randomness is good practice.

Creating a static PRNG instance seeded with hardware-based randomness provides better initial entropy than using deterministic seeds. This approach ensures good randomization quality while keeping the generator accessible only within this file.


1761-1784: Effective PRNG state management for deterministic randomness.

The implementation correctly saves and restores the PRNG seed state, allowing this effect to maintain consistent randomization patterns while preventing interference with other effects. The approach of setting a specific seed for an effect and then restoring the previous seed afterward is a good pattern for maintaining deterministic randomness within effects.


2547-2547: CRGBW return type enables white channel support.

Changing the return type from CRGB to CRGBW enables support for the white channel, which is an improvement over the FastLED implementation that didn't natively support RGBW.


2582-2582: Simplified color initialization for CRGBW.

The code now initializes CRGBW colors more cleanly, including using c = 0 to represent black, which is a clear and efficient approach.

Also applies to: 2597-2597


2620-2627: Color scaling now uses custom function instead of FastLED's nscale8_video.

The replacement of FastLED's nscale8_video with the custom color_fade function provides equivalent functionality while supporting the new CRGBW type.


4148-4158: Good pattern for deterministic randomness in twinkleup effect.

The code correctly saves and restores the PRNG seed, while using a fixed seed (535) for consistent effect behavior. This pattern ensures reproducible randomness for visual consistency between runs.


5826-5828: PRNG used consistently for bee position initialization.

The code consistently replaces FastLED random functions with calls to the PRNG instance, maintaining the same functionality while eliminating the FastLED dependency.


7511-7511: Enhanced easing function improves visual quality.

Replacing ease8InOutApprox with ease8InOutCubic likely provides smoother, more aesthetically pleasing transitions as cubic easing tends to be more natural-looking than approximated easing functions.


7864-7864: Hardware random used for critical visual decision.

Using hardware randomness (hw_random()) instead of pseudo-random numbers for determining explosion type is a good choice as it provides true randomness for important visual variations.


9729-9729: Cosine function replacement for consistent calculations.

Replacing cos8() with cos8_t() ensures consistent trigonometric calculations across the codebase, aligning with the updated math utilities from the FastLED replacement.


10083-10083: Audio processing enhancement with mid-frequency energy calculation.

This new calculation for mid-frequency energy by averaging FFT bins 5-10 and applying a square root function is a good enhancement for audio-reactive effects. The comment also helpfully documents the frequency ranges.

Note that sqrt32_bw returns a uint32_t while the original sqrt16 returned a uint8_t. This difference in return types shouldn't cause issues here, but it's something to be aware of if this value is used in contexts expecting a narrower type.


5699-5699:

❓ Verification inconclusive

Right-shifting signed values may have undefined behavior.

Using right shift on signed integers ((int16_t)perlin8(...) - 127) >> 2) can lead to undefined behavior according to the C++ standard. While it works as expected on most architectures, consider using division or explicitly cast to unsigned before shifting.

-byte col = ((int16_t)perlin8(i * someVal, j * someVal, t) - 127) >> 2; // about +/- 32
+byte col = ((int16_t)perlin8(i * someVal, j * someVal, t) - 127) / 4; // about +/- 32

If performance is critical and you prefer to keep the bit-shift, ensure the behavior is consistent across all target platforms:


🏁 Script executed:

#!/bin/bash
# Check if the compiler used in this project defines right-shifting of negative values
echo "#include <stdio.h>
int main() {
  int16_t test = -128;
  int16_t result = test >> 2;
  printf(\"Right-shifting -128 by 2 gives: %d\\n\", result);
  printf(\"Division -128/4 gives: %d\\n\", -128/4);
  return 0;
}" > test_shift.c

# Try to compile and run
gcc test_shift.c -o test_shift
if [ $? -eq 0 ]; then
  ./test_shift
  rm test_shift test_shift.c
else
  echo "Failed to compile test program"
  rm test_shift.c
fi

Length of output: 451


Action Required: Address Undefined Behavior in Signed Right-Shift

The current code in wled00/FX.cpp uses a bit-shift operator on a signed integer:

byte col = ((int16_t)perlin8(i * someVal, j * someVal, t) - 127) >> 2; // about +/- 32

Right-shifting signed values can invoke undefined behavior according to the C++ standard. A safer, more portable alternative is to either use division (which is well-defined) or explicitly cast to an unsigned type before shifting.

For example, consider the following change:

- byte col = ((int16_t)perlin8(i * someVal, j * someVal, t) - 127) >> 2; // about +/- 32
+ byte col = ((int16_t)perlin8(i * someVal, j * someVal, t) - 127) / 4; // about +/- 32

A verification script was provided to check compiler behavior; however, it could not run due to the absence of gcc in the current environment. Please validate this change using your target compiler to ensure consistent behavior if maintaining the bit-shift for performance is critical.

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.

5 participants