Skip to content

IMG_webp: Incorrect frame composition in animated WebP loading #522

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 16 commits into
base: SDL2
Choose a base branch
from

Conversation

inigomonyota
Copy link

The current animated WebP loader in SDL_image has incorrect frame composition behavior, leading to visual artifacts in animated WebP files. This is particularly noticeable in WebP animations that use transparency or have overlapping frames with different disposal methods.

some test images here:

https://github.com/inigomonyota/webptestimages

The WebP animation loader was not correctly handling frame composition
and disposal. Fixed by properly implementing:

1. Frame disposal methods (WEBP_MUX_DISPOSE_BACKGROUND)
2. Initial canvas state based on alpha channel presence
3. Frame blending using SDL_BLENDMODE_NONE for accurate composition
4. Region-specific disposal and updates

The loader now correctly:
- Uses background color for non-alpha WebPs
- Uses transparency for WebPs with alpha channel
- Handles frame disposal before applying new frames
- Maintains proper frame boundaries during composition

This ensures animated WebPs render correctly regardless of their
disposal methods and alpha channel configuration.
@slouken
Copy link
Collaborator

slouken commented Feb 14, 2025

Can you please revert the formatting changes?

src/IMG_webp.c Outdated
if (features.has_alpha) {
SDL_FillRect(canvas, NULL, SDL_MapRGBA(canvas->format, 0, 0, 0, 0));
SDL_FillRect(prevCanvas, NULL, SDL_MapRGBA(prevCanvas->format, 0, 0, 0, 0));
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
}
} else {

Copy link
Author

Choose a reason for hiding this comment

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

got it

src/IMG_webp.c Outdated
if (canvas) {
SDL_FreeSurface(canvas);
}
if (anim) {
IMG_FreeAnimation(anim);
if (anim->frames) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What did IMG_FreeAnimation() do that we don't want done here?

Copy link
Author

Choose a reason for hiding this comment

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

I returned the error goto to how it was originally, hopefully fixed up formatting too

src/IMG_webp.c Outdated
dst.y = iter.y_offset;
dst.w = iter.width;
dst.h = iter.height;
SDL_SetSurfaceBlendMode(curr, SDL_BLENDMODE_NONE);
Copy link
Collaborator

Choose a reason for hiding this comment

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

How do you handle WEBP_MUX_BLEND?

Copy link
Author

Choose a reason for hiding this comment

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

oh right, what do you think of this?

```
    // Handle blending mode for current frame
        if (iter.blend_method == WEBP_MUX_BLEND) {
            SDL_SetSurfaceBlendMode(curr, SDL_BLENDMODE_BLEND);
        } else {
            // For NO_BLEND, we first clear the region then copy without blending
            if (features.has_alpha) {
                SDL_FillRect(canvas, &dst, SDL_MapRGBA(canvas->format, 0, 0, 0, 0));
            } else {
                SDL_FillRect(canvas, &dst, bgcolor);
            }
            SDL_SetSurfaceBlendMode(curr, SDL_BLENDMODE_NONE);
        }

would be just before blit

Copy link
Collaborator

Choose a reason for hiding this comment

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

Clearing the region isn't necessary for BLENDMODE_NONE, the entire rectangle is copied from the source.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We should just be able to use the original code:

            if (iter.blend_method == WEBP_MUX_BLEND) {
                SDL_SetSurfaceBlendMode(curr, SDL_BLENDMODE_BLEND);
            } else {
                SDL_SetSurfaceBlendMode(curr, SDL_BLENDMODE_NONE);
            }

src/IMG_webp.c Outdated

// Handle disposal of THIS frame's region before drawing it
if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
SDL_FillRect(canvas, &dst, SDL_MapRGBA(canvas->format, 0, 0, 0, 0));
Copy link
Collaborator

Choose a reason for hiding this comment

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

disposing with the background should use the background color, not black.

src/IMG_webp.c Outdated
@@ -347,6 +350,15 @@ IMG_Animation *IMG_LoadWEBPAnimation_RW(SDL_RWops *src)
(bgcolor >> 24) & 0xFF);
#endif

// Initialize both canvases - use bgcolor for non-alpha format, transparency for alpha
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are you sure we should use transparency for alpha? is bgcolor a transparent pixel in that case?

Copy link
Author

Choose a reason for hiding this comment

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

I see white in the non-updated areas if not starting with alpha.

Investigation reveals many viewers just ignore background color entirely.

https://developers.google.com/speed/webp/docs/riff_container#animation

Kind of vague.

Some discussion on the matter:

SixLabors/ImageSharp#2547

   - Properly handles WEBP_MUX_DISPOSE_BACKGROUND with alpha channel status
   - Correctly applies bgcolor for non-alpha WebPs and transparency for alpha WebPs
   - Sets appropriate blend modes (BLEND/NONE) based on frame's blend_method
   - Removes unused prevCanvas and associated operations
   - Eliminates redundant frame region tracking
// Handle disposal and prepare region for new frame
if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ||
iter.blend_method == WEBP_MUX_NO_BLEND) {
SDL_FillRect(canvas, &dst, bgcolor);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
SDL_FillRect(canvas, &dst, bgcolor);
SDL_FillRect(canvas, &dst, bgcolor);

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.

3 participants