SDL_image: Animated WEBP fixes (#709) (1a977)

From 1a977a8ef59bf44acbb7b2c0fec5613e2adf36ed Mon Sep 17 00:00:00 2001
From: inigomonyota <[EMAIL REDACTED]>
Date: Fri, 3 Apr 2026 10:56:59 -0400
Subject: [PATCH] Animated WEBP fixes (#709)

* Implemented proper state tracking for frame disposal using prev_dispose and prev_rect. Previously, the decoder was incorrectly clearing the current frame's area based on its own disposal flag, rather than clearing the previous frame's area as required by the WebP specification.

Introduced defensive transparency: if the bitstream features indicate an alpha channel, the canvas is cleared to transparent black (0,0,0,0) regardless of the bgcolor metadata. This prevents "white box" flickering caused by incorrect background metadata in assets.

Optimized WEBP_MUX_NO_BLEND handling by using SDL_BLENDMODE_NONE. This allows the blitter to overwrite destination pixels directly, preserving incremental "delta" updates and eliminating redundant SDL_FillRect calls that caused parts of the image to blank out.

Fixed "ghosting" artifacts where elements from previous frames were not correctly erased when they did not overlap with the current frame's bounding box.

(cherry picked from commit 1ecbc406b7c77a27132d1c152f2ad5d28698b64f)
---
 src/IMG_webp.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/src/IMG_webp.c b/src/IMG_webp.c
index 6c49fc110..932061ab5 100644
--- a/src/IMG_webp.c
+++ b/src/IMG_webp.c
@@ -281,6 +281,11 @@ IMG_Animation *IMG_LoadWEBPAnimation_RW(SDL_RWops *src)
     uint32_t bgcolor;
     SDL_Surface *canvas = NULL;
 
+    // State machine variables for correct disposal handling
+    int prev_dispose = WEBP_MUX_DISPOSE_NONE;
+    SDL_Rect prev_rect;
+    uint32_t clear_color;
+
     if (!src) {
         /* The error message has been set in SDL_RWFromFile */
         return NULL;
@@ -356,11 +361,14 @@ IMG_Animation *IMG_LoadWEBPAnimation_RW(SDL_RWops *src)
 
     /* Initialize canvas - use bgcolor for non-alpha format, transparency for alpha */
     if (features.has_alpha) {
-        SDL_FillRect(canvas, NULL, SDL_MapRGBA(canvas->format, 0, 0, 0, 0));
+        clear_color = SDL_MapRGBA(canvas->format, 0, 0, 0, 0);
     } else {
-        SDL_FillRect(canvas, NULL, bgcolor);
+        clear_color = bgcolor;
     }
 
+    SDL_FillRect(canvas, NULL, clear_color);
+    SDL_zero(prev_rect);
+
     SDL_zero(iter);
     if (lib.WebPDemuxGetFrame(demuxer, 1, &iter)) {
         do {
@@ -377,10 +385,9 @@ IMG_Animation *IMG_LoadWEBPAnimation_RW(SDL_RWops *src)
             dst.w = iter.width;
             dst.h = iter.height;
 
-            /* 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);
+            /* Handle disposal of the PREVIOUS frame before drawing the current one */
+            if (prev_dispose == WEBP_MUX_DISPOSE_BACKGROUND) {
+                SDL_FillRect(canvas, &prev_rect, clear_color);
             }
 
             curr = SDL_CreateRGBSurfaceWithFormat(0, iter.width, iter.height, 0, SDL_PIXELFORMAT_RGBA32);
@@ -398,7 +405,7 @@ IMG_Animation *IMG_LoadWEBPAnimation_RW(SDL_RWops *src)
                 goto error;
             }
 
-            /* Set blend mode based on the frame's blend method */
+            /* Set blend mode. WEBP_MUX_NO_BLEND overwrites pixels, avoiding the need to manual clear */
             if (iter.blend_method == WEBP_MUX_BLEND) {
                 SDL_SetSurfaceBlendMode(curr, SDL_BLENDMODE_BLEND);
             } else {
@@ -412,6 +419,10 @@ IMG_Animation *IMG_LoadWEBPAnimation_RW(SDL_RWops *src)
             anim->frames[frame_idx] = SDL_DuplicateSurface(canvas);
             anim->delays[frame_idx] = iter.duration;
 
+            /* Update state for the next frame's disposal logic */
+            prev_dispose = iter.dispose_method;
+            prev_rect = dst;
+
         } while (lib.WebPDemuxNextFrame(&iter));
 
         lib.WebPDemuxReleaseIterator(&iter);