Magpie/Runtime/CursorRenderer.cpp
2021-09-13 00:33:47 +08:00

422 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "pch.h"
#include "CursorRenderer.h"
#include "App.h"
#include "Utils.h"
extern std::shared_ptr<spdlog::logger> logger;
const char noCursorPS[] = R"(
Texture2D tex : register(t0);
SamplerState sam : register(s0);
struct VS_OUTPUT {
float4 Position : SV_POSITION;
float4 TexCoord : TEXCOORD0;
};
float4 main(VS_OUTPUT input) : SV_Target{
return tex.Sample(sam, input.TexCoord);
}
)";
const char withCursorPS[] = R"(
Texture2D inputTex : register(t0);
Texture2D cursorTex : register(t1);
SamplerState linearSam : register(s0);
SamplerState pointSam : register(s1);
cbuffer constants : register(b0) {
uint2 inputTexSize;
};
cbuffer constants : register(b1) {
int4 cursorRect;
bool isMono;
};
struct VS_OUTPUT {
float4 Position : SV_POSITION;
float4 TexCoord : TEXCOORD0;
};
float4 main(VS_OUTPUT input) : SV_TARGET {
float2 coord = input.TexCoord.xy;
int2 pos = floor(coord * inputTexSize);
float3 cur = inputTex.Sample(linearSam, coord).rgb;
if (pos.x < cursorRect.x || pos.x >= cursorRect.z || pos.y < cursorRect.y || pos.y >= cursorRect.w) {
return float4(cur, 1);
} else {
float2 cursorCoord = (pos - cursorRect.xy + 0.5f) / (cursorRect.zw - cursorRect.xy);
cursorCoord.y /= 2;
uint3 andMask = cursorTex.Sample(pointSam, cursorCoord).rgb * 255;
float4 xorMask = cursorTex.Sample(pointSam, float2(cursorCoord.x, cursorCoord.y + 0.5f));
float3 r = ((uint3(cur * 255) & andMask) ^ uint3(xorMask.rgb * 255)) / 255.0f;
// 模拟透明度
// 单色光标的透明度和一般光标不同
return float4(isMono ? r : lerp(r, cur, 1.0f - xorMask.a), 1);
}
}
)";
bool CursorRenderer::Initialize(ComPtr<ID3D11Texture2D> input, ComPtr<ID3D11Texture2D> output) {
App& app = App::GetInstance();
Renderer& renderer = app.GetRenderer();
_d3dDC = renderer.GetD3DDC();
_d3dDevice = renderer.GetD3DDevice();
// 限制鼠标在窗口内
if (!ClipCursor(&App::GetInstance().GetSrcClientRect())) {
SPDLOG_LOGGER_ERROR(logger, "ClipCursor 失败");
}
D3D11_TEXTURE2D_DESC inputDesc, outputDesc;
input->GetDesc(&inputDesc);
output->GetDesc(&outputDesc);
// 如果输出可以容纳输入,将其居中;如果不能容纳,等比缩放到能容纳的最大大小
if (inputDesc.Width <= outputDesc.Width && inputDesc.Height <= outputDesc.Height) {
_destRect.left = (outputDesc.Width - inputDesc.Width) / 2;
_destRect.right = _destRect.left + inputDesc.Width;
_destRect.top = (outputDesc.Height - inputDesc.Height) / 2;
_destRect.bottom = _destRect.top + inputDesc.Height;
} else {
float scaleX = float(outputDesc.Width) / inputDesc.Width;
float scaleY = float(outputDesc.Height) / inputDesc.Height;
if ( scaleX >= scaleY ) {
long width = lroundf(inputDesc.Width * scaleY);
_destRect.left = (outputDesc.Width - width) / 2;
_destRect.right = _destRect.left + width;
_destRect.top = 0;
_destRect.bottom = outputDesc.Height;
} else {
long height = lroundf(inputDesc.Height * scaleX);
_destRect.left = 0;
_destRect.right = outputDesc.Width;
_destRect.top = (outputDesc.Height - height) / 2;
_destRect.bottom = _destRect.top + height;
}
}
const RECT& srcClient = app.GetSrcClientRect();
_scaleX = float(_destRect.right - _destRect.left) / (srcClient.right - srcClient.left);
_scaleY = float(_destRect.bottom - _destRect.top) / (srcClient.bottom - srcClient.top);
SPDLOG_LOGGER_INFO(logger, fmt::format("scaleX{}scaleY{}", _scaleX, _scaleY));
if (App::GetInstance().IsAdjustCursorSpeed()) {
// 设置鼠标移动速度
if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &_cursorSpeed, 0)) {
long newSpeed = std::clamp(lroundf(_cursorSpeed / (_scaleX + _scaleY) * 2), 1L, 20L);
if (!SystemParametersInfo(SPI_SETMOUSESPEED, 0, (PVOID)(intptr_t)newSpeed, 0)) {
SPDLOG_LOGGER_ERROR(logger, "设置光标移速失败");
}
} else {
SPDLOG_LOGGER_ERROR(logger, "获取光标移速失败");
}
SPDLOG_LOGGER_INFO(logger, "已调整光标移速");
}
HRESULT hr = renderer.GetRenderTargetView(output.Get(), &_outputRtv);
if (FAILED(hr)) {
SPDLOG_LOGGER_ERROR(logger, fmt::sprintf("GetRenderTargetView 失败\n\tHRESULT0x%X", hr));
return false;
}
hr = renderer.GetShaderResourceView(input.Get(), &_inputSrv);
if (FAILED(hr)) {
SPDLOG_LOGGER_ERROR(logger, fmt::sprintf("GetShaderResourceView 失败\n\tHRESULT0x%X", hr));
return false;
}
_vp.TopLeftX = (FLOAT)_destRect.left;
_vp.TopLeftY = (FLOAT)_destRect.top;
_vp.Width = float(_destRect.right - _destRect.left);
_vp.Height = float(_destRect.bottom - _destRect.top);
_vp.MinDepth = 0.0f;
_vp.MaxDepth = 1.0f;
_sampler = renderer.GetSampler(Renderer::FilterType::POINT).Get();
ComPtr<ID3DBlob> blob = nullptr;
if (!Utils::CompilePixelShader(noCursorPS, sizeof(noCursorPS), &blob)) {
SPDLOG_LOGGER_ERROR(logger, "编译无光标着色器失败");
return false;
}
hr = renderer.GetD3DDevice()->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, &_noCursorPS);
if (FAILED(hr)) {
SPDLOG_LOGGER_ERROR(logger, fmt::sprintf("创建像素着色器失败\n\tHRESULT0x%X", hr));
return false;
}
if (!Utils::CompilePixelShader(withCursorPS, sizeof(withCursorPS), &blob)) {
SPDLOG_LOGGER_ERROR(logger, "编译有光标着色器失败");
return false;
}
hr = renderer.GetD3DDevice()->CreatePixelShader(blob->GetBufferPointer(), blob->GetBufferSize(), nullptr, &_withCursorPS);
if (FAILED(hr)) {
SPDLOG_LOGGER_ERROR(logger, fmt::sprintf("创建像素着色器失败\n\tHRESULT0x%X", hr));
return false;
}
// 创建存储输入尺寸的缓冲区
D3D11_BUFFER_DESC bd{};
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = 16;
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
D3D11_SUBRESOURCE_DATA initData{};
UINT inputTexSize[4] = { inputDesc.Width, inputDesc.Height, 0, 0 };
initData.pSysMem = inputTexSize;
hr = _d3dDevice->CreateBuffer(&bd, &initData, &_constantBuffer1);
if (FAILED(hr)) {
SPDLOG_LOGGER_ERROR(logger, fmt::sprintf("CreateBuffer 失败\n\tHRESULT0x%X", hr));
return false;
}
// 创建存储光标位置的缓冲区
bd.Usage = D3D11_USAGE_DYNAMIC;
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
bd.ByteWidth = 32;
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
hr = _d3dDevice->CreateBuffer(&bd, nullptr, &_constantBuffer2);
if (FAILED(hr)) {
SPDLOG_LOGGER_ERROR(logger, fmt::sprintf("CreateBuffer 失败\n\tHRESULT0x%X", hr));
return false;
}
_linearSam = renderer.GetSampler(Renderer::FilterType::LINEAR);
_pointSam = renderer.GetSampler(Renderer::FilterType::POINT);
if (!MagShowSystemCursor(FALSE)) {
SPDLOG_LOGGER_ERROR(logger, "MagShowSystemCursor 失败");
}
SPDLOG_LOGGER_INFO(logger, "CursorRenderer 初始化完成");
return true;
}
CursorRenderer::~CursorRenderer() {
ClipCursor(nullptr);
if (App::GetInstance().IsAdjustCursorSpeed()) {
SystemParametersInfo(SPI_SETMOUSESPEED, 0, (PVOID)(intptr_t)_cursorSpeed, 0);
}
MagShowSystemCursor(TRUE);
SPDLOG_LOGGER_INFO(logger, "CursorRenderer 已析构");
}
bool GetHBmpBits32(HBITMAP hBmp, int& width, int& height, std::vector<BYTE>& pixels) {
BITMAP bmp{};
if (!GetObject(hBmp, sizeof(bmp), &bmp)) {
SPDLOG_LOGGER_ERROR(logger, fmt::format("GetObject 失败\n\tLastErrorCode{}", GetLastError()));
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);
if (GetDIBits(hdc, hBmp, 0, height, &pixels[0], &bi, DIB_RGB_COLORS) != height) {
SPDLOG_LOGGER_ERROR(logger, fmt::format("GetDIBits 失败\n\tLastErrorCode{}", GetLastError()));
ReleaseDC(NULL, hdc);
return false;
}
ReleaseDC(NULL, hdc);
return true;
}
bool CursorRenderer::_ResolveCursor(HCURSOR hCursor, _CursorInfo& result) const {
assert(hCursor != NULL);
ICONINFO ii{};
if (!GetIconInfo(hCursor, &ii)) {
SPDLOG_LOGGER_ERROR(logger, fmt::format("GetIconInfo 失败\n\tLastErrorCode{}", GetLastError()));
return false;
}
result.xHotSpot = ii.xHotspot;
result.yHotSpot = ii.yHotspot;
auto clear = [&ii]() {
if (ii.hbmColor) {
DeleteBitmap(ii.hbmColor);
}
DeleteBitmap(ii.hbmMask);
};
std::vector<BYTE> pixels;
result.isMonochrome = ii.hbmColor == NULL;
if (result.isMonochrome) {
// 单色光标
if (!GetHBmpBits32(ii.hbmMask, result.width, result.height, pixels)) {
SPDLOG_LOGGER_ERROR(logger, "GetHBmpBits32 失败");
clear();
return false;
}
} else {
if (!GetHBmpBits32(ii.hbmMask, result.width, result.height, pixels)) {
SPDLOG_LOGGER_ERROR(logger, "GetHBmpBits32 失败");
clear();
return false;
}
std::vector<BYTE> colorMsk;
if (!GetHBmpBits32(ii.hbmColor, result.width, result.height, colorMsk)) {
SPDLOG_LOGGER_ERROR(logger, "GetHBmpBits32 失败");
clear();
return false;
}
if (pixels.size() != colorMsk.size()) {
SPDLOG_LOGGER_ERROR(logger, "hbmMask 和 hbmColor 的尺寸不一致");
clear();
return false;
}
pixels.resize(pixels.size() * 2);
std::memcpy(pixels.data() + colorMsk.size(), colorMsk.data(), colorMsk.size());
result.height *= 2;
}
clear();
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;
ComPtr<ID3D11Texture2D> texture;
HRESULT hr = _d3dDevice->CreateTexture2D(&desc, &initData, &texture);
if (FAILED(hr)) {
SPDLOG_LOGGER_ERROR(logger, fmt::sprintf("创建 Texture2D 失败\n\tHRESULT0x%X", hr));
return false;
}
hr = _d3dDevice->CreateShaderResourceView(texture.Get(), nullptr, &result.masks);
if (FAILED(hr)) {
SPDLOG_LOGGER_ERROR(logger, fmt::sprintf("创建 ShaderResourceView 失败\n\tHRESULT0x%X", hr));
return false;
}
result.height /= 2;
return true;
}
bool CursorRenderer::_DrawWithCursor() {
CURSORINFO ci{};
ci.cbSize = sizeof(ci);
if (!GetCursorInfo(&ci)) {
SPDLOG_LOGGER_ERROR(logger, fmt::format("GetCursorInfo 失败\n\tLastErrorCode{}", GetLastError()));
return false;
}
if (!ci.hCursor || ci.flags != CURSOR_SHOWING) {
return false;
}
_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 false;
}
}
// 映射坐标
// 鼠标坐标为整数,否则会出现模糊
const RECT& srcClient = App::GetInstance().GetSrcClientRect();
POINT targetScreenPos = {
lroundf((ci.ptScreenPos.x - srcClient.left) * _scaleX) - info->xHotSpot,
lroundf((ci.ptScreenPos.y - srcClient.top) * _scaleY) - info->yHotSpot
};
// 向着色器传递光标位置
_d3dDC->PSSetConstantBuffers(0, 0, nullptr);
D3D11_MAPPED_SUBRESOURCE ms{};
HRESULT hr = _d3dDC->Map(_constantBuffer2.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms);
if (FAILED(hr)) {
SPDLOG_LOGGER_ERROR(logger, fmt::sprintf("Map 失败\n\tHRESULT0x%X", hr));
return false;
}
INT* cursorRect = (INT*)ms.pData;
cursorRect[0] = targetScreenPos.x;
cursorRect[1] = targetScreenPos.y;
cursorRect[2] = targetScreenPos.x + info->width;
cursorRect[3] = targetScreenPos.y + info->height;
cursorRect[4] = info->isMonochrome;
_d3dDC->Unmap(_constantBuffer2.Get(), 0);
_d3dDC->PSSetShader(_withCursorPS.Get(), nullptr, 0);
ID3D11ShaderResourceView* srvs[2] = { _inputSrv, info->masks.Get() };
_d3dDC->PSSetShaderResources(0, 2, srvs);
ID3D11Buffer* buffer[2] = { _constantBuffer1.Get(), _constantBuffer2.Get() };
_d3dDC->PSSetConstantBuffers(0, 2, buffer);
ID3D11SamplerState* samplers[2] = { _linearSam.Get(), _pointSam.Get() };
_d3dDC->PSSetSamplers(0, 2, samplers);
return true;
}
void CursorRenderer::Draw() {
_d3dDC->OMSetRenderTargets(1, &_outputRtv, nullptr);
_d3dDC->RSSetViewports(1, &_vp);
if (!_DrawWithCursor()) {
// 不显示鼠标或创建映射失败
_d3dDC->PSSetShaderResources(0, 1, &_inputSrv);
_d3dDC->PSSetShader(_noCursorPS.Get(), nullptr, 0);
_d3dDC->PSSetSamplers(0, 1, &_sampler);
}
_d3dDC->Draw(3, 0);
}