#include "pch.h" #include "CursorDrawer.h" #include "App.h" #include "Utils.h" #include extern std::shared_ptr logger; constexpr const char* monochromeCursorPS = R"( Texture2D originTex : register(t0); Texture2D maskTex : register(t1); SamplerState sam : register(s0); float4 main(float4 pos : SV_POSITION, float2 coord : TEXCOORD) : SV_Target{ float2 masks = maskTex.Sample(sam, coord).xy; if (masks.x > 0.5) { float3 origin = originTex.Sample(sam, coord).rgb; if (masks.y > 0.5) { return float4(1 - origin, 1); } else { return float4(origin, 1); } } else { if (masks.y > 0.5) { return float4(1, 1, 1, 1); } else { return float4(0, 0, 0, 1); } } } )"; LRESULT CALLBACK CursorDrawer::LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode >= 0 && wParam == WM_MOUSEMOVE) { MSLLHOOKSTRUCT* info = (MSLLHOOKSTRUCT*)lParam; const RECT& srcClientRect = App::GetInstance().GetSrcClientRect(); const RECT& hostRect = App::GetInstance().GetHostWndRect(); CursorDrawer& that = App::GetInstance().GetRenderer().GetCursorDrawer(); if (that._isUnderCapture) { if (!PtInRect(&srcClientRect, info->pt)) { that._StopCapture(info->pt); // 通常鼠标钩子里不能使用此函数,但放在这里出乎意料的可以工作 // 使用窗口消息似乎会随机丢掉一些调用 SetCursorPos(info->pt.x, info->pt.y); return TRUE; } } else { if (PtInRect(&hostRect, info->pt)) { that._StartCapture(info->pt); SetCursorPos(info->pt.x, info->pt.y); return TRUE; } } } return CallNextHookEx(NULL, nCode, wParam, lParam); } bool CursorDrawer::Initialize(ComPtr renderTarget, const RECT& destRect) { App& app = App::GetInstance(); if (!app.IsNoCursor()) { Renderer& renderer = app.GetRenderer(); _d3dDC = renderer.GetD3DDC(); _d3dDevice = renderer.GetD3DDevice(); _zoomFactorX = _zoomFactorY = App::GetInstance().GetCursorZoomFactor(); if (_zoomFactorX <= 0) { D3D11_TEXTURE2D_DESC desc{}; app.GetFrameSource().GetOutput()->GetDesc(&desc); _zoomFactorX = float(destRect.right - destRect.left) / desc.Width; _zoomFactorY = float(destRect.bottom - destRect.top) / desc.Height; } if (!renderer.GetRenderTargetView(renderTarget.Get(), &_rtv)) { SPDLOG_LOGGER_ERROR(logger, "GetRenderTargetView 失败"); return false; } if (!renderer.GetShaderResourceView(renderTarget.Get(), &_renderTargetSrv)) { SPDLOG_LOGGER_ERROR(logger, "GetShaderResourceView 失败"); return false; } ComPtr blob; if (!renderer.CompileShader(false, monochromeCursorPS, "main", &blob, "MonochromeCursorPS")) { SPDLOG_LOGGER_ERROR(logger, "编译 MonochromeCursorPS 失败"); return false; } HRESULT hr = renderer.GetD3DDevice()->CreatePixelShader( blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, &_monoCursorPS); if (FAILED(hr)) { SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("创建 MonochromeCursorPS 失败", hr)); return false; } D3D11_BUFFER_DESC bd = {}; bd.Usage = D3D11_USAGE_DYNAMIC; bd.ByteWidth = sizeof(VertexPositionTexture) * 4; bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; hr = renderer.GetD3DDevice()->CreateBuffer(&bd, nullptr, &_vtxBuffer); if (FAILED(hr)) { SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("创建顶点缓冲区失败", hr)); return false; } if (!renderer.GetSampler(EffectSamplerFilterType::Linear, EffectSamplerAddressType::Clamp, &_linearSam) || !renderer.GetSampler(EffectSamplerFilterType::Point, EffectSamplerAddressType::Clamp, &_pointSam) ) { SPDLOG_LOGGER_ERROR(logger, "GetSampler 失败"); return false; } _monoCursorSize = { GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR) }; D3D11_TEXTURE2D_DESC desc{}; desc.Width = lroundf(_monoCursorSize.cx * _zoomFactorX); desc.Height = lroundf(_monoCursorSize.cy * _zoomFactorY); desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; desc.Usage = D3D11_USAGE_DEFAULT; hr = _d3dDevice->CreateTexture2D(&desc, nullptr, &_monoTmpTexture); if (FAILED(hr)) { SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("创建 Texture2D 失败", hr)); return false; } if (!renderer.GetRenderTargetView(_monoTmpTexture.Get(), &_monoTmpRtv)) { SPDLOG_LOGGER_ERROR(logger, "GetRenderTargetView 失败"); return false; } if (!renderer.GetShaderResourceView(_monoTmpTexture.Get(), &_monoTmpSrv)) { SPDLOG_LOGGER_ERROR(logger, "GetShaderResourceView 失败"); return false; } D3D11_TEXTURE2D_DESC rtDesc; renderTarget->GetDesc(&rtDesc); _renderTargetSize = { (long)rtDesc.Width, (long)rtDesc.Height }; _destRect = destRect; } const RECT& srcClient = app.GetSrcClientRect(); SIZE srcSize = { srcClient.right - srcClient.left, srcClient.bottom - srcClient.top }; _clientScaleX = float(destRect.right - destRect.left) / srcSize.cx; _clientScaleY = float(destRect.bottom - destRect.top) / srcSize.cy; // 此钩子会拖累光标移动流畅度! _hCursorHook = SetWindowsHookEx(WH_MOUSE_LL, LowLevelMouseProc, NULL, 0); /*if (!App::GetInstance().IsBreakpointMode()) { // 限制光标在窗口内 if (App::GetInstance().IsConfineCursorIn3DGames()) { // 为了在 3D 游戏中起作用,每隔一定时间执行一次 if (!app.RegisterTimer(50, std::bind(ClipCursor, &App::GetInstance().GetSrcClientRect()))) { SPDLOG_LOGGER_ERROR(logger, "注册定时器失败"); // 注册定时器失败则仅尝试一次 } } if (!ClipCursor(&App::GetInstance().GetSrcClientRect())) { SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("ClipCursor 失败")); } }*/ SPDLOG_LOGGER_INFO(logger, "CursorDrawer 初始化完成"); return true; } CursorDrawer::~CursorDrawer() { UnhookWindowsHookEx(_hCursorHook); if (_isUnderCapture) { POINT pt; GetCursorPos(&pt); _StopCapture(pt); SetCursorPos(pt.x, pt.y); } SPDLOG_LOGGER_INFO(logger, "CursorDrawer 已析构"); } bool GetHBmpBits32(HBITMAP hBmp, int& width, int& height, std::vector& pixels) { BITMAP bmp{}; if (!GetObject(hBmp, sizeof(bmp), &bmp)) { SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("GetObject 失败")); return false; } width = bmp.bmWidth; height = bmp.bmHeight; BITMAPINFO bi{}; bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth = width; bi.bmiHeader.biHeight = -height; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biCompression = BI_RGB; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biSizeImage = width * height * 4; pixels.resize(bi.bmiHeader.biSizeImage); HDC hdc = GetDC(NULL); Utils::ScopeExit se([hdc]() { ReleaseDC(NULL, hdc); }); if (GetDIBits(hdc, hBmp, 0, height, &pixels[0], &bi, DIB_RGB_COLORS) != height) { SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("GetDIBits 失败")); return false; } return true; } bool CursorDrawer::_ResolveCursor(HCURSOR hCursor, _CursorInfo& result) const { assert(hCursor != NULL); ICONINFO ii{}; if (!GetIconInfo(hCursor, &ii)) { SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("GetIconInfo 失败")); return false; } result.xHotSpot = ii.xHotspot; result.yHotSpot = ii.yHotspot; Utils::ScopeExit se([&ii]() { if (ii.hbmColor) { DeleteBitmap(ii.hbmColor); } DeleteBitmap(ii.hbmMask); }); ComPtr texture; if(ii.hbmColor == NULL) { // 单色光标 BITMAP bmp{}; if (!GetObject(ii.hbmMask, sizeof(bmp), &bmp)) { SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("GetObject 失败")); return false; } result.width = bmp.bmWidth; result.height = bmp.bmHeight; BITMAPINFO bi{}; bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth = result.width; bi.bmiHeader.biHeight = -(LONG)result.height; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biCompression = BI_RGB; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biSizeImage = result.width * result.height * 4; std::vector pixels(bi.bmiHeader.biSizeImage); HDC hdc = GetDC(NULL); if (GetDIBits(hdc, ii.hbmMask, 0, result.height, &pixels[0], &bi, DIB_RGB_COLORS) != result.height) { SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("GetDIBits 失败")); ReleaseDC(NULL, hdc); return false; } ReleaseDC(NULL, hdc); // 红色通道是 AND 掩码,绿色通道是 XOR 掩码 const int halfSize = bi.bmiHeader.biSizeImage / 8; BYTE* upPtr = &pixels[1]; BYTE* downPtr = &pixels[static_cast(halfSize) * 4]; for (int i = 0; i < halfSize; ++i) { *upPtr = *downPtr; // 判断光标是否存在反色部分 if (!result.hasInv && *(upPtr - 1) == 255 && *upPtr == 255) { result.hasInv = true; } upPtr += 4; downPtr += 4; } if (result.hasInv) { SPDLOG_LOGGER_INFO(logger, "此光标含反色部分"); result.height /= 2; if (result.width != _monoCursorSize.cx || result.height != _monoCursorSize.cy) { SPDLOG_LOGGER_ERROR(logger, "单色光标的尺寸不合法"); return false; } D3D11_TEXTURE2D_DESC desc{}; desc.Width = result.width; desc.Height = result.height; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; desc.Usage = D3D11_USAGE_DEFAULT; D3D11_SUBRESOURCE_DATA initData{}; initData.pSysMem = &pixels[0]; initData.SysMemPitch = result.width * 4; HRESULT hr = _d3dDevice->CreateTexture2D(&desc, &initData, &texture); if (FAILED(hr)) { SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("创建 Texture2D 失败", hr)); return false; } } } if(!result.hasInv) { // 光标无反色部分,使用 WIC 将光标转换为带 Alpha 通道的图像 ComPtr wicFactory = App::GetInstance().GetWICImageFactory(); if (!wicFactory) { SPDLOG_LOGGER_ERROR(logger, "获取 WICImageFactory 失败"); return false; } ComPtr wicBitmap; HRESULT hr = wicFactory->CreateBitmapFromHICON(hCursor, &wicBitmap); if (FAILED(hr)) { SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("CreateBitmapFromHICON 失败", hr)); return false; } hr = wicBitmap->GetSize(&result.width, &result.height); if (FAILED(hr)) { SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("GetSize 失败", hr)); return false; } std::vector pixels(result.width * result.height * 4); hr = wicBitmap->CopyPixels(nullptr, result.width * 4, (UINT)pixels.size(), pixels.data()); if (FAILED(hr)) { SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("CopyPixels 失败", hr)); return false; } D3D11_TEXTURE2D_DESC desc{}; desc.Width = result.width; desc.Height = result.height; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; desc.Usage = D3D11_USAGE_IMMUTABLE; D3D11_SUBRESOURCE_DATA initData{}; initData.pSysMem = &pixels[0]; initData.SysMemPitch = result.width * 4; hr = _d3dDevice->CreateTexture2D(&desc, &initData, &texture); if (FAILED(hr)) { SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("创建 Texture2D 失败", hr)); return false; } } HRESULT hr = _d3dDevice->CreateShaderResourceView(texture.Get(), nullptr, &result.texture); if (FAILED(hr)) { SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("创建 ShaderResourceView 失败", hr)); return false; } return true; } void CursorDrawer::_StartCapture(POINT& cursorPt) { // 在以下情况下进入捕获状态: // 1. 当前未捕获 // 2. 光标进入全屏区域 // // 进入捕获状态时: // 1. 调整光标速度,全局隐藏光标 // 2. 将光标移到源窗口的对应位置 // // 在有黑边的情况下自动将光标调整到画面内 // 全局隐藏光标 if (!MagShowSystemCursor(FALSE)) { SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("MagShowSystemCursor 失败")); } if (App::GetInstance().IsAdjustCursorSpeed()) { // 设置鼠标移动速度 if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &_cursorSpeed, 0)) { long newSpeed = std::clamp(lroundf(_cursorSpeed / (_clientScaleX + _clientScaleY) * 2), 1L, 20L); if (!SystemParametersInfo(SPI_SETMOUSESPEED, 0, (PVOID)(intptr_t)newSpeed, 0)) { SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("设置光标移速失败")); } } else { SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("获取光标移速失败")); } } // 移动光标位置 const RECT& srcClientRect = App::GetInstance().GetSrcClientRect(); const RECT& hostRect = App::GetInstance().GetHostWndRect(); // 跳过黑边 cursorPt.x = std::clamp(cursorPt.x, hostRect.left + _destRect.left, hostRect.left + _destRect.right - 1); cursorPt.y = std::clamp(cursorPt.y, hostRect.top + _destRect.top, hostRect.top + _destRect.bottom - 1); double posX = double(cursorPt.x - hostRect.left - _destRect.left) / (_destRect.right - _destRect.left); double posY = double(cursorPt.y - hostRect.top - _destRect.top) / (_destRect.bottom - _destRect.top); cursorPt.x = std::lround(posX * (srcClientRect.right - srcClientRect.left) + srcClientRect.left); cursorPt.y = std::lround(posY * (srcClientRect.bottom - srcClientRect.top) + srcClientRect.top); _isUnderCapture = true; } void CursorDrawer::_StopCapture(POINT& cursorPt) { // 在以下情况下离开捕获状态: // 1. 当前处于捕获状态 // 2. 光标离开源窗口客户区 // // 离开捕获状态时 // 1. 还原光标速度,全局显示光标 // 2. 将光标移到全屏窗口外的对应位置 // // 在有黑边的情况下自动将光标调整到全屏窗口外 const RECT& srcClientRect = App::GetInstance().GetSrcClientRect(); const RECT& hostRect = App::GetInstance().GetHostWndRect(); POINT oldPt = cursorPt; if (cursorPt.x >= srcClientRect.right) { cursorPt.x = hostRect.right + cursorPt.x - srcClientRect.right + 1; } else if (cursorPt.x < srcClientRect.left) { cursorPt.x = hostRect.left + cursorPt.x - srcClientRect.left; } else { double pos = double(cursorPt.x - srcClientRect.left) / (srcClientRect.right - srcClientRect.left); cursorPt.x = std::lround(pos * (_destRect.right - _destRect.left)) + _destRect.left + hostRect.left; } if (cursorPt.y >= srcClientRect.bottom) { cursorPt.y = hostRect.bottom + cursorPt.y - srcClientRect.bottom + 1; } else if (cursorPt.y < srcClientRect.top) { cursorPt.y = hostRect.top + cursorPt.y - srcClientRect.top; } else { double pos = double(cursorPt.y - srcClientRect.top) / (srcClientRect.bottom - srcClientRect.top); cursorPt.y = std::lround(pos * (_destRect.bottom - _destRect.top)) + _destRect.top + hostRect.top; } if (MonitorFromPoint(cursorPt, MONITOR_DEFAULTTONULL) == NULL) { // 边界之外无屏幕,则将光标限制在源窗口中 cursorPt.x = std::clamp(oldPt.x, srcClientRect.left, srcClientRect.right); cursorPt.y = std::clamp(oldPt.y, srcClientRect.top, srcClientRect.bottom); return; } if (!MagShowSystemCursor(TRUE)) { SPDLOG_LOGGER_ERROR(logger, "MagShowSystemCursor 失败"); } // WGC 捕获模式会随机使 MagShowSystemCursor(TRUE) 失效,重新加载光标可以解决这个问题 SystemParametersInfo(SPI_SETCURSORS, 0, 0, 0); if (App::GetInstance().IsAdjustCursorSpeed()) { SystemParametersInfo(SPI_SETMOUSESPEED, 0, (PVOID)(intptr_t)_cursorSpeed, 0); } _isUnderCapture = false; } void CursorDrawer::Draw() { if (App::GetInstance().IsNoCursor() || !_isUnderCapture) { // 不绘制光标 return; } CURSORINFO ci{}; ci.cbSize = sizeof(ci); if (!GetCursorInfo(&ci)) { SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("GetCursorInfo 失败")); return; } if (!ci.hCursor || ci.flags != CURSOR_SHOWING) { return; } _CursorInfo* info = nullptr; auto it = _cursorMap.find(ci.hCursor); if (it != _cursorMap.end()) { info = &it->second; } else { // 未在映射中找到,创建新映射 _CursorInfo t; if (_ResolveCursor(ci.hCursor, t)) { info = &_cursorMap[ci.hCursor]; *info = t; SPDLOG_LOGGER_INFO(logger, fmt::format("已解析光标:{}", (void*)ci.hCursor)); } else { SPDLOG_LOGGER_ERROR(logger, "解析光标失败"); return; } } SIZE cursorSize = { lroundf(info->width * _zoomFactorX), lroundf(info->height * _zoomFactorY) }; // 映射坐标 const RECT& srcClient = App::GetInstance().GetSrcClientRect(); POINT targetScreenPos = { lroundf((ci.ptScreenPos.x - srcClient.left) * _clientScaleX - info->xHotSpot * _zoomFactorX), lroundf((ci.ptScreenPos.y - srcClient.top) * _clientScaleY - info->yHotSpot * _zoomFactorY) }; RECT cursorRect{ targetScreenPos.x + _destRect.left, targetScreenPos.y + _destRect.top, targetScreenPos.x + cursorSize.cx + _destRect.left, targetScreenPos.y + cursorSize.cy + _destRect.top }; if (cursorRect.right <= _destRect.left || cursorRect.bottom <= _destRect.top || cursorRect.left >= _destRect.right || cursorRect.top >= _destRect.bottom ) { // 光标在窗口外 return; } float left = targetScreenPos.x / FLOAT(_destRect.right - _destRect.left) * 2 - 1.0f; float top = 1.0f - targetScreenPos.y / FLOAT(_destRect.bottom - _destRect.top) * 2; float right = left + cursorSize.cx / FLOAT(_destRect.right - _destRect.left) * 2; float bottom = top - cursorSize.cy / FLOAT(_destRect.bottom - _destRect.top) * 2; Renderer& renderer = App::GetInstance().GetRenderer(); renderer.SetSimpleVS(_vtxBuffer.Get()); _d3dDC->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); if (!info->hasInv) { _d3dDC->OMSetRenderTargets(1, &_rtv, nullptr); D3D11_VIEWPORT vp{ (FLOAT)_destRect.left, (FLOAT)_destRect.top, FLOAT(_destRect.right - _destRect.left), FLOAT(_destRect.bottom - _destRect.top), 0.0f, 1.0f }; _d3dDC->RSSetViewports(1, &vp); D3D11_MAPPED_SUBRESOURCE ms; HRESULT hr = _d3dDC->Map(_vtxBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms); if (FAILED(hr)) { SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("Map 失败", hr)); return; } VertexPositionTexture* data = (VertexPositionTexture*)ms.pData; data[0] = { XMFLOAT3(left, top, 0.5f), XMFLOAT2(0.0f, 0.0f) }; data[1] = { XMFLOAT3(right, top, 0.5f), XMFLOAT2(1.0f, 0.0f) }; data[2] = { XMFLOAT3(left, bottom, 0.5f), XMFLOAT2(0.0f, 1.0f) }; data[3] = { XMFLOAT3(right, bottom, 0.5f), XMFLOAT2(1.0f, 1.0f) }; _d3dDC->Unmap(_vtxBuffer.Get(), 0); if (!renderer.SetCopyPS( App::GetInstance().GetCursorInterpolationMode() == 0 ? _pointSam : _linearSam, info->texture.Get()) ) { SPDLOG_LOGGER_ERROR(logger, "SetCopyPS 失败"); return; } if (!renderer.SetAlphaBlend(true)) { SPDLOG_LOGGER_ERROR(logger, "SetAlphaBlend 失败"); return; } _d3dDC->Draw(4, 0); if (!renderer.SetAlphaBlend(false)) { SPDLOG_LOGGER_ERROR(logger, "SetAlphaBlend 失败"); return; } } else { // 绘制带有反色部分的光标,首先将光标覆盖的纹理复制到 _monoTmpTexture 中 // 不知为何 CopySubresourceRegion 会大幅增加 GPU 占用 _d3dDC->OMSetRenderTargets(1, &_monoTmpRtv, nullptr); D3D11_VIEWPORT vp{}; vp.Width = (FLOAT)cursorSize.cx; vp.Height = (FLOAT)cursorSize.cy; vp.MaxDepth = 1.0f; _d3dDC->RSSetViewports(1, &vp); D3D11_MAPPED_SUBRESOURCE ms; HRESULT hr = _d3dDC->Map(_vtxBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms); if (FAILED(hr)) { SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("Map 失败", hr)); return; } VertexPositionTexture* data = (VertexPositionTexture*)ms.pData; float leftPos = (float)cursorRect.left / _renderTargetSize.cx; float rightPos = (float)cursorRect.right / _renderTargetSize.cx; float topPos = (float)cursorRect.top / _renderTargetSize.cy; float bottomPos = (float)cursorRect.bottom / _renderTargetSize.cy; data[0] = { XMFLOAT3(-1, 1, 0.5f), XMFLOAT2(leftPos, topPos) }; data[1] = { XMFLOAT3(1, 1, 0.5f), XMFLOAT2(rightPos, topPos) }; data[2] = { XMFLOAT3(-1, -1, 0.5f), XMFLOAT2(leftPos, bottomPos) }; data[3] = { XMFLOAT3(1, -1, 0.5f), XMFLOAT2(rightPos, bottomPos) }; _d3dDC->Unmap(_vtxBuffer.Get(), 0); if (!renderer.SetCopyPS(_pointSam, _renderTargetSrv)) { SPDLOG_LOGGER_ERROR(logger, "SetCopyPS 失败"); return; } _d3dDC->Draw(4, 0); // 绘制光标 hr = _d3dDC->Map(_vtxBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms); if (FAILED(hr)) { SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("Map 失败", hr)); return; } data = (VertexPositionTexture*)ms.pData; data[0] = { XMFLOAT3(left, top, 0.5f), XMFLOAT2(0.0f, 0.0f) }; data[1] = { XMFLOAT3(right, top, 0.5f), XMFLOAT2(1.0f, 0.0f) }; data[2] = { XMFLOAT3(left, bottom, 0.5f), XMFLOAT2(0.0f, 1.0f) }; data[3] = { XMFLOAT3(right, bottom, 0.5f), XMFLOAT2(1.0f, 1.0f) }; _d3dDC->Unmap(_vtxBuffer.Get(), 0); _d3dDC->PSSetShader(_monoCursorPS.Get(), nullptr, 0); _d3dDC->PSSetConstantBuffers(0, 0, nullptr); _d3dDC->OMSetRenderTargets(0, nullptr, nullptr); ID3D11ShaderResourceView* srv[2] = { _monoTmpSrv, info->texture.Get() }; _d3dDC->PSSetShaderResources(0, 2, srv); _d3dDC->PSSetSamplers(0, 1, App::GetInstance().GetCursorInterpolationMode() == 0 ? &_pointSam : &_linearSam); _d3dDC->OMSetRenderTargets(1, &_rtv, nullptr); vp.TopLeftX = (FLOAT)_destRect.left; vp.TopLeftY = (FLOAT)_destRect.top; vp.Width = FLOAT(_destRect.right - _destRect.left); vp.Height = FLOAT(_destRect.bottom - _destRect.top); _d3dDC->RSSetViewports(1, &vp); _d3dDC->Draw(4, 0); } }