fix(gif): compose sub-rectangle frames so the animation actually plays - #195
Merged
Conversation
A gif component showed its first frame and then nothing — no error, no warning, scenario valid, render successful. Every frame after the first is usually a sub-rectangle holding only the pixels that changed. The decoder stored frame.buffer alongside the *canvas* dimensions, so the buffer was shorter than width * height * 4; raster_from_data returned None and the `if let Some(img)` arm was skipped. The first frame is full-canvas in most encoders, which is why it was the only one that ever appeared. Compose each frame onto a persistent canvas at (left, top), honouring alpha 0 as "leave what is underneath" and the disposal method (Background clears the frame's rect, Previous restores the canvas, Keep/Any leave it). Even at the right size, drawing an optimised frame alone would have shown a fragment on an empty background. The three silent exits now report once per source, the way #181 made the audio analysis report. AlphaType is Unpremul: the decoder emits straight RGBA.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #185.
What
A
gifcomponent showed its first frame and then nothing. No error, no warning — the scenario validated and the render succeeded.Cause
Every frame after the first is usually a sub-rectangle holding only the pixels that changed. The decoder stored
frame.bufferalongside the canvas dimensions, so the buffer was shorter thanwidth * height * 4.raster_from_datareturnedNone, theif let Some(img)arm was skipped, and nothing was drawn or said. The first frame is full-canvas in most encoders — which is why it was the only one that ever appeared.How
decode_composed_framescomposes each frame onto a persistent canvas at(left, top):Backgroundclears the frame's rect,Previousrestores the canvas captured before compositing,Keep/Anyleave it;Even at the right size, drawing an optimised frame on its own would have shown a fragment against an empty background — the composition is the fix, not just the sizing.
Two smaller corrections came with it: the three silent
returns now report once per source (the treatment #181 gave the audio analysis), andAlphaTypeisUnpremulbecause the decoder emits straight RGBA.After
Tests
gifhad none. Three added, on a fixture written with thegifencoder — frame 0 full canvas, frame 1 a 2x2 sub-rectangle withKeep:Noneand claims its warn-once slot — silence was the bug, so the test asserts the reporting, not just the returncargo test --workspacegreen,cargo fmt --checkandcargo clippy --all-targets -- -D warningsclean.