Browse Source

Merge pull request #65133 from bruvzg/win_cursor

[Windows] Simplify cursor handling and add support for fully transparent cursors.
Rémi Verschelde 2 years ago
parent
commit
5ff8e4ee67

+ 38 - 82
platform/windows/display_server_windows.cpp

@@ -1496,7 +1496,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]));
@@ -1509,55 +1509,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_
 
@@ -1610,8 +1561,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;
@@ -1620,39 +1589,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);
@@ -1665,17 +1623,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;

+ 0 - 2
platform/windows/display_server_windows.h

@@ -311,8 +311,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
 	};