SDL_image: Added support for PNG icons and cursors

From 5c79674516669f4f70fac959647c15f7919d2f60 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sun, 19 Oct 2025 22:12:50 -0700
Subject: [PATCH] Added support for PNG icons and cursors

---
 src/IMG_bmp.c | 254 ++++++++++++++++++++++++++++----------------------
 1 file changed, 140 insertions(+), 114 deletions(-)

diff --git a/src/IMG_bmp.c b/src/IMG_bmp.c
index 36896faf..56e80cc7 100644
--- a/src/IMG_bmp.c
+++ b/src/IMG_bmp.c
@@ -39,6 +39,10 @@
 
 #ifdef LOAD_BMP
 
+#define RIFF_FOURCC(c0, c1, c2, c3)                 \
+    ((Uint32)(Uint8)(c0) | ((Uint32)(Uint8)(c1) << 8) | \
+     ((Uint32)(Uint8)(c2) << 16) | ((Uint32)(Uint8)(c3) << 24))
+
 #define ICON_TYPE_ICO   1
 #define ICON_TYPE_CUR   2
 
@@ -118,33 +122,18 @@ static SDL_Surface *LoadBMP_IO(SDL_IOStream *src, bool closeio)
     return SDL_LoadBMP_IO(src, closeio);
 }
 
-static SDL_Surface *LoadICOCUR_IO(SDL_IOStream * src, int type, bool closeio)
+static SDL_Surface *ReadBitmapSurface(SDL_IOStream *src)
 {
     bool was_error = true;
-    Sint64 fp_offset = 0;
     int bmpPitch;
-    int i,j, pad;
+    int i, j, pad;
     SDL_Surface *surface = NULL;
-    /*
-    Uint32 Rmask;
-    Uint32 Gmask;
-    Uint32 Bmask;
-    */
     Uint8 *bits;
     int ExpandBMP;
-    Uint8 maxCol = 0;
-    Uint32 icoOfs = 0;
-    int nHotX = 0;
-    int nHotY = 0;
     Uint32 palette[256];
 
-    /* The Win32 ICO file header (14 bytes) */
-    Uint16 bfReserved;
-    Uint16 bfType;
-    Uint16 bfCount;
-
     /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
-    Uint32 biSize;
+    /* Uint32 biSize; */
     Sint32 biWidth;
     Sint32 biHeight;
     /* Uint16 biPlanes; */
@@ -158,102 +147,17 @@ static SDL_Surface *LoadICOCUR_IO(SDL_IOStream * src, int type, bool closeio)
     */
     Uint32 biClrUsed;
 
-    /* Make sure we are passed a valid data source */
-    if (src == NULL) {
-        goto done;
-    }
-
-    /* Read in the ICO file header */
-    fp_offset = SDL_TellIO(src);
-
-    if (!SDL_ReadU16LE(src, &bfReserved) ||
-        !SDL_ReadU16LE(src, &bfType) ||
-        !SDL_ReadU16LE(src, &bfCount) ||
-        (bfReserved != 0) || (bfType != type) || (bfCount == 0)) {
-        SDL_SetError("File is not a Windows %s file", type == 1 ? "ICO" : "CUR");
-        goto done;
-    }
-
-    /* Read the Win32 Icon Directory */
-    for (i = 0; i < bfCount; i++) {
-        /* Icon Directory Entries */
-        Uint8 bWidth;       /* Uint8, but 0 = 256 ! */
-        Uint8 bHeight;      /* Uint8, but 0 = 256 ! */
-        Uint8 bColorCount;  /* Uint8, but 0 = 256 ! */
-        Uint8 bReserved;
-        Uint16 wPlanes;
-        Uint16 wBitCount;
-        Uint32 dwBytesInRes;
-        Uint32 dwImageOffset;
-        int nWidth, nHeight, nColorCount;
-
-        if (!SDL_ReadU8(src, &bWidth) ||
-            !SDL_ReadU8(src, &bHeight) ||
-            !SDL_ReadU8(src, &bColorCount) ||
-            !SDL_ReadU8(src, &bReserved) ||
-            !SDL_ReadU16LE(src, &wPlanes) ||
-            !SDL_ReadU16LE(src, &wBitCount) ||
-            !SDL_ReadU32LE(src, &dwBytesInRes) ||
-            !SDL_ReadU32LE(src, &dwImageOffset)) {
-            goto done;
-        }
-
-        if (bWidth) {
-            nWidth = bWidth;
-        } else {
-            nWidth = 256;
-        }
-        if (bHeight) {
-            nHeight = bHeight;
-        } else {
-            nHeight = 256;
-        }
-        if (bColorCount) {
-            nColorCount = bColorCount;
-        } else {
-            nColorCount = 256;
-        }
-
-        if (type == ICON_TYPE_CUR) {
-            nHotX = wPlanes;
-            nHotY = wBitCount;
-        }
-
-        //SDL_Log("%dx%d@%d - %08x\n", nWidth, nHeight, nColorCount, dwImageOffset);
-        (void)nWidth;
-        (void)nHeight;
-        if (nColorCount > maxCol) {
-            maxCol = nColorCount;
-            icoOfs = dwImageOffset;
-            //SDL_Log("marked\n");
-        }
-    }
-
-    /* Advance to the DIB Data */
-    if (SDL_SeekIO(src, fp_offset + icoOfs, SDL_IO_SEEK_SET) < 0) {
-        goto done;
-    }
-
-    /* Read the Win32 BITMAPINFOHEADER */
-    if (!SDL_ReadU32LE(src, &biSize)) {
-        goto done;
-    }
-    if (biSize == 40) {
-        if (!SDL_ReadS32LE(src, &biWidth) ||
-            !SDL_ReadS32LE(src, &biHeight) ||
-            !SDL_ReadU16LE(src, NULL /* biPlanes */) ||
-            !SDL_ReadU16LE(src, &biBitCount) ||
-            !SDL_ReadU32LE(src, &biCompression) ||
-            !SDL_ReadU32LE(src, NULL /* biSizeImage */) ||
-            !SDL_ReadU32LE(src, NULL /* biXPelsPerMeter */) ||
-            !SDL_ReadU32LE(src, NULL /* biYPelsPerMeter */) ||
-            !SDL_ReadU32LE(src, &biClrUsed) ||
-            !SDL_ReadU32LE(src, NULL /* biClrImportant */)) {
-            goto done;
-        }
-    } else {
-        SDL_SetError("Unsupported ICO bitmap format");
-        goto done;
+    if (!SDL_ReadS32LE(src, &biWidth) ||
+        !SDL_ReadS32LE(src, &biHeight) ||
+        !SDL_ReadU16LE(src, NULL /* biPlanes */) ||
+        !SDL_ReadU16LE(src, &biBitCount) ||
+        !SDL_ReadU32LE(src, &biCompression) ||
+        !SDL_ReadU32LE(src, NULL /* biSizeImage */) ||
+        !SDL_ReadU32LE(src, NULL /* biXPelsPerMeter */) ||
+        !SDL_ReadU32LE(src, NULL /* biYPelsPerMeter */) ||
+        !SDL_ReadU32LE(src, &biClrUsed) ||
+        !SDL_ReadU32LE(src, NULL /* biClrImportant */)) {
+        return NULL;
     }
 
     /* We don't support any BMP compression right now */
@@ -434,6 +338,128 @@ static SDL_Surface *LoadICOCUR_IO(SDL_IOStream * src, int type, bool closeio)
         }
     }
 
+    was_error = false;
+
+done:
+    if (was_error) {
+        SDL_DestroySurface(surface);
+        return NULL;
+    }
+    return surface;
+}
+
+static SDL_Surface *LoadICOCUR_IO(SDL_IOStream *src, int type, bool closeio)
+{
+    bool was_error = true;
+    Sint64 fp_offset = 0;
+    int i;
+    int maxCol = 0;
+    Uint32 icoOfs = 0;
+    int nHotX = 0;
+    int nHotY = 0;
+    Uint32 biSize;
+    SDL_Surface *surface = NULL;
+
+    /* The Win32 ICO file header (14 bytes) */
+    Uint16 bfReserved;
+    Uint16 bfType;
+    Uint16 bfCount;
+
+    /* Make sure we are passed a valid data source */
+    if (src == NULL) {
+        goto done;
+    }
+
+    /* Read in the ICO file header */
+    fp_offset = SDL_TellIO(src);
+
+    if (!SDL_ReadU16LE(src, &bfReserved) ||
+        !SDL_ReadU16LE(src, &bfType) ||
+        !SDL_ReadU16LE(src, &bfCount) ||
+        (bfReserved != 0) || (bfType != type) || (bfCount == 0)) {
+        SDL_SetError("File is not a Windows %s file", type == 1 ? "ICO" : "CUR");
+        goto done;
+    }
+
+    /* Read the Win32 Icon Directory */
+    //SDL_Log("Icon directory: %d cursors", bfCount);
+    for (i = 0; i < bfCount; ++i) {
+        /* Icon Directory Entries */
+        Uint8 bWidth;       /* Uint8, but 0 = 256 ! */
+        Uint8 bHeight;      /* Uint8, but 0 = 256 ! */
+        Uint8 bColorCount;  /* Uint8, but 0 = 256 ! */
+        Uint8 bReserved;
+        Uint16 wPlanes;
+        Uint16 wBitCount;
+        Uint32 dwBytesInRes;
+        Uint32 dwImageOffset;
+        int nWidth, nHeight, nColorCount;
+
+        if (!SDL_ReadU8(src, &bWidth) ||
+            !SDL_ReadU8(src, &bHeight) ||
+            !SDL_ReadU8(src, &bColorCount) ||
+            !SDL_ReadU8(src, &bReserved) ||
+            !SDL_ReadU16LE(src, &wPlanes) ||
+            !SDL_ReadU16LE(src, &wBitCount) ||
+            !SDL_ReadU32LE(src, &dwBytesInRes) ||
+            !SDL_ReadU32LE(src, &dwImageOffset)) {
+            goto done;
+        }
+
+        if (bWidth) {
+            nWidth = bWidth;
+        } else {
+            nWidth = 256;
+        }
+        if (bHeight) {
+            nHeight = bHeight;
+        } else {
+            nHeight = 256;
+        }
+        if (bColorCount) {
+            nColorCount = bColorCount;
+        } else {
+            nColorCount = 256;
+        }
+
+        if (type == ICON_TYPE_CUR) {
+            nHotX = wPlanes;
+            nHotY = wBitCount;
+        }
+
+        //SDL_Log("%dx%d@%d - %d", nWidth, nHeight, nColorCount, (int)dwImageOffset);
+        (void)nWidth;
+        (void)nHeight;
+        if (nColorCount > maxCol) {
+            //SDL_Log("%d > %d, marked", nColorCount, maxCol);
+            maxCol = nColorCount;
+            icoOfs = dwImageOffset;
+        }
+    }
+
+    /* Advance to the DIB Data */
+    if (SDL_SeekIO(src, fp_offset + icoOfs, SDL_IO_SEEK_SET) < 0) {
+        goto done;
+    }
+
+    /* Read the Win32 BITMAPINFOHEADER */
+    if (!SDL_ReadU32LE(src, &biSize)) {
+        goto done;
+    }
+    if (biSize == 40) {
+        surface = ReadBitmapSurface(src);
+    } else if (biSize == RIFF_FOURCC(0x89, 'P', 'N', 'G')) {
+        if (SDL_SeekIO(src, -4, SDL_IO_SEEK_CUR) < 0) {
+            goto done;
+        }
+        surface = IMG_LoadPNG_IO(src);
+    } else {
+        SDL_SetError("Unsupported ICO bitmap format");
+    }
+    if (!surface) {
+        goto done;
+    }
+
     if (type == ICON_TYPE_CUR) {
         SDL_PropertiesID props = SDL_GetSurfaceProperties(surface);
         SDL_SetNumberProperty(props, SDL_PROP_SURFACE_HOTSPOT_X_NUMBER, nHotX);