Cleanup WIN_CreateCursor() code a bit

Generate bimap mask from the alpha channel.
main
Dimitriy Ryazantcev 2023-11-17 12:23:06 +02:00 committed by Sam Lantinga
parent 4722269fb6
commit 49d58bc73a
1 changed files with 84 additions and 56 deletions

View File

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