[Windows] Simplify cursor handling and add support for fully transparent cursors.

This commit is contained in:
bruvzg 2022-08-30 18:50:56 +03:00
parent c40855f818
commit 77a4567122
No known key found for this signature in database
GPG Key ID: 7960FCF39844EC38
2 changed files with 37 additions and 83 deletions

View File

@ -1470,7 +1470,7 @@ void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) {
IDC_HELP
};
if (cursors[p_shape] != nullptr) {
if (cursors_cache.has(p_shape)) {
SetCursor(cursors[p_shape]);
} else {
SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));
@ -1483,55 +1483,6 @@ DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const {
return cursor_shape;
}
void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) {
// Get the system display DC.
HDC hDC = GetDC(nullptr);
// Create helper DC.
HDC hMainDC = CreateCompatibleDC(hDC);
HDC hAndMaskDC = CreateCompatibleDC(hDC);
HDC hXorMaskDC = CreateCompatibleDC(hDC);
// Get the dimensions of the source bitmap.
BITMAP bm;
GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
// Create the mask bitmaps.
hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // Color.
hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // Color.
// Release the system display DC.
ReleaseDC(nullptr, hDC);
// Select the bitmaps to helper DC.
HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap);
HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap);
HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap);
// Assign the monochrome AND mask bitmap pixels so that the pixels of the source bitmap
// with 'clrTransparent' will be white pixels of the monochrome bitmap.
SetBkColor(hMainDC, clrTransparent);
BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY);
// Assign the color XOR mask bitmap pixels so that the pixels of the source bitmap
// with 'clrTransparent' will be black and rest the pixels same as corresponding
// pixels of the source bitmap.
SetBkColor(hXorMaskDC, RGB(0, 0, 0));
SetTextColor(hXorMaskDC, RGB(255, 255, 255));
BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hAndMaskDC, 0, 0, SRCCOPY);
BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCAND);
// Deselect bitmaps from the helper DC.
SelectObject(hMainDC, hOldMainBitmap);
SelectObject(hAndMaskDC, hOldAndMaskBitmap);
SelectObject(hXorMaskDC, hOldXorMaskBitmap);
// Delete the helper DC.
DeleteDC(hXorMaskDC);
DeleteDC(hAndMaskDC);
DeleteDC(hMainDC);
}
void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
_THREAD_SAFE_METHOD_
@ -1584,8 +1535,26 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor
UINT image_size = texture_size.width * texture_size.height;
// Create the BITMAP with alpha channel.
COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size);
COLORREF *buffer = nullptr;
BITMAPV5HEADER bi;
ZeroMemory(&bi, sizeof(bi));
bi.bV5Size = sizeof(bi);
bi.bV5Width = texture_size.width;
bi.bV5Height = -texture_size.height;
bi.bV5Planes = 1;
bi.bV5BitCount = 32;
bi.bV5Compression = BI_BITFIELDS;
bi.bV5RedMask = 0x00ff0000;
bi.bV5GreenMask = 0x0000ff00;
bi.bV5BlueMask = 0x000000ff;
bi.bV5AlphaMask = 0xff000000;
HDC dc = GetDC(nullptr);
HBITMAP bitmap = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO *>(&bi), DIB_RGB_COLORS, reinterpret_cast<void **>(&buffer), nullptr, 0);
HBITMAP mask = CreateBitmap(texture_size.width, texture_size.height, 1, 1, nullptr);
bool fully_transparent = true;
for (UINT index = 0; index < image_size; index++) {
int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
@ -1594,39 +1563,28 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor
column_index = MIN(column_index, atlas_rect.size.width - 1);
row_index = MIN(row_index, atlas_rect.size.height - 1);
}
const Color &c = image->get_pixel(column_index, row_index);
fully_transparent = fully_transparent && (c.a == 0.f);
*(buffer + index) = image->get_pixel(column_index, row_index).to_argb32();
}
// Using 4 channels, so 4 * 8 bits.
HBITMAP bitmap = CreateBitmap(texture_size.width, texture_size.height, 1, 4 * 8, buffer);
COLORREF clrTransparent = -1;
// Create the AND and XOR masks for the bitmap.
HBITMAP hAndMask = nullptr;
HBITMAP hXorMask = nullptr;
GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask);
if (nullptr == hAndMask || nullptr == hXorMask) {
memfree(buffer);
DeleteObject(bitmap);
return;
*(buffer + index) = c.to_argb32();
}
// Finally, create the icon.
ICONINFO iconinfo;
iconinfo.fIcon = FALSE;
iconinfo.xHotspot = p_hotspot.x;
iconinfo.yHotspot = p_hotspot.y;
iconinfo.hbmMask = hAndMask;
iconinfo.hbmColor = hXorMask;
if (cursors[p_shape]) {
DestroyIcon(cursors[p_shape]);
}
cursors[p_shape] = CreateIconIndirect(&iconinfo);
if (fully_transparent) {
cursors[p_shape] = nullptr;
} else {
ICONINFO iconinfo;
iconinfo.fIcon = FALSE;
iconinfo.xHotspot = p_hotspot.x;
iconinfo.yHotspot = p_hotspot.y;
iconinfo.hbmMask = mask;
iconinfo.hbmColor = bitmap;
cursors[p_shape] = CreateIconIndirect(&iconinfo);
}
Vector<Variant> params;
params.push_back(p_cursor);
@ -1639,17 +1597,15 @@ void DisplayServerWindows::cursor_set_custom_image(const Ref<Resource> &p_cursor
}
}
DeleteObject(hAndMask);
DeleteObject(hXorMask);
memfree(buffer);
DeleteObject(mask);
DeleteObject(bitmap);
ReleaseDC(nullptr, dc);
} else {
// Reset to default system cursor.
if (cursors[p_shape]) {
DestroyIcon(cursors[p_shape]);
cursors[p_shape] = nullptr;
}
cursors[p_shape] = nullptr;
CursorShape c = cursor_shape;
cursor_shape = CURSOR_MAX;

View File

@ -295,8 +295,6 @@ class DisplayServerWindows : public DisplayServer {
String tablet_driver;
Vector<String> tablet_drivers;
void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap);
enum {
KEY_EVENT_BUFFER_SIZE = 512
};