SDL: Cleanup WIN_CreateCursor() code a bit (49d58)

From 49d58bc73af79bc893c861d84abf256b79ec9fbe Mon Sep 17 00:00:00 2001
From: Dimitriy Ryazantcev <[EMAIL REDACTED]>
Date: Fri, 17 Nov 2023 12:23:06 +0200
Subject: [PATCH] Cleanup WIN_CreateCursor() code a bit

Generate bimap mask from the alpha channel.
---
 src/video/windows/SDL_windowsmouse.c | 142 ++++++++++++++++-----------
 1 file changed, 85 insertions(+), 57 deletions(-)

diff --git a/src/video/windows/SDL_windowsmouse.c b/src/video/windows/SDL_windowsmouse.c
index 44491d37abcb..521b8c679221 100644
--- a/src/video/windows/SDL_windowsmouse.c
+++ b/src/video/windows/SDL_windowsmouse.c
@@ -84,75 +84,103 @@ static SDL_Cursor *WIN_CreateDefaultCursor()
     return cursor;
 }
 
-static SDL_Cursor *WIN_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
+static HBITMAP CreateColorBitmap(SDL_Surface *surface)
 {
-    /* msdn says cursor mask has to be padded out to word alignment. Not sure
-        if that means machine word or WORD, but this handles either case. */
-    const size_t pad = (sizeof(size_t) * 8); /* 32 or 64, or whatever. */
-    SDL_Cursor *cursor;
-    HICON hicon;
-    HICON hcursor;
-    HDC hdc;
-    BITMAPV4HEADER bmh;
-    LPVOID pixels;
-    LPVOID maskbits;
-    size_t maskbitslen;
-    SDL_bool isstack;
-    ICONINFO ii;
+    HBITMAP bitmap;
+    BITMAPINFO bi;
+    void *pixels;
+
+    SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
+
+    SDL_zero(bi);
+    bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    bi.bmiHeader.biWidth = surface->w;
+    bi.bmiHeader.biHeight = -surface->h; /* Invert height to make the top-down DIB. */
+    bi.bmiHeader.biPlanes = 1;
+    bi.bmiHeader.biBitCount = 32;
+    bi.bmiHeader.biCompression = BI_RGB;
+
+    bitmap = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, &pixels, NULL, 0);
+    if (!bitmap || !pixels) {
+        WIN_SetError("CreateDIBSection()");
+        return NULL;
+    }
+
+    SDL_memcpy(pixels, surface->pixels, surface->pitch * surface->h);
+
+    return bitmap;
+}
 
-    SDL_zero(bmh);
-    bmh.bV4Size = sizeof(bmh);
-    bmh.bV4Width = surface->w;
-    bmh.bV4Height = -surface->h; /* Invert the image */
-    bmh.bV4Planes = 1;
-    bmh.bV4BitCount = 32;
-    bmh.bV4V4Compression = BI_BITFIELDS;
-    bmh.bV4AlphaMask = 0xFF000000;
-    bmh.bV4RedMask = 0x00FF0000;
-    bmh.bV4GreenMask = 0x0000FF00;
-    bmh.bV4BlueMask = 0x000000FF;
-
-    maskbitslen = ((surface->w + (pad - (surface->w % pad))) / 8) * surface->h;
-    maskbits = SDL_small_alloc(Uint8, maskbitslen, &isstack);
-    if (!maskbits) {
+static HBITMAP CreateMaskBitmap(SDL_Surface *surface)
+{
+    HBITMAP bitmap;
+    void *pixels;
+    int x, y;
+    Uint8 *src, *dst;
+    const int pitch = (((surface->w + 15) & ~15) / 8);
+    static const unsigned char masks[] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 };
+
+    SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
+
+    pixels = SDL_stack_alloc(Uint8, pitch * surface->h);
+    if (!pixels) {
         SDL_OutOfMemory();
         return NULL;
     }
 
-    /* AND the cursor against full bits: no change. We already have alpha. */
-    SDL_memset(maskbits, 0xFF, maskbitslen);
+    /* Make the entire mask completely transparent. */
+    SDL_memset(pixels, 0xff, pitch * surface->h);
+
+    SDL_LockSurface(surface);
+
+    src = surface->pixels;
+    dst = pixels;
+    for (y = 0; y < surface->h; y++, src += surface->pitch, dst += pitch) {
+        for (x = 0; x < surface->w; x++) {
+            Uint8 alpha = src[x * 4 + 3];
+            if (alpha != 0) {
+                /* Reset bit of an opaque pixel. */
+                dst[x >> 3] &= ~masks[x & 7];
+            }
+        }
+    }
+
+    SDL_UnlockSurface(surface);
+
+    bitmap = CreateBitmap(surface->w, surface->h, 1, 1, pixels);
+    SDL_stack_free(pixels);
+    if (!bitmap) {
+        WIN_SetError("CreateBitmap()");
+        return NULL;
+    }
+
+    return bitmap;
+}
+
+static SDL_Cursor *WIN_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
+{
+    HCURSOR hcursor;
+    SDL_Cursor *cursor;
+    ICONINFO ii;
 
-    hdc = GetDC(NULL);
     SDL_zero(ii);
     ii.fIcon = FALSE;
     ii.xHotspot = (DWORD)hot_x;
     ii.yHotspot = (DWORD)hot_y;
-    ii.hbmColor = CreateDIBSection(hdc, (BITMAPINFO *)&bmh, DIB_RGB_COLORS, &pixels, NULL, 0);
-    ii.hbmMask = CreateBitmap(surface->w, surface->h, 1, 1, maskbits);
-    ReleaseDC(NULL, hdc);
-    SDL_small_free(maskbits, isstack);
+    ii.hbmColor = CreateColorBitmap(surface);
+    ii.hbmMask = CreateMaskBitmap(surface);
 
-    SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
-    SDL_assert(surface->pitch == surface->w * 4);
-    SDL_memcpy(pixels, surface->pixels, (size_t)surface->h * surface->pitch);
+    if (!ii.hbmColor || !ii.hbmMask) {
+        return NULL;
+    }
 
-    hicon = CreateIconIndirect(&ii);
+    hcursor = CreateIconIndirect(&ii);
 
     DeleteObject(ii.hbmColor);
     DeleteObject(ii.hbmMask);
 
-    if (!hicon) {
-        WIN_SetError("CreateIconIndirect()");
-        return NULL;
-    }
-
-    /* The cursor returned by CreateIconIndirect does not respect system cursor size
-        preference, use CopyImage to duplicate the cursor with desired sizes */
-    hcursor = CopyImage(hicon, IMAGE_CURSOR, surface->w, surface->h, 0);
-    DestroyIcon(hicon);
-
     if (!hcursor) {
-        WIN_SetError("CopyImage()");
+        WIN_SetError("CreateIconIndirect()");
         return NULL;
     }
 
@@ -160,7 +188,7 @@ static SDL_Cursor *WIN_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
     if (cursor) {
         cursor->driverdata = hcursor;
     } else {
-        DestroyIcon(hcursor);
+        DestroyCursor(hcursor);
         SDL_OutOfMemory();
     }
 
@@ -227,11 +255,11 @@ static SDL_Cursor *WIN_CreateSystemCursor(SDL_SystemCursor id)
 
     cursor = SDL_calloc(1, sizeof(*cursor));
     if (cursor) {
-        HICON hicon;
+        HCURSOR hcursor;
 
-        hicon = LoadCursor(NULL, name);
+        hcursor = LoadCursor(NULL, name);
 
-        cursor->driverdata = hicon;
+        cursor->driverdata = hcursor;
     } else {
         SDL_OutOfMemory();
     }
@@ -241,9 +269,9 @@ static SDL_Cursor *WIN_CreateSystemCursor(SDL_SystemCursor id)
 
 static void WIN_FreeCursor(SDL_Cursor *cursor)
 {
-    HICON hicon = (HICON)cursor->driverdata;
+    HCURSOR hcursor = (HCURSOR)cursor->driverdata;
 
-    DestroyIcon(hicon);
+    DestroyCursor(hcursor);
     SDL_free(cursor);
 }