Skip to content

fix: canvas corruption in dispose-to-background for non-alpha frames#178

Merged
fintelia merged 1 commit into
image-rs:mainfrom
lilith:fix/dispose-canvas-stride-bug
Mar 25, 2026
Merged

fix: canvas corruption in dispose-to-background for non-alpha frames#178
fintelia merged 1 commit into
image-rs:mainfrom
lilith:fix/dispose-canvas-stride-bug

Conversation

@lilith

@lilith lilith commented Mar 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • composite_frame clear code branched on frame_has_alpha to pick byte stride, but the canvas is always RGBA (4 bytes/pixel)
  • The !frame_has_alpha paths used 3-byte stride (chunks_exact_mut(3) for full-size, * 3 indexing for sub-frames), misaligning pixel writes
  • Triggers when an animation has dispose=Background and the next frame lacks alpha (e.g. lossy VP8 without ALPH chunk)

Test plan

  • Added dispose_clear_fullsize_rgb_frame unit test
  • Added dispose_clear_subframe_rgb_frame unit test (confirms failure on old code: pixel (4,3) not cleared: [0, 0, 170, 170])

The clear code in `composite_frame` branched on `frame_has_alpha` to
decide byte stride, but the canvas is always RGBA (4 bytes/pixel).
The `!frame_has_alpha` paths used 3-byte stride — `chunks_exact_mut(3)`
for full-size and `* 3` indexing for sub-frames — misaligning pixel
writes and corrupting the canvas.

This triggers when an animation has `dispose=Background` and the next
frame lacks alpha (e.g. lossy VP8 without ALPH chunk). The previous
frame's rectangle gets cleared with wrong byte offsets.

Fix: always clear with 4-byte pixels. `frame_has_alpha` describes the
incoming frame data format, not the canvas format.

Adds unit tests for both full-size and sub-frame disposal with RGB
frames. The sub-frame test fails against the old code with:
  pixel (4,3) not cleared: [0, 0, 170, 170]
@fintelia
fintelia merged commit 28bed22 into image-rs:main Mar 25, 2026
8 checks passed
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