SDL_image: Set the colorkey or expand the palette for indexed PNG images

From c6f46fe8c5d865bead16cfff7cc7f165303147b9 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 28 Dec 2023 09:35:52 -0800
Subject: [PATCH] Set the colorkey or expand the palette for indexed PNG images

SDL doesn't support alpha blending using the palette alpha

Fixes https://github.com/libsdl-org/SDL_image/issues/409
---
 src/IMG_stb.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/src/IMG_stb.c b/src/IMG_stb.c
index 464b5e36..464efce1 100644
--- a/src/IMG_stb.c
+++ b/src/IMG_stb.c
@@ -113,6 +113,9 @@ SDL_Surface *IMG_LoadSTB_RW(SDL_RWops *src)
     rw_callbacks.eof = IMG_LoadSTB_RW_eof;
     w = h = format = 0; /* silence warning */
     if (use_palette) {
+        /* Unused palette entries will be opaque white */
+        SDL_memset(palette_colors, 0xff, sizeof(palette_colors));
+
         pixels = stbi_load_from_callbacks_with_palette(
             &rw_callbacks,
             src,
@@ -145,6 +148,8 @@ SDL_Surface *IMG_LoadSTB_RW(SDL_RWops *src)
             SDL_PIXELFORMAT_INDEX8
         );
         if (surface) {
+            SDL_bool has_colorkey = SDL_FALSE;
+            int colorkey_index = -1;
             SDL_bool has_alpha = SDL_FALSE;
             SDL_Palette *palette = surface->format->palette;
             if (palette) {
@@ -157,12 +162,26 @@ SDL_Surface *IMG_LoadSTB_RW(SDL_RWops *src)
                     palette->colors[i].b = *palette_bytes++;
                     palette->colors[i].a = *palette_bytes++;
                     if (palette->colors[i].a != SDL_ALPHA_OPAQUE) {
-                        has_alpha = SDL_TRUE;
+                        if (palette->colors[i].a == SDL_ALPHA_TRANSPARENT && !has_colorkey) {
+                            has_colorkey = SDL_TRUE;
+                            colorkey_index = i;
+                        } else {
+                            /* Partial opacity or multiple colorkeys */
+                            has_alpha = SDL_TRUE;
+                        }
                     }
                 }
             }
             if (has_alpha) {
+#if 1 /* SDL doesn't support blitting with the palette alpha, so expand the palette */
+                SDL_Surface *converted = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA32);
+                SDL_DestroySurface(surface);
+                surface = converted;
+#else
                 SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
+#endif
+            } else if (has_colorkey) {
+                SDL_SetSurfaceColorKey(surface, SDL_TRUE, colorkey_index);
             }
 
             /* FIXME: This sucks. It'd be better to allocate the surface first, then
@@ -170,7 +189,9 @@ SDL_Surface *IMG_LoadSTB_RW(SDL_RWops *src)
              * https://github.com/nothings/stb/issues/58
              * -flibit
              */
-            surface->flags &= ~SDL_PREALLOC;
+            if (surface) {
+                surface->flags &= ~SDL_PREALLOC;
+            }
         }
 
     } else if (format == STBI_grey || format == STBI_rgb || format == STBI_rgb_alpha) {