Magpie/Runtime/CursorRenderer.cpp
2021-09-21 22:30:07 +08:00

383 lines
11 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"
#include "shaders/CursorPS.h"
#include <VertexTypes.h>
extern std::shared_ptr<spdlog::logger> logger;
bool CursorRenderer::Initialize(ComPtr<ID3D11Texture2D> renderTarget, SIZE outputSize) {
App& app = App::GetInstance();
Renderer& renderer = app.GetRenderer();
_d3dDC = renderer.GetD3DDC();
_d3dDevice = renderer.GetD3DDevice();
// 限制鼠标在窗口内
/*if (!ClipCursor(&App::GetInstance().GetSrcClientRect())) {
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("ClipCursor 失败"));
}*/
D3D11_TEXTURE2D_DESC rtDesc;
renderTarget->GetDesc(&rtDesc);
_vp.TopLeftX = 0;
_vp.TopLeftY = 0;
_vp.Width = rtDesc.Width;
_vp.Height = rtDesc.Height;
_vp.MinDepth = 0.0f;
_vp.MaxDepth = 1.0f;
const RECT& srcClient = app.GetSrcClientRect();
SIZE srcSize = { srcClient.right - srcClient.left, srcClient.bottom - srcClient.top };
_destRect.left = (outputSize.cx - srcSize.cx) / 2;
_destRect.right = _destRect.left + srcSize.cx;
_destRect.top = (outputSize.cy - srcSize.cy) / 2;
_destRect.bottom = _destRect.top + srcSize.cy;
_scaleX = float(_destRect.right - _destRect.left) / srcSize.cx;
_scaleY = float(_destRect.bottom - _destRect.top) / srcSize.cy;
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, MakeWin32ErrorMsg("设置光标移速失败"));
}
} else {
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("获取光标移速失败"));
}
SPDLOG_LOGGER_INFO(logger, "已调整光标移速");
}*/
HRESULT hr = renderer.GetRenderTargetView(renderTarget.Get(), &_rtv);
if (FAILED(hr)) {
SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("GetRenderTargetView 失败", hr));
return false;
}
hr = renderer.GetD3DDevice()->CreatePixelShader(CursorPSShaderByteCode, sizeof(CursorPSShaderByteCode), nullptr, &_cursorPS);
if (FAILED(hr)) {
SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("创建像素着色器失败", 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, fmt::sprintf("创建顶点缓冲区失败\n\tHRESULT0x%X", hr));
return false;
}
if (!renderer.GetSampler(Renderer::FilterType::LINEAR, &_linearSam)
|| !renderer.GetSampler(Renderer::FilterType::POINT, &_pointSam)
) {
SPDLOG_LOGGER_ERROR(logger, "GetSampler 失败");
return false;
}
/*if (!MagShowSystemCursor(FALSE)) {
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("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, 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);
if (GetDIBits(hdc, hBmp, 0, height, &pixels[0], &bi, DIB_RGB_COLORS) != height) {
SPDLOG_LOGGER_ERROR(logger, MakeWin32ErrorMsg("GetDIBits 失败"));
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, MakeWin32ErrorMsg("GetIconInfo 失败"));
return false;
}
result.xHotSpot = ii.xHotspot;
result.yHotSpot = ii.yHotspot;
auto clear = [&ii]() {
if (ii.hbmColor) {
DeleteBitmap(ii.hbmColor);
}
DeleteBitmap(ii.hbmMask);
};
result.isMonochrome = ii.hbmColor == NULL;
ComPtr<ID3D11Texture2D> texture;
if (result.isMonochrome) {
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);
clear();
// 单色光标的纹理由两部分组成:上半部分为单色光标的掩码,红色通道是 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;
upPtr += 4;
downPtr += 4;
}
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;
}
result.height /= 2;
} else {
// 彩色光标,使用 WIC 将光标转换为带 Alpha 通道的图像
clear();
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;
}
bool CursorRenderer::_DrawWithCursor() {
/*
// 向着色器传递光标信息
_d3dDC->PSSetConstantBuffers(0, 0, nullptr);
D3D11_MAPPED_SUBRESOURCE ms{};
HRESULT hr = _d3dDC->Map(_withCursorCB.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms);
if (FAILED(hr)) {
SPDLOG_LOGGER_ERROR(logger, MakeComErrorMsg("Map 失败", hr));
return false;
}
FLOAT* cursorRect = (FLOAT*)ms.pData;
cursorRect[0] = float(targetScreenPos.x) / _inputSize.cx;
cursorRect[1] = float(targetScreenPos.y) / _inputSize.cy;
cursorRect[2] = float(targetScreenPos.x + info->width) / _inputSize.cx;
cursorRect[3] = float(targetScreenPos.y + info->height) / _inputSize.cy;
_d3dDC->Unmap(_withCursorCB.Get(), 0);
_d3dDC->PSSetShader(_cursorPS.Get(), nullptr, 0);
ID3D11ShaderResourceView* srvs[2] = { _inputSrv, info->texture.Get() };
_d3dDC->PSSetShaderResources(0, 2, srvs);
ID3D11Buffer* withCursorCB = _withCursorCB.Get();
_d3dDC->PSSetConstantBuffers(0, 1, &withCursorCB);
ID3D11SamplerState* samplers[2] = { _linearSam, _pointSam };
_d3dDC->PSSetSamplers(0, 2, samplers);
*/
return true;
}
void CursorRenderer::Draw() {
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;
}
}
// 映射坐标
// 鼠标坐标为整数,否则会出现模糊
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->OMSetRenderTargets(1, &_rtv, nullptr);
//_d3dDC->RSSetViewports(1, &_vp);
/*
D3D11_MAPPED_SUBRESOURCE ms;
_d3dDC->Map(_vtxBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms);
VertexPositionTexture vertices[] = {
{ XMFLOAT3(outputLeft, outputTop, 0.5f), XMFLOAT2(0.0f, 0.0f) },
{ XMFLOAT3(outputRight, outputTop, 0.5f), XMFLOAT2(1.0f, 0.0f) },
{ XMFLOAT3(outputLeft, outputBottom, 0.5f), XMFLOAT2(0.0f, 1.0f) },
{ XMFLOAT3(outputRight, outputBottom, 0.5f), XMFLOAT2(1.0f, 1.0f) }
};
_d3dDC->Unmap(_vtxBuffer.Get(), 0);
if (!info->isMonochrome) {
D3D11_BLEND_DESC desc{};
ID3D11BlendState* _blendState;
_d3dDevice->CreateBlendState(&desc, &_blendState);
_d3dDC->OMSetBlendState()
}*/
//_d3dDC->Draw(4, 0);
}