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);
}