From 49d58bc73af79bc893c861d84abf256b79ec9fbe Mon Sep 17 00:00:00 2001 From: Dimitriy Ryazantcev 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 | 140 ++++++++++++++++----------- 1 file changed, 84 insertions(+), 56 deletions(-) diff --git a/src/video/windows/SDL_windowsmouse.c b/src/video/windows/SDL_windowsmouse.c index 44491d37ab..521b8c6792 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_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; + SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888); - maskbitslen = ((surface->w + (pad - (surface->w % pad))) / 8) * surface->h; - maskbits = SDL_small_alloc(Uint8, maskbitslen, &isstack); - if (!maskbits) { + 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; +} + +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); }