From 48c57c11e9d09a314b3057e2dea8af7073cf2624 Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Fri, 11 Oct 2024 11:51:27 +0300 Subject: [PATCH] [d3d9] Make proper use of X/YHotSpot for software cursors --- src/d3d9/d3d9_cursor.cpp | 8 +++--- src/d3d9/d3d9_cursor.h | 6 ++-- src/d3d9/d3d9_device.cpp | 57 +++++++++---------------------------- src/d3d9/d3d9_swapchain.cpp | 4 +-- src/d3d9/d3d9_swapchain.h | 2 +- 5 files changed, 24 insertions(+), 53 deletions(-) diff --git a/src/d3d9/d3d9_cursor.cpp b/src/d3d9/d3d9_cursor.cpp index ac79ecfa879..cff32e58d1a 100644 --- a/src/d3d9/d3d9_cursor.cpp +++ b/src/d3d9/d3d9_cursor.cpp @@ -41,8 +41,8 @@ namespace dxvk { POINT currentPos = { }; ::GetCursorPos(¤tPos); - m_sCursor.X = static_cast(currentPos.x); - m_sCursor.Y = static_cast(currentPos.y); + m_sCursor.X = static_cast(currentPos.x) - m_sCursor.XHotSpot; + m_sCursor.Y = static_cast(currentPos.y) - m_sCursor.YHotSpot; } @@ -97,8 +97,8 @@ namespace dxvk { m_sCursor.Width = Width; m_sCursor.Height = Height; - m_sCursor.X = XHotSpot; - m_sCursor.Y = YHotSpot; + m_sCursor.XHotSpot = XHotSpot; + m_sCursor.YHotSpot = YHotSpot; m_sCursor.ResetCursor = false; ShowCursor(m_visible); diff --git a/src/d3d9/d3d9_cursor.h b/src/d3d9/d3d9_cursor.h index af96884bea9..c45338d4394 100644 --- a/src/d3d9/d3d9_cursor.h +++ b/src/d3d9/d3d9_cursor.h @@ -10,8 +10,10 @@ namespace dxvk { struct D3D9_SOFTWARE_CURSOR { UINT Width = 0; UINT Height = 0; - UINT X = 0; - UINT Y = 0; + UINT XHotSpot = 0; + UINT YHotSpot = 0; + int32_t X = 0; + int32_t Y = 0; bool DrawCursor = false; bool ResetCursor = false; }; diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 459d40294b6..67b4d9bdfbd 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -350,6 +350,11 @@ namespace dxvk { || (inputHeight && (inputHeight & (inputHeight - 1)))) return D3DERR_INVALIDCALL; + // It makes no sense to have a hotspot outside of the bitmap. + if (XHotSpot > std::max(inputWidth - 1, 0u) + || YHotSpot > std::max(inputHeight - 1, 0u)) + return D3DERR_INVALIDCALL; + D3DPRESENT_PARAMETERS params; m_implicitSwapchain->GetPresentParameters(¶ms); @@ -387,55 +392,17 @@ namespace dxvk { // Set this as our cursor. return m_cursor.SetHardwareCursor(XHotSpot, YHotSpot, bitmap); } else { - // The cursor bitmap passed by the application has the potential - // to not be clipped to the correct dimensions, so we need to - // discard any transparent edges and keep only a tight rectangle - // bounded by the cursor's visible edge pixels - uint32_t leftEdge = inputWidth * HardwareCursorFormatSize; - uint32_t topEdge = inputHeight; - uint32_t rightEdge = 0; - uint32_t bottomEdge = 0; - - uint32_t rowPitch = inputWidth * HardwareCursorFormatSize; - - for (uint32_t h = 0; h < inputHeight; h++) { - uint32_t rowOffset = h * rowPitch; - for (uint32_t w = 0; w < rowPitch; w += HardwareCursorFormatSize) { - // Examine only pixels with non-zero alpha - if (data[rowOffset + w + 3] != 0) { - if (leftEdge > w) leftEdge = w; - if (topEdge > h) topEdge = h; - if (rightEdge < w) rightEdge = w; - if (bottomEdge < h) bottomEdge = h; - } - } - } - leftEdge /= HardwareCursorFormatSize; - rightEdge /= HardwareCursorFormatSize; - - if (leftEdge > rightEdge || topEdge > bottomEdge) { - UnlockImage(cursorTex, 0, 0); - - return D3DERR_INVALIDCALL; - } - - // Calculate clipped bitmap dimensions - uint32_t clippedInputWidth = rightEdge + 1 - leftEdge + 1; - uint32_t clippedInputHeight = bottomEdge + 1 - topEdge + 1; - // Windows works with a stride of 128, lets respect that. - uint32_t clippedCopyPitch = clippedInputWidth * HardwareCursorFormatSize; - - std::vector clippedBitmap(clippedInputHeight * clippedCopyPitch, 0); + size_t copyPitch = inputWidth * HardwareCursorFormatSize; + std::vector bitmap(inputHeight * copyPitch, 0); - for (uint32_t h = 0; h < clippedInputHeight; h++) - std::memcpy(&clippedBitmap[h * clippedCopyPitch], - &data[(h + topEdge) * lockedBox.RowPitch + leftEdge * HardwareCursorFormatSize], clippedCopyPitch); + for (uint32_t h = 0; h < inputHeight; h++) + std::memcpy(&bitmap[h * copyPitch], &data[h * lockedBox.RowPitch], copyPitch); UnlockImage(cursorTex, 0, 0); - m_implicitSwapchain->SetCursorTexture(clippedInputWidth, clippedInputHeight, &clippedBitmap[0]); + m_implicitSwapchain->SetCursorTexture(inputWidth, inputHeight, &bitmap[0]); - return m_cursor.SetSoftwareCursor(clippedInputWidth, clippedInputHeight, XHotSpot, YHotSpot); + return m_cursor.SetSoftwareCursor(inputWidth, inputHeight, XHotSpot, YHotSpot); } return D3D_OK; @@ -3929,6 +3896,8 @@ namespace dxvk { if (unlikely(pSoftwareCursor->ResetCursor)) { pSoftwareCursor->Width = 0; pSoftwareCursor->Height = 0; + pSoftwareCursor->XHotSpot = 0; + pSoftwareCursor->YHotSpot = 0; pSoftwareCursor->X = 0; pSoftwareCursor->Y = 0; pSoftwareCursor->ResetCursor = false; diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index e9beb030b4a..749fb6ba5e0 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -758,8 +758,8 @@ namespace dxvk { } - void D3D9SwapChainEx::SetCursorPosition(UINT X, UINT Y, UINT Width, UINT Height) { - VkOffset2D cursorPosition = { int32_t(X), int32_t(Y) }; + void D3D9SwapChainEx::SetCursorPosition(int32_t X, int32_t Y, UINT Width, UINT Height) { + VkOffset2D cursorPosition = { X, Y }; VkExtent2D cursorSize = { uint32_t(Width), uint32_t(Height) }; VkRect2D cursorRect = { cursorPosition, cursorSize }; diff --git a/src/d3d9/d3d9_swapchain.h b/src/d3d9/d3d9_swapchain.h index b28f3232569..e26a97a38f7 100644 --- a/src/d3d9/d3d9_swapchain.h +++ b/src/d3d9/d3d9_swapchain.h @@ -120,7 +120,7 @@ namespace dxvk { void SetCursorTexture(UINT Width, UINT Height, uint8_t* pCursorBitmap); - void SetCursorPosition(UINT X, UINT Y, UINT Width, UINT Height); + void SetCursorPosition(int32_t X, int32_t Y, UINT Width, UINT Height); HRESULT SetDialogBoxMode(bool bEnableDialogs);