Skip to content

Commit

Permalink
[d3d9] Make proper use of X/YHotSpot for software cursors
Browse files Browse the repository at this point in the history
  • Loading branch information
WinterSnowfall authored and doitsujin committed Oct 11, 2024
1 parent 44b6820 commit 48c57c1
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 53 deletions.
8 changes: 4 additions & 4 deletions src/d3d9/d3d9_cursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ namespace dxvk {
POINT currentPos = { };
::GetCursorPos(&currentPos);

m_sCursor.X = static_cast<UINT>(currentPos.x);
m_sCursor.Y = static_cast<UINT>(currentPos.y);
m_sCursor.X = static_cast<int32_t>(currentPos.x) - m_sCursor.XHotSpot;
m_sCursor.Y = static_cast<int32_t>(currentPos.y) - m_sCursor.YHotSpot;
}


Expand Down Expand Up @@ -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);
Expand Down
6 changes: 4 additions & 2 deletions src/d3d9/d3d9_cursor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
57 changes: 13 additions & 44 deletions src/d3d9/d3d9_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(&params);

Expand Down Expand Up @@ -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<uint8_t> clippedBitmap(clippedInputHeight * clippedCopyPitch, 0);
size_t copyPitch = inputWidth * HardwareCursorFormatSize;
std::vector<uint8_t> 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;
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/d3d9/d3d9_swapchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down
2 changes: 1 addition & 1 deletion src/d3d9/d3d9_swapchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 48c57c1

Please sign in to comment.