Skip to content

Conversation

@softhack007
Copy link
Collaborator

@softhack007 softhack007 commented Nov 6, 2025

  • fixed a bug that caused wrong behavior with segment mirroring
    (effects must use virtualHeight() / virtualWidth() instead of height() / width())

  • added image blur as an option (second slider)

  • added very basic error reporting for users

  • up to 25% faster, especially with big, animated gifs

  • made all local variables "static" (don't pollute global namespace)

  • drawPixelCallback: cache calculations that do not depend on x/y position

  • reduced memory allocations on boards without PSRAM, to avoid crashes. This may lead to error messages when trying opening GIFs > 64x64 pixels, however boards without PSRAM are not able to handle more pixels any way.

Summary by CodeRabbit

  • New Features

    • Image/GIF playback: added an extra image parameter plus a Blur intensity; improved per-segment scaling/upscaling and memory-aware decoder behavior on low-RAM devices.
  • Bug Fixes

    • Image mode now runs only on 2D/matrix layouts and rejects tiny GIFs.
    • Robust filename validation, graceful failures for missing/unsupported files or low-memory, better decoding error handling and playback cleanup.

* fixed a bug that caused wrong behavior with segment mirroring
    (effects must use virtualHeight() /  virtualWidth() instead of height() / width())
* added image blur as an option (second slider)
* added very basic error reporting for users
*  up to 25% faster, especially with big animated gifs

* made all local variables "static" (don't pollute global namespace)
* drawPixelCallback: cache calculation that do not depend on x/y position
* reduced memory allocations on boards without PSRAM, to avoid crashes
@coderabbitai
Copy link

coderabbitai bot commented Nov 6, 2025

Walkthrough

Short-circuits image mode for non-matrix or very-small segments; updates FX_MODE_IMAGE metadata; refactors GIF/image rendering into file-local statics with PSRAM-aware decoder allocation, per-segment scaling/upscaling, new file I/O callbacks, enhanced error/OOM handling, painting optimizations, and thorough cleanup.

Changes

Cohort / File(s) Summary
mode_image guards & metadata
wled00/FX.cpp
Enforce 2D-only operation: return mode_oops() if not running on a matrix or if a segment's max virtual dimension < 4; update FX_MODE_IMAGE data string to add a Blur parameter and an ix parameter (keeping sx = 128).
Image/GIF renderer internals
wled00/image_loader.cpp
Move decoder, file, and state into file-local static variables; add per-segment state (segCols, segRows, expandX, expandY, lastX, lastY); compute per-segment up/down-scaling and implement nearest-neighbor mapping with bounds checks and last-pixel skip; add pre-flight GIF size checks, robust filename validation, improved decode/frame error mapping, PSRAM-aware decoder allocation with OOM handling, and comprehensive end-of-playback cleanup.
New decoder I/O callbacks & wiring
wled00/image_loader.cpp
Add and wire filePositionCallback(), fileReadCallback(), and fileReadBlockCallback(void*, int) to the GIF decoder I/O path; openGif() now logs and ensures proper file handling (close previous, validate suffix, log missing/unsupported files).

Sequence Diagram(s)

sequenceDiagram
    participant FX as FX::mode_image
    participant R as ImageLoader
    participant D as GIFDecoder
    participant P as draw2DPixelCallback
    participant U as updateScreenCallback

    FX->>FX: check matrix && max(virtW,virtH) >= 4
    alt not matrix or too small
        FX-->>FX: return mode_oops()
    else
        FX->>R: start render for segment
        R->>R: set segCols/segRows, compute expandX/expandY
        R->>D: openGif(filename) & alloc (PSRAM-aware)
        alt alloc fail or invalid file
            D-->>R: error / DECODER_ALLOC
            R-->>FX: abort playback
        else
            D->>P: decode -> draw2DPixelCallback(pixel)
            P->>P: nearest-neighbor map, bounds check, skip same pixel
            P->>R: paint into segment buffer
            loop per frame
                D->>U: frame complete -> timing
                U->>R: optional blur, reset last pos
            end
            R->>R: cleanup (close file, dealloc, reset state)
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas to review closely:
    • expandX/expandY computation and integer rounding for up/downscaling.
    • draw2DPixelCallback bounds checks and last-position skipping correctness.
    • PSRAM vs non-PSRAM decoder allocation and exception/OOM handling paths.
    • File I/O callback correctness (position/read/block) and proper file lifecycle (open/close) in edge cases.
    • endImagePlayback state cleanup to avoid stale filenames or leaked decoder state.

Possibly related PRs

  • GIF Playback #226 — overlaps GIF/image playback changes and touches mode_image and image loader callback/decoder wiring.

Poem

🐰 I hop through frames both big and small,

I scale each pixel, skip repeats, and call,
My static burrow holds decoder and file,
I blur and paint the matrix with a smile,
Hooray — GIFs dance across the tile! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main changes: gifdecoder bugfixes, performance improvements (speedup), and minor enhancements, all of which are reflected in the raw summary and PR objectives.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Gif_MM_plus

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

❤️ Share

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

@softhack007 softhack007 changed the title gifdecoder bugfixes, speedup and minor improvements (WIP) gifdecoder bugfixes, 25% speedup, and minor improvements Nov 6, 2025
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
wled00/FX.cpp (1)

142-145: Add WLED_DISABLE_2D guard around Akemi fallback to prevent build breaks

Calling mode_2DAkemi_core(false) unguarded will fail with -D WLED_DISABLE_2D, since Akemi isn’t compiled in that config. Restore the guard to match intended behavior.

Apply this diff:

   // 2D fallback: akemi in blue
-  if (SEGMENT.is2D() && (width > 3) && (height > 3))
-    return mode_2DAkemi_core(false);
+  #ifndef WLED_DISABLE_2D
+  if (SEGMENT.is2D() && (width > 3) && (height > 3))
+    return mode_2DAkemi_core(false);
+  #endif

Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5177ebd and 10b3ac0.

📒 Files selected for processing (2)
  • wled00/FX.cpp (2 hunks)
  • wled00/image_loader.cpp (5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
wled00/**/*.{cpp,h}

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

Use spaces (2 per level) for C++ firmware source and headers in wled00

Files:

  • wled00/FX.cpp
  • wled00/image_loader.cpp
🧠 Learnings (3)
📓 Common learnings
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-02T22:07:15.689Z
Learning: In MoonModules/WLED-MM, WLEDMM_FASTPATH will become the default code path. Code inside "#ifndef WLEDMM_FASTPATH" blocks represents deprecated paths that are being phased out and should be ignored during reviews.
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h:155-157
Timestamp: 2025-10-22T21:50:25.839Z
Learning: In WLED-MM PR #270, only Effect (Mode) IDs are migrated to 16-bit; Palette IDs and counts remain 8-bit. Usermod code should widen mode-related indices/loops to uint16_t while keeping palette-related indices/loops as uint8_t.
Learnt from: DedeHai
Repo: MoonModules/WLED-MM PR: 253
File: usermods/audioreactive/audio_source.h:1256-1261
Timestamp: 2025-07-12T04:20:14.546Z
Learning: In WLED AudioReactive usermod, DC offset removal optimization in DMAadcSource::getSamples() should be implemented after PR #248 merges, which will change the sample buffer from float to int16. This eliminates the need for temporary integer buffers and allows direct integer arithmetic on the native buffer type for better memory efficiency on resource-constrained MCUs like C3 and S2.
📚 Learning: 2025-11-02T22:17:26.380Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-02T22:17:26.380Z
Learning: In wled00/FX.cpp, the `#ifndef WLED_DISABLE_2D` guard around the Akemi 2D fallback in the `mode_oops()` function (lines 142-146) is intentionally kept. This guard is necessary because when users build with `-D WLED_DISABLE_2D` to remove all 2D effects from the build, the Akemi effect is also not compiled, so the guard prevents calling a non-existent function.

Applied to files:

  • wled00/FX.cpp
📚 Learning: 2025-10-22T21:50:25.839Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h:155-157
Timestamp: 2025-10-22T21:50:25.839Z
Learning: In WLED-MM PR #270, only Effect (Mode) IDs are migrated to 16-bit; Palette IDs and counts remain 8-bit. Usermod code should widen mode-related indices/loops to uint16_t while keeping palette-related indices/loops as uint8_t.

Applied to files:

  • wled00/FX.cpp
🧬 Code graph analysis (2)
wled00/FX.cpp (1)
wled00/FX_fcn.cpp (1)
  • SEGMENT (737-812)
wled00/image_loader.cpp (1)
wled00/FX_fcn.cpp (4)
  • i (714-724)
  • i (714-714)
  • i (725-731)
  • i (725-725)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Builds (athom_music_esp32_4MB_M)
  • GitHub Check: Builds (esp32c3mini_dio_4MB_M)
  • GitHub Check: Builds (esp8266_4MB_S)
  • GitHub Check: Builds (esp8266_2MB_S)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_M)
  • GitHub Check: Builds (esp32S3_8MB_PSRAM_M)
  • GitHub Check: Builds (esp32S3_8MB_M)
  • GitHub Check: Builds (adafruit_matrixportal_esp32s3_tinyUF2)
  • GitHub Check: Builds (esp32S3_4MB_S)
  • GitHub Check: Builds (esp32s2_PSRAM_S)
  • GitHub Check: Builds (esp32S3_WROOM-2_M)
  • GitHub Check: Builds (esp32s2_PSRAM_M)
  • GitHub Check: Builds (esp32S3_16MB_PSRAM_M_HUB75)
  • GitHub Check: Builds (esp32c3dev_2MB_M)
  • GitHub Check: Builds (esp32S3_8MB_S)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32_16MB_V4_M)
  • GitHub Check: Builds (esp32_4MB_V4_HUB75_forum)
  • GitHub Check: Builds (esp32_4MB_V4_S)
  • GitHub Check: Builds (esp32_16MB_V4_S)
🔇 Additional comments (10)
wled00/FX.cpp (2)

4696-4703: 2D-only gate and small-matrix bail look good

  • Enforces matrix-only use and avoids tiny panels; switches to virtualWidth()/virtualHeight() as required by mirroring fix. LGTM.

Minor: confirm the cutoff “< 4” is intentional (Akemi fallback uses >3). If 3×N matrices are supported elsewhere, consider aligning thresholds.


4709-4709: FX string: Blur slider added—confirm mapping and defaults

“Image@!,Blur,;;;12;sx=128,ix=0” adds Blur as the second slider. Please verify:

  • Which field the renderer reads for blur (Intensity vs customN), and that this label matches it.
  • That ix=0 is the desired default (not overly blurring or disabling filtering inadvertently).

If the renderer expects a custom slider, consider relabeling/mapping accordingly (e.g., c2) to avoid user confusion.

wled00/image_loader.cpp (8)

12-20: LGTM! Static declarations reduce namespace pollution.

The refactoring to static linkage encapsulates the decoder state properly and the PSRAM-conditional sizing helps prevent OOM crashes on constrained boards.


42-56: LGTM! Good observability and consistent static pattern.

The side-effect comment and debug output improve maintainability. The additional static rendering state variables follow the same encapsulation pattern.


62-71: LGTM! Well-designed blur feature with performance optimization.

The conditional blur strategy (fast 1D blurRows for low intensity, slower 2D blur for high intensity) provides a good balance between visual quality and performance. The lastX/lastY invalidation correctly resets the downscaling optimization state.


82-87: LGTM! Loop reordering improves cache locality.

The Y-outer/X-inner loop order is optimal for row-major pixel storage, which is typical for LED matrices. This should provide the claimed performance improvement.


108-109: LGTM! Correct fix for segment mirroring.

Using virtualWidth() and virtualHeight() instead of width() and height() properly accounts for segment transformations including mirroring. This addresses the bug mentioned in the PR objectives.


116-125: LGTM! Improved error reporting for users.

The enhanced error messages with USER_PRINTF provide better feedback when GIFs fail to load or have unsupported formats, addressing the "basic error reporting" goal mentioned in the PR.


166-178: LGTM! Robust frame decode error handling and timing compensation.

The frame decode error handling (lines 166-170) properly logs failures and returns appropriate error codes. The frame timing compensation (lines 173-174) correctly prevents integer underflow when frames run longer than intended, with the ternary operator providing safe clamping to zero.


134-146: Rewrite issue: decoder.alloc() OOM error handling is unimplemented, despite acknowledged risk

The review comment's analysis is accurate. While the code acknowledges that decoder.alloc() may throw OOM and crash (line 134), there is no error handling in place. The commented-out check at line 151 (//if (!decoder)...) shows a previous attempt to handle allocation failures, but it was disabled or removed. Currently, only startDecoding() errors are caught (lines 138-145); alloc() failures bypass all error handling.

Since GifDecoder.h is an external library not present in the repository, the solution depends on whether its alloc() method supports error recovery (return codes or exceptions). Recommend either:

  • Wrapping decoder.alloc() in try-catch if the library can throw exceptions, or
  • Investigating why the line 151 check was disabled and restoring appropriate error handling if the library provides error detection.

catch error early before starting the decode/paint loop
@softhack007 softhack007 changed the title (WIP) gifdecoder bugfixes, 25% speedup, and minor improvements (WIP) gifdecoder bugfixes, 30% speedup, and minor improvements Nov 6, 2025
... by adding an exception handler that will catch OOM inside decoder.alloc().
not tested yet.
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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 698da84 and 25391fe.

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

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

Use spaces (2 per level) for C++ firmware source and headers in wled00

Files:

  • wled00/image_loader.cpp
🧠 Learnings (3)
📓 Common learnings
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-02T22:07:15.689Z
Learning: In MoonModules/WLED-MM, WLEDMM_FASTPATH will become the default code path. Code inside "#ifndef WLEDMM_FASTPATH" blocks represents deprecated paths that are being phased out and should be ignored during reviews.
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h:155-157
Timestamp: 2025-10-22T21:50:25.839Z
Learning: In WLED-MM PR #270, only Effect (Mode) IDs are migrated to 16-bit; Palette IDs and counts remain 8-bit. Usermod code should widen mode-related indices/loops to uint16_t while keeping palette-related indices/loops as uint8_t.
Learnt from: DedeHai
Repo: MoonModules/WLED-MM PR: 253
File: usermods/audioreactive/audio_source.h:1256-1261
Timestamp: 2025-07-12T04:20:14.546Z
Learning: In WLED AudioReactive usermod, DC offset removal optimization in DMAadcSource::getSamples() should be implemented after PR #248 merges, which will change the sample buffer from float to int16. This eliminates the need for temporary integer buffers and allows direct integer arithmetic on the native buffer type for better memory efficiency on resource-constrained MCUs like C3 and S2.
📚 Learning: 2025-10-22T22:24:46.163Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/json.cpp:295-304
Timestamp: 2025-10-22T22:24:46.163Z
Learning: When deriving an upper-bound index from a count in WLED-MM PR #270, use a safe guard: last = count ? count - 1 : 0. Avoid min/max with 0 on unsigned types and never pass (count-1) directly to helpers (e.g., updateVal16, relativeChange, random8) without guarding. Effect IDs are 16-bit; palette IDs remain 8-bit but should use the same guard when count can be 0.

Applied to files:

  • wled00/image_loader.cpp
📚 Learning: 2025-10-22T21:59:24.331Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.

Applied to files:

  • wled00/image_loader.cpp
🧬 Code graph analysis (1)
wled00/image_loader.cpp (1)
wled00/FX_fcn.cpp (4)
  • i (714-724)
  • i (714-714)
  • i (725-731)
  • i (725-725)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Builds (esp32_4MB_V4_S)
  • GitHub Check: Builds (esp32s2_PSRAM_M)
  • GitHub Check: Builds (esp8266pro_16MB_M)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_S)
  • GitHub Check: Builds (athom_music_esp32_4MB_M)
  • GitHub Check: Builds (esp32S3_16MB_PSRAM_M_HUB75)
  • GitHub Check: Builds (esp32S3_8MB_M)
  • GitHub Check: Builds (seeed_esp32c3_4MB_S)
  • GitHub Check: Builds (esp32c3dev_4MB_M)
  • GitHub Check: Builds (esp32c3mini_dio_4MB_M)
  • GitHub Check: Builds (esp32s2_PSRAM_S)
  • GitHub Check: Builds (esp32S3_4MB_S)
  • GitHub Check: Builds (esp32c3dev_2MB_M)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_M)
  • GitHub Check: Builds (esp32_16MB_V4_M)
  • GitHub Check: Builds (esp32S3_WROOM-2_M)
  • GitHub Check: Builds (esp32S3_8MB_S)
  • GitHub Check: Builds (esp32_4MB_V4_HUB75_forum)
  • GitHub Check: Builds (esp32_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32_16MB_V4_S)

if seg.name is empty or shorter than four characters, ``strlen(lastFilename) - 4``  underflows (size_t), so the pointer passed to strcmp lands far before the buffer and triggers undefined behavior.
This patch catches too-short segment names and aborts decoding.
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 (1)
wled00/image_loader.cpp (1)

114-119: Complete the extension check fix by using the cached length.

Line 115 computes fnameLen but then calls strlen(lastFilename) again in the strcmp. This is inefficient and defeats the purpose of caching the length.

Apply this diff:

     size_t fnameLen = strlen(lastFilename);
-    if ((fnameLen < 4) || strcmp(lastFilename + strlen(lastFilename) - 4, ".gif") != 0) {
+    if ((fnameLen < 4) || strcmp(lastFilename + fnameLen - 4, ".gif") != 0) {
       gifDecodeFailed = true;
🧹 Nitpick comments (1)
wled00/image_loader.cpp (1)

135-158: Robust error handling with helpful user messaging.

The exception handling for decoder.alloc() (lines 135-146) and comprehensive error checking for decoder.startDecoding() (lines 149-156) significantly improve reliability. The specific error codes and user-facing messages align well with the PR's objective of adding basic error reporting.

Minor suggestion: Line 143's message "I'm going to shoot myself now" could be more professional:

-      USER_PRINTLN("\nGIF decoder out of memory. I'm going to shoot myself now.\n");
+      USER_PRINTLN("GIF decoder out of memory\n");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 25391fe and 9564791.

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

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

Use spaces (2 per level) for C++ firmware source and headers in wled00

Files:

  • wled00/image_loader.cpp
🧠 Learnings (3)
📓 Common learnings
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-02T22:07:15.689Z
Learning: In MoonModules/WLED-MM, WLEDMM_FASTPATH will become the default code path. Code inside "#ifndef WLEDMM_FASTPATH" blocks represents deprecated paths that are being phased out and should be ignored during reviews.
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h:155-157
Timestamp: 2025-10-22T21:50:25.839Z
Learning: In WLED-MM PR #270, only Effect (Mode) IDs are migrated to 16-bit; Palette IDs and counts remain 8-bit. Usermod code should widen mode-related indices/loops to uint16_t while keeping palette-related indices/loops as uint8_t.
Learnt from: DedeHai
Repo: MoonModules/WLED-MM PR: 253
File: usermods/audioreactive/audio_source.h:1256-1261
Timestamp: 2025-07-12T04:20:14.546Z
Learning: In WLED AudioReactive usermod, DC offset removal optimization in DMAadcSource::getSamples() should be implemented after PR #248 merges, which will change the sample buffer from float to int16. This eliminates the need for temporary integer buffers and allows direct integer arithmetic on the native buffer type for better memory efficiency on resource-constrained MCUs like C3 and S2.
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-02T22:17:26.380Z
Learning: In wled00/FX.cpp, the `#ifndef WLED_DISABLE_2D` guard around the Akemi 2D fallback in the `mode_oops()` function (lines 142-146) is intentionally kept. This guard is necessary because when users build with `-D WLED_DISABLE_2D` to remove all 2D effects from the build, the Akemi effect is also not compiled, so the guard prevents calling a non-existent function.
📚 Learning: 2025-10-22T22:24:46.163Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/json.cpp:295-304
Timestamp: 2025-10-22T22:24:46.163Z
Learning: When deriving an upper-bound index from a count in WLED-MM PR #270, use a safe guard: last = count ? count - 1 : 0. Avoid min/max with 0 on unsigned types and never pass (count-1) directly to helpers (e.g., updateVal16, relativeChange, random8) without guarding. Effect IDs are 16-bit; palette IDs remain 8-bit but should use the same guard when count can be 0.

Applied to files:

  • wled00/image_loader.cpp
📚 Learning: 2025-10-22T21:59:24.331Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.

Applied to files:

  • wled00/image_loader.cpp
🧬 Code graph analysis (1)
wled00/image_loader.cpp (1)
wled00/FX_fcn.cpp (4)
  • i (714-724)
  • i (714-714)
  • i (725-731)
  • i (725-725)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: Builds (esp32S3_8MB_M)
  • GitHub Check: Builds (athom_music_esp32_4MB_M)
  • GitHub Check: Builds (adafruit_matrixportal_esp32s3_tinyUF2)
  • GitHub Check: Builds (esp32c3dev_4MB_M)
  • GitHub Check: Builds (esp8266_4MB_M)
  • GitHub Check: Builds (esp32s2_PSRAM_S)
  • GitHub Check: Builds (esp8266pro_16MB_M)
  • GitHub Check: Builds (esp32s2_PSRAM_M)
  • GitHub Check: Builds (seeed_esp32c3_4MB_S)
  • GitHub Check: Builds (esp32S3_16MB_PSRAM_M_HUB75)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32S3_WROOM-2_M)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_M)
  • GitHub Check: Builds (esp32S3_8MB_PSRAM_M)
  • GitHub Check: Builds (esp32S3_8MB_M)
  • GitHub Check: Builds (esp32S3_4MB_S)
  • GitHub Check: Builds (esp32S3_8MB_S)
  • GitHub Check: Builds (esp32_16MB_V4_M)
  • GitHub Check: Builds (esp32_4MB_V4_S)
  • GitHub Check: Builds (esp32_16MB_V4_S)
🔇 Additional comments (6)
wled00/image_loader.cpp (6)

12-20: Good namespace hygiene with static scoping.

Moving these variables to file-local static scope prevents global namespace pollution and aligns with the PR objectives. The PSRAM-aware decoder sizing is a sensible approach to avoid OOM crashes on constrained boards.


50-56: Static rendering state variables support the performance optimizations.

These file-local statics cache rendering state and enable the optimizations described in the PR (especially the lastX/lastY pixel-skip optimization and pre-calculated expand factors). The comment explaining why gifWidth and gifHeight remain uint16_t is helpful.


62-71: Blur functionality implementation looks solid.

The two-tier blur approach (fast blurRows for smaller amounts on 2D matrices, slower blur for larger amounts) is a good performance trade-off. Invalidating the lastX/lastY cache after blur ensures correctness for the next frame.


72-88: Downscaling and upscaling optimizations are well-implemented.

The nearest-neighbor downscaling with the lastX/lastY skip optimization effectively reduces redundant pixel writes. The unsigned cast at line 77 elegantly handles both negative coordinates and out-of-bounds checks. The loop order change for better cache locality is a nice touch.


108-109: Correct fix for segment mirroring bug.

Using virtualWidth() and virtualHeight() instead of width() and height() properly accounts for segment mirroring, directly addressing the bug mentioned in the PR objectives.


172-182: Division by zero guard successfully addresses past review concerns.

The dimension validation at lines 174-178 properly protects against division by zero in both the expandX/expandY calculations (lines 180-181) and the drawPixelCallback divisions (lines 74-75). The comprehensive error handling with user-facing messages completes the fix from previous review rounds.

* use camelCase for local variables
* furure support: rename drawPixelCallback to draw2DPixelCallback
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/image_loader.cpp (2)

115-116: Redundant strlen() call after computing fnameLen.

Line 115 computes fnameLen but line 116 calls strlen(lastFilename) again. Use the cached value for efficiency.

Apply this diff:

     size_t fnameLen = strlen(lastFilename);
-    if ((fnameLen < 4) || strcmp(lastFilename + strlen(lastFilename) - 4, ".gif") != 0) {
+    if ((fnameLen < 4) || strcmp(lastFilename + fnameLen - 4, ".gif") != 0) {

136-147: Exception handling is well-structured, but error message may alarm users.

The try-catch block correctly handles OOM during allocation. However, the message "I'm going to shoot myself now" (line 144) might unnecessarily alarm users. Consider a more reassuring message like "GIF decoder out of memory. Please try a smaller image or free up memory."

Apply this diff:

-      USER_PRINTLN("\nGIF decoder out of memory. I'm going to shoot myself now.\n");
+      USER_PRINTLN("GIF decoder out of memory. Please try a smaller image.");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9564791 and f955df0.

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

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

Use spaces (2 per level) for C++ firmware source and headers in wled00

Files:

  • wled00/image_loader.cpp
🧠 Learnings (3)
📓 Common learnings
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-02T22:07:15.689Z
Learning: In MoonModules/WLED-MM, WLEDMM_FASTPATH will become the default code path. Code inside "#ifndef WLEDMM_FASTPATH" blocks represents deprecated paths that are being phased out and should be ignored during reviews.
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h:155-157
Timestamp: 2025-10-22T21:50:25.839Z
Learning: In WLED-MM PR #270, only Effect (Mode) IDs are migrated to 16-bit; Palette IDs and counts remain 8-bit. Usermod code should widen mode-related indices/loops to uint16_t while keeping palette-related indices/loops as uint8_t.
Learnt from: DedeHai
Repo: MoonModules/WLED-MM PR: 253
File: usermods/audioreactive/audio_source.h:1256-1261
Timestamp: 2025-07-12T04:20:14.546Z
Learning: In WLED AudioReactive usermod, DC offset removal optimization in DMAadcSource::getSamples() should be implemented after PR #248 merges, which will change the sample buffer from float to int16. This eliminates the need for temporary integer buffers and allows direct integer arithmetic on the native buffer type for better memory efficiency on resource-constrained MCUs like C3 and S2.
📚 Learning: 2025-10-22T22:24:46.163Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/json.cpp:295-304
Timestamp: 2025-10-22T22:24:46.163Z
Learning: When deriving an upper-bound index from a count in WLED-MM PR #270, use a safe guard: last = count ? count - 1 : 0. Avoid min/max with 0 on unsigned types and never pass (count-1) directly to helpers (e.g., updateVal16, relativeChange, random8) without guarding. Effect IDs are 16-bit; palette IDs remain 8-bit but should use the same guard when count can be 0.

Applied to files:

  • wled00/image_loader.cpp
📚 Learning: 2025-10-22T21:59:24.331Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.

Applied to files:

  • wled00/image_loader.cpp
🧬 Code graph analysis (1)
wled00/image_loader.cpp (1)
wled00/FX_fcn.cpp (4)
  • i (714-724)
  • i (714-714)
  • i (725-731)
  • i (725-725)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: Builds (esp32s2_PSRAM_M)
  • GitHub Check: Builds (esp32S3_8MB_S)
  • GitHub Check: Builds (esp32S3_8MB_PSRAM_M)
  • GitHub Check: Builds (esp32_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32_4MB_V4_S)
  • GitHub Check: Builds (esp32_4MB_V4_HUB75_forum)
  • GitHub Check: Builds (seeed_esp32c3_4MB_S)
  • GitHub Check: Builds (adafruit_matrixportal_esp32s3_tinyUF2)
  • GitHub Check: Builds (esp32s2_PSRAM_M)
  • GitHub Check: Builds (esp32S3_4MB_S)
  • GitHub Check: Builds (esp32S3_16MB_PSRAM_M_HUB75)
  • GitHub Check: Builds (esp32S3_8MB_S)
  • GitHub Check: Builds (esp32S3_8MB_PSRAM_M)
  • GitHub Check: Builds (esp32S3_WROOM-2_M)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_M)
  • GitHub Check: Builds (esp32_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32S3_8MB_M)
  • GitHub Check: Builds (esp32_16MB_V4_M)
🔇 Additional comments (10)
wled00/image_loader.cpp (10)

12-20: PSRAM-aware decoder configuration looks good.

The conditional compilation correctly selects smaller decoder parameters for boards without PSRAM, reducing memory pressure and preventing crashes.


26-36: File I/O callbacks are correctly implemented.

The callbacks provide a clean interface between the decoder and the file system, delegating to the static file object.


42-48: Enhanced logging in openGif improves debugging.

The addition of the filename to debug output helps track file operations.


50-57: Per-segment rendering state properly encapsulated.

The static variables track rendering state across frames while avoiding global namespace pollution, aligning with the PR's objectives.


63-72: Blur implementation elegantly uses the intensity slider.

The two-tier approach (fast blurRows for light blur on 2D, slower blur for heavier amounts) balances performance and quality. Resetting lastX/lastY ensures downscaling cache is fresh for the next frame.


73-88: Downscaling optimization and cache locality improvements are effective.

The lastX/lastY cache (line 78) correctly skips redundant pixel writes during downscaling. The Y-outer/X-inner loop order (lines 83-84) improves cache locality for row-major memory layouts. The unsigned cast bounds check (line 77) elegantly handles both negative values and out-of-range coordinates.


108-110: Critical fix: using virtualWidth/virtualHeight correctly handles segment mirroring.

This addresses the bug mentioned in the PR objectives. The virtual dimensions account for mirroring and rotation, ensuring correct rendering.


150-158: Comprehensive error handling with appropriate error flag mapping.

The code correctly captures decoder errors, logs them, and maps specific error codes (TOO_WIDE, UNSUPPORTED_FEATURE, INVALID_PARAMETER) to memory error flags. Line 158 properly resets previous memory error flags on success.


173-182: Zero-dimension guard resolves division-by-zero risks.

The validation at lines 175-179 prevents division by zero in both the draw2DPixelCallback (lines 75-76) and the upscaling calculations (lines 181-182). The ceiling division pattern correctly ensures proper pixel coverage during upscaling.


185-189: Frame decode error handling is consistent and complete.

The error handling properly captures decode failures, sets the failure flag, logs the error code, and returns the appropriate error constant.

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f955df0 and 9115cd1.

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

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

Use spaces (2 per level) for C++ firmware source and headers in wled00

Files:

  • wled00/image_loader.cpp
🧠 Learnings (3)
📓 Common learnings
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-02T22:07:15.689Z
Learning: In MoonModules/WLED-MM, WLEDMM_FASTPATH will become the default code path. Code inside "#ifndef WLEDMM_FASTPATH" blocks represents deprecated paths that are being phased out and should be ignored during reviews.
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h:155-157
Timestamp: 2025-10-22T21:50:25.839Z
Learning: In WLED-MM PR #270, only Effect (Mode) IDs are migrated to 16-bit; Palette IDs and counts remain 8-bit. Usermod code should widen mode-related indices/loops to uint16_t while keeping palette-related indices/loops as uint8_t.
Learnt from: DedeHai
Repo: MoonModules/WLED-MM PR: 253
File: usermods/audioreactive/audio_source.h:1256-1261
Timestamp: 2025-07-12T04:20:14.546Z
Learning: In WLED AudioReactive usermod, DC offset removal optimization in DMAadcSource::getSamples() should be implemented after PR #248 merges, which will change the sample buffer from float to int16. This eliminates the need for temporary integer buffers and allows direct integer arithmetic on the native buffer type for better memory efficiency on resource-constrained MCUs like C3 and S2.
📚 Learning: 2025-10-22T22:24:46.163Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/json.cpp:295-304
Timestamp: 2025-10-22T22:24:46.163Z
Learning: When deriving an upper-bound index from a count in WLED-MM PR #270, use a safe guard: last = count ? count - 1 : 0. Avoid min/max with 0 on unsigned types and never pass (count-1) directly to helpers (e.g., updateVal16, relativeChange, random8) without guarding. Effect IDs are 16-bit; palette IDs remain 8-bit but should use the same guard when count can be 0.

Applied to files:

  • wled00/image_loader.cpp
📚 Learning: 2025-10-22T21:59:24.331Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.

Applied to files:

  • wled00/image_loader.cpp
🧬 Code graph analysis (1)
wled00/image_loader.cpp (1)
wled00/FX_fcn.cpp (4)
  • i (714-724)
  • i (714-714)
  • i (725-731)
  • i (725-725)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
  • GitHub Check: Builds (esp32c3dev_4MB_M)
  • GitHub Check: Builds (esp32S3_4MB_S)
  • GitHub Check: Builds (esp01_1MB_S)
  • GitHub Check: Builds (esp8266pro_16MB_M)
  • GitHub Check: Builds (esp32c3mini_dio_4MB_M)
  • GitHub Check: Builds (esp32c3dev_4MB_M)
  • GitHub Check: Builds (athom_music_esp32_4MB_M)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_M)
  • GitHub Check: Builds (esp32s2_PSRAM_M)
  • GitHub Check: Builds (esp32S3_WROOM-2_M)
  • GitHub Check: Builds (esp32S3_16MB_PSRAM_M_HUB75)
  • GitHub Check: Builds (esp32S3_8MB_PSRAM_M)
  • GitHub Check: Builds (esp32s2_PSRAM_S)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32_16MB_V4_M)
  • GitHub Check: Builds (esp32S3_4MB_S)
  • GitHub Check: Builds (esp32_16MB_V4_S)
  • GitHub Check: Builds (esp32_4MB_V4_HUB75_forum)
  • GitHub Check: Builds (esp32_4MB_V4_S)

If seg.name is at least 32 characters long, strncpy will fill the 32-byte window without appending '\0'. This could lead to sporadic errors.
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

🧹 Nitpick comments (1)
wled00/image_loader.cpp (1)

66-69: Clarify the threshold for blur method selection.

The magic number 24 determines whether to use fast blurRows or slower blur. Consider extracting this to a named constant or adding a comment explaining why 24 is the threshold.

-  if ((blurAmount < 24) && (activeSeg->is2D())) activeSeg->blurRows(activeSeg->intensity >> 1);  // some blur - fast
+  const uint8_t BLUR_THRESHOLD = 24;  // threshold between fast row-based blur and full 2D blur
+  if ((blurAmount < BLUR_THRESHOLD) && (activeSeg->is2D())) activeSeg->blurRows(activeSeg->intensity >> 1);  // some blur - fast
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9115cd1 and c58c6ef.

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

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

Use spaces (2 per level) for C++ firmware source and headers in wled00

Files:

  • wled00/image_loader.cpp
🧠 Learnings (3)
📓 Common learnings
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 0
File: :0-0
Timestamp: 2025-11-02T22:07:15.689Z
Learning: In MoonModules/WLED-MM, WLEDMM_FASTPATH will become the default code path. Code inside "#ifndef WLEDMM_FASTPATH" blocks represents deprecated paths that are being phased out and should be ignored during reviews.
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h:155-157
Timestamp: 2025-10-22T21:50:25.839Z
Learning: In WLED-MM PR #270, only Effect (Mode) IDs are migrated to 16-bit; Palette IDs and counts remain 8-bit. Usermod code should widen mode-related indices/loops to uint16_t while keeping palette-related indices/loops as uint8_t.
Learnt from: DedeHai
Repo: MoonModules/WLED-MM PR: 253
File: usermods/audioreactive/audio_source.h:1256-1261
Timestamp: 2025-07-12T04:20:14.546Z
Learning: In WLED AudioReactive usermod, DC offset removal optimization in DMAadcSource::getSamples() should be implemented after PR #248 merges, which will change the sample buffer from float to int16. This eliminates the need for temporary integer buffers and allows direct integer arithmetic on the native buffer type for better memory efficiency on resource-constrained MCUs like C3 and S2.
📚 Learning: 2025-10-22T22:24:46.163Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/json.cpp:295-304
Timestamp: 2025-10-22T22:24:46.163Z
Learning: When deriving an upper-bound index from a count in WLED-MM PR #270, use a safe guard: last = count ? count - 1 : 0. Avoid min/max with 0 on unsigned types and never pass (count-1) directly to helpers (e.g., updateVal16, relativeChange, random8) without guarding. Effect IDs are 16-bit; palette IDs remain 8-bit but should use the same guard when count can be 0.

Applied to files:

  • wled00/image_loader.cpp
📚 Learning: 2025-10-22T21:59:24.331Z
Learnt from: softhack007
Repo: MoonModules/WLED-MM PR: 270
File: wled00/fcn_declare.h:403-406
Timestamp: 2025-10-22T21:59:24.331Z
Learning: In MoonModules/WLED-MM PR #270, the out-of-bounds null-terminator write in wled00/util.cpp (extractModeSlider) is deferred and tracked in Issue #272; do not address it within PR #270.

Applied to files:

  • wled00/image_loader.cpp
🧬 Code graph analysis (1)
wled00/image_loader.cpp (1)
wled00/FX_fcn.cpp (4)
  • i (714-724)
  • i (714-714)
  • i (725-731)
  • i (725-725)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (18)
  • GitHub Check: Builds (esp32S3_4MB_S)
  • GitHub Check: Builds (esp32S3_8MB_M)
  • GitHub Check: Builds (esp32_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32_16MB_V4_S)
  • GitHub Check: Builds (esp32s2_PSRAM_M)
  • GitHub Check: Builds (adafruit_matrixportal_esp32s3_tinyUF2)
  • GitHub Check: Builds (athom_music_esp32_4MB_M)
  • GitHub Check: Builds (esp32s2_PSRAM_S)
  • GitHub Check: Builds (esp32c3dev_4MB_M)
  • GitHub Check: Builds (esp32_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32S3_4MB_PSRAM_S)
  • GitHub Check: Builds (esp32S3_16MB_PSRAM_M_HUB75)
  • GitHub Check: Builds (esp32_16MB_V4_M)
  • GitHub Check: Builds (esp32S3_4MB_S)
  • GitHub Check: Builds (esp32S3_8MB_PSRAM_M)
  • GitHub Check: Builds (esp32S3_8MB_S)
  • GitHub Check: Builds (esp32S3_8MB_M)
  • GitHub Check: Builds (esp32_16MB_V4_S)
🔇 Additional comments (9)
wled00/image_loader.cpp (9)

12-20: LGTM: Static scope and PSRAM-aware decoder sizing.

The conversion to static scope prevents namespace pollution, and the conditional decoder template parameters appropriately reduce memory usage on boards without PSRAM.


42-48: LGTM: Debug logging and clear side-effect documentation.

The added logging aids troubleshooting, and the comment clearly documents the function's side-effect on the static file variable.


50-57: LGTM: Cached rendering state enables the performance optimizations.

These static variables support the per-segment scaling and "cached calculations that do not depend on x/y position" mentioned in the PR objectives.


73-88: LGTM: Downscaling logic is correct with guards in place.

The division by gifWidth/gifHeight is now safe thanks to the zero-dimension guard at lines 176-180. The unsigned cast for bounds checking is elegant, the lastX/lastY optimization reduces redundant writes, and the reordered loops improve cache locality.


108-110: LGTM: Correct fix for segment mirroring bug.

Using virtualWidth() and virtualHeight() instead of width() and height() properly accounts for segment mirroring, as noted in the PR objectives.


114-128: LGTM: File validation now safe and user-friendly.

The explicit null termination (line 114) and length guard (line 116) address the critical issues from past reviews. The enhanced error messages support the "very basic error reporting for users" mentioned in the PR objectives.


151-159: LGTM: Comprehensive error handling for decoder initialization.

The error code capture, specific error-to-flag mapping, and flag reset on success provide good error reporting as mentioned in the PR objectives.


175-183: LGTM: Division by zero guard addresses past critical issues.

The zero-dimension validation (lines 176-180) prevents division by zero in both draw2DPixelCallback and the expandX/expandY calculations, resolving the critical issues from previous reviews.


186-190: LGTM: Frame decode error handling is consistent and thorough.

Proper error detection, flag setting, logging, and error code return.

no fun, just rabbits. Lets stay professional.
gifDecodeFailed = false;
if (strcmp(lastFilename + strlen(lastFilename) - 4, ".gif") != 0) {
size_t fnameLen = strlen(lastFilename);
if ((fnameLen < 4) || strcmp(lastFilename + fnameLen - 4, ".gif") != 0) { // empty segment name, name too short, or name not ending in .gif
Copy link
Collaborator Author

@softhack007 softhack007 Nov 7, 2025

Choose a reason for hiding this comment

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

open ends (will not fix in this PR):

  • compare should be case-insensitive - ".GIF", ".Gif", ".gIf" should be accepted.
    esp32 does not have "stricmp", so we might need a custom solution, maybe with a utility function that internally uses tolower(c) before comparing.
  • strip leading / trailing blanks from the filename.

@softhack007 softhack007 changed the title (WIP) gifdecoder bugfixes, 30% speedup, and minor improvements gifdecoder bugfixes, 30% speedup, and minor improvements Nov 7, 2025
@softhack007 softhack007 changed the title gifdecoder bugfixes, 30% speedup, and minor improvements gifdecoder bugfixes, up to 30% speedup, and minor improvements Nov 7, 2025
@softhack007 softhack007 merged commit 01d7f17 into mdev Nov 7, 2025
57 checks passed
softhack007 added a commit that referenced this pull request Nov 7, 2025
gifdecoder bugfixes, up to 30% speedup, and minor improvements
- several optimizations for the drawing a single pixel (hot path)
- Image mode now runs correctly on 2D/matrix layouts with mirroring.
- added an extra slider for Blur intensity; 
- improved per-segment scaling/upscaling and memory-aware decoder behavior on low-RAM devices.
- Robust filename validation, graceful failures for missing/unsupported files or low-memory, better decoding error handling and playback cleanup.
- Better user messages

open ends (will not fix in this PR):
* ".gif" filename compare should be case-insensitive - ".GIF", ".Gif", ".gIf" should be accepted.
   esp32 does not have "stricmp", so we might need a custom solution, maybe with a utility function that internally uses tolower(c) before comparing. Also strip leading / trailing blanks from the filename.
@softhack007 softhack007 deleted the Gif_MM_plus branch November 25, 2025 12:24
@softhack007 softhack007 added this to the 14.7.1 milestone Dec 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants