mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
641 lines
20 KiB
C++
641 lines
20 KiB
C++
#include "pch.h"
|
|
#include "CursorDrawer.h"
|
|
#include "App.h"
|
|
#include "Utils.h"
|
|
#include <VertexTypes.h>
|
|
|
|
extern std::shared_ptr<spdlog::logger> 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);
|
|
}
|
|
}
|
|
}
|
|
)";
|
|
|
|
bool CursorDrawer::Initialize(ComPtr<ID3D11Texture2D> 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<ID3DBlob> 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;
|
|
|
|
/*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() {
|
|
if (_isUnderCapture) {
|
|
POINT pt;
|
|
GetCursorPos(&pt);
|
|
_StopCapture(pt);
|
|
}
|
|
|
|
SPDLOG_LOGGER_INFO(logger, "CursorDrawer 已析构");
|
|
}
|
|
|
|
void CursorDrawer::Update() {
|
|
const RECT& srcClientRect = App::GetInstance().GetSrcClientRect();
|
|
const RECT& hostRect = App::GetInstance().GetHostWndRect();
|
|
|
|
CURSORINFO ci{ sizeof(CURSORINFO) };
|
|
GetCursorInfo(&ci);
|
|
|
|
if (_isUnderCapture) {
|
|
if (!PtInRect(&srcClientRect, ci.ptScreenPos)) {
|
|
_StopCapture(ci.ptScreenPos);
|
|
}
|
|
} else {
|
|
if (PtInRect(&hostRect, ci.ptScreenPos)) {
|
|
_StartCapture(ci.ptScreenPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool GetHBmpBits32(HBITMAP hBmp, int& width, int& height, std::vector<BYTE>& 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<ID3D11Texture2D> 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<BYTE> 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<size_t>(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<IWICImagingFactory2> wicFactory = App::GetInstance().GetWICImageFactory();
|
|
if (!wicFactory) {
|
|
SPDLOG_LOGGER_ERROR(logger, "获取 WICImageFactory 失败");
|
|
return false;
|
|
}
|
|
|
|
ComPtr<IWICBitmap> 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<BYTE> 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 (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("获取光标移速失败"));
|
|
}
|
|
|
|
SPDLOG_LOGGER_INFO(logger, "已调整光标移速");
|
|
}
|
|
|
|
// 全局隐藏光标
|
|
if (!MagShowSystemCursor(FALSE)) {
|
|
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("MagShowSystemCursor 失败"));
|
|
}
|
|
|
|
// 移动光标位置
|
|
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);
|
|
cursorPt.y = std::clamp(cursorPt.y, hostRect.top + _destRect.top, hostRect.top + _destRect.bottom);
|
|
|
|
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);
|
|
|
|
SetCursorPos(
|
|
std::lround(posX * (srcClientRect.right - srcClientRect.left) + srcClientRect.left),
|
|
std::lround(posY * (srcClientRect.bottom - srcClientRect.top) + srcClientRect.top)
|
|
);
|
|
|
|
_isUnderCapture = true;
|
|
}
|
|
|
|
void CursorDrawer::_StopCapture(POINT cursorPt) {
|
|
// 在以下情况下离开捕获状态:
|
|
// 1. 当前处于捕获状态
|
|
// 2. 光标离开源窗口客户区
|
|
//
|
|
// 离开捕获状态时
|
|
// 1. 还原光标速度,全局显示光标
|
|
// 2. 将光标移到全屏窗口外的对应位置
|
|
//
|
|
// 在有黑边的情况下自动将光标调整到全屏窗口外
|
|
|
|
if (App::GetInstance().IsAdjustCursorSpeed()) {
|
|
SystemParametersInfo(SPI_SETMOUSESPEED, 0, (PVOID)(intptr_t)_cursorSpeed, 0);
|
|
}
|
|
|
|
MagShowSystemCursor(TRUE);
|
|
|
|
// 移动光标位置
|
|
const RECT& srcClientRect = App::GetInstance().GetSrcClientRect();
|
|
const RECT& hostRect = App::GetInstance().GetHostWndRect();
|
|
|
|
if (cursorPt.x > srcClientRect.right) {
|
|
cursorPt.x = hostRect.right + cursorPt.x - srcClientRect.right;
|
|
} 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;
|
|
}
|
|
|
|
if (cursorPt.y > srcClientRect.bottom) {
|
|
cursorPt.y = hostRect.bottom + cursorPt.y - srcClientRect.bottom;
|
|
} 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;
|
|
}
|
|
|
|
SetCursorPos(cursorPt.x, cursorPt.y);
|
|
|
|
_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);
|
|
}
|
|
}
|