初步支持 WinRT Capturer API

This commit is contained in:
Xu Liu 2021-04-19 21:45:36 +08:00
commit 8d21ff0c75
11 changed files with 156 additions and 161 deletions

View file

@ -20,9 +20,37 @@ namespace Magpie {
{
""effect"": ""scale"",
""type"": ""Anime4KxDenoise""
},
{
""effect"": ""scale"",
""type"": ""mitchell"",
""scale"": [0,0],
""useSharperVersion"": true
},
{
""effect"": ""sharpen"",
""type"": ""adaptive"",
""curveHeight"": 0.2
}
]";
private const string CommonEffectJson = @"[
{
""effect"": ""scale"",
""type"": ""lanczos6"",
""scale"": [0,0],
""ARStrength"": 0.7
},
{
""effect"": ""sharpen"",
""type"": ""adaptive"",
""curveHeight"": 0.6
},
{
""effect"": ""sharpen"",
""type"": ""builtIn"",
""sharpness"": 0.5,
""threshold"": 0.5
}
]";
IKeyboardMouseEvents keyboardEvents = null;
@ -57,6 +85,7 @@ namespace Magpie {
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e) {
DestroyMagWindow();
Settings.Default.Save();
}
@ -94,9 +123,7 @@ namespace Magpie {
HookCursorAtRuntime();
}
} else {
NativeMethods.BroadcastMessage(NativeMethods.MAGPIE_WM_DESTORYMAG);
thread.Abort();
thread = null;
DestroyMagWindow();
}
}
}});
@ -111,6 +138,14 @@ namespace Magpie {
}
private void DestroyMagWindow() {
NativeMethods.BroadcastMessage(NativeMethods.MAGPIE_WM_DESTORYMAG);
if(thread != null) {
thread.Abort();
thread = null;
}
}
private void HookCursorAtRuntime() {
IntPtr hwndSrc = NativeMethods.GetForegroundWindow();
int pid = NativeMethods.GetWindowProcessId(hwndSrc);

View file

@ -16,13 +16,16 @@ cbuffer constants : register(b0) {
D2D_PS_ENTRY(main) {
InitMagpieSampleInputWithScale(float2(2, 2));
float4 coord0 = D2DGetInputCoordinate(0);
coord0.xy /= float2(2,2); // 将 dest 坐标映射为 src 坐标
float4 coord1 = D2DGetInputCoordinate(1);
coord1.xy /= float2(2, 2); // 将 dest 坐标映射为 src 坐标
float2 f = frac(coord.xy / coord.zw);
float2 f = frac(coord1.xy / coord1.zw);
// 截取整数部分,如果使用 round 会有 bug因为在特殊情况下 f 可能等于 0.75
int2 i = int2(f * 2);
float l = Uncompress(SampleInputRGBAOffNoCheck(1, (float2(0.5, 0.5) - f)))[i.y * 2 + i.x];
float l = Uncompress(D2DSampleInput(1, coord1.xy + (float2(0.5, 0.5) - f) * coord1.zw))[i.y * 2 + i.x];
float3 yuv = RGB2YUV(SampleInputCur(0));
float3 yuv = RGB2YUV(D2DSampleInput(0, coord0.xy).xyz);
return float4(YUV2RGB(yuv.x+l, yuv.y, yuv.z), 1);
}

View file

@ -45,6 +45,14 @@ public:
return _d3dDevice.Get();
}
const ID2D1Device* GetD2DDevice() const {
return _d2dDevice.Get();
}
ID2D1Device* GetD2DDevice() {
return _d2dDevice.Get();
}
void Render(std::function<void()> renderFunc) {
if (_lowLantencyMode) {
WaitForSingleObjectEx(_frameLatencyWaitableObject, 1000, true);

View file

@ -26,9 +26,9 @@ public:
_SetDestSize(srcSize);
_ReadEffectsJson(effectsJson);
// 计算输出位置
float x = float(_hostClient.right - _hostClient.left - _outputSize.cx) / 2;
float y = float(_hostClient.bottom - _hostClient.top - _outputSize.cy) / 2;
// 计算输出位置x 和 y 必须为整数,否则会使画面模糊
float x = float((_hostClient.right - _hostClient.left - _outputSize.cx) / 2);
float y = float((_hostClient.bottom - _hostClient.top - _outputSize.cy) / 2);
_outputRect = RectF(x, y, x + _outputSize.cx, y + _outputSize.cy);
}
@ -38,66 +38,28 @@ public:
void Render() override {
ComPtr<ID2D1Image> outputImg = nullptr;
_outputEffect->GetOutput(&outputImg);
if (_firstEffect) {
_firstEffect->SetInput(0, _inputBmp.Get());
_d2dContext.GetD2DDC()->DrawImage(
outputImg.Get(),
Point2F(_outputRect.left, _outputRect.top)
);
}
ComPtr<ID2D1Image> outputImg = nullptr;
_outputEffect->GetOutput(&outputImg);
void SetInput(IWICBitmapSource* srcBmp) {
assert(srcBmp != nullptr);
if (!_d2dSourceEffect) {
// 创建 Source effect
Debug::ThrowIfComFailed(
_d2dContext.GetD2DDC()->CreateEffect(CLSID_D2D1BitmapSource, &_d2dSourceEffect),
L"创建 D2D1BitmapSource 失败"
_d2dContext.GetD2DDC()->DrawImage(
outputImg.Get(),
Point2F(_outputRect.left, _outputRect.top)
);
} else {
_d2dContext.GetD2DDC()->DrawImage(
_inputBmp.Get(),
Point2F(_outputRect.left, _outputRect.top)
);
if (_firstEffect) {
_firstEffect->SetInputEffect(0, _d2dSourceEffect.Get());
} else {
_outputEffect = _firstEffect = _d2dSourceEffect;
}
}
Debug::ThrowIfComFailed(
_d2dSourceEffect->SetValue(D2D1_BITMAPSOURCE_PROP_WIC_BITMAP_SOURCE, srcBmp),
L"设置 D2D1BitmapSource 源失败"
);
if (!_outputEffect) {
_outputEffect = _d2dSourceEffect;
}
}
void SetInput(ID2D1Bitmap* srcBmp, const RECT& clientRect) {
void SetInput(ComPtr<ID2D1Bitmap> srcBmp) {
assert(srcBmp != nullptr);
if (!_d2dCropEffect) {
// 创建 Crop Effect
Debug::ThrowIfComFailed(
_d2dContext.GetD2DDC()->CreateEffect(CLSID_D2D1Crop, &_d2dCropEffect),
L"创建 D2D1Crop 失败"
);
if (!_firstEffect) {
_outputEffect = _firstEffect = _d2dCropEffect;
} else {
_firstEffect->SetInputEffect(0, _d2dCropEffect.Get());
}
}
Debug::ThrowIfComFailed(
_d2dCropEffect->SetValue(
D2D1_CROP_PROP_RECT,
RectF(clientRect.left, clientRect.top, clientRect.right, clientRect.bottom)
),
L"设置 D2D1_CROP_PROP_RECT 失败"
);
_d2dCropEffect->SetInput(0, srcBmp);
_inputBmp = srcBmp;
}
const D2D1_RECT_F& GetOutputRect() const {
@ -570,8 +532,8 @@ private:
}
}
ComPtr<ID2D1Effect> _d2dSourceEffect = nullptr;
ComPtr<ID2D1Effect> _d2dCropEffect = nullptr;
ComPtr<ID2D1Bitmap> _inputBmp = nullptr;
ComPtr<ID2D1Effect> _firstEffect = nullptr;
ComPtr<ID2D1Effect> _outputEffect = nullptr;

View file

@ -8,19 +8,21 @@
class GDIWindowCapturer : public WindowCapturerBase {
public:
GDIWindowCapturer(
D2DContext& d2dContext,
HWND hwndSrc,
const RECT& srcRect,
IWICImagingFactory2* wicImgFactory,
bool useBitblt = false
): _wicImgFactory(wicImgFactory), _srcRect(srcRect), _hwndSrc(hwndSrc), _useBitblt(useBitblt) {
): WindowCapturerBase(d2dContext), _wicImgFactory(wicImgFactory), _srcRect(srcRect), _hwndSrc(hwndSrc), _useBitblt(useBitblt) {
}
std::variant<ComPtr<IWICBitmapSource>, ComPtr<ID2D1Bitmap1>> GetFrame() override {
if (_useBitblt) {
return _GetFrameWithBitblt();
} else {
return _GetFrameWithNoBitblt();
}
ComPtr<ID2D1Bitmap> GetFrame() override {
ComPtr<IWICBitmapSource> wicBmp = _useBitblt ? _GetFrameWithBitblt() : _GetFrameWithNoBitblt();
ComPtr<ID2D1Bitmap> bmp;
_d2dContext.GetD2DDC()->CreateBitmapFromWicBitmap(wicBmp.Get(), &bmp);
return bmp;
}
private:

View file

@ -9,11 +9,11 @@
class MagCallbackWindowCapturer : public WindowCapturerBase {
public:
MagCallbackWindowCapturer(
D2DContext& d2dContext,
HINSTANCE hInstance,
HWND hwndHost,
const RECT& srcRect,
IWICImagingFactory2* wicImgFactory
): _srcRect(srcRect), _wicImgFactory(wicImgFactory) {
const RECT& srcRect
): WindowCapturerBase(d2dContext), _srcRect(srcRect) {
if (_instance) {
Debug::Assert(false, L"已存在 MagCallbackWindowCapturer 实例");
}
@ -47,13 +47,13 @@ public:
_instance = nullptr;
}
std::variant<ComPtr<IWICBitmapSource>, ComPtr<ID2D1Bitmap1>> GetFrame() override {
ComPtr<ID2D1Bitmap> GetFrame() override {
// MagSetWindowSource 是同步执行的
if (!MagSetWindowSource(_hwndMag, _srcRect)) {
Debug::WriteErrorMessage(L"MagSetWindowSource 失败");
}
return _wicBmp;
return _bmp;
}
private:
@ -75,28 +75,22 @@ private:
Debug::WriteErrorMessage(L"srcdata 不是BGRA格式");
return FALSE;
}
Debug::ThrowIfComFailed(
_instance->_wicImgFactory->CreateBitmapFromMemory(
srcheader.width,
srcheader.height,
GUID_WICPixelFormat32bppPBGRA,
srcheader.stride,
(UINT)srcheader.cbSize,
(BYTE*)srcdata,
&_instance->_wicBmp
),
L"´ÓÄÚ´æ´´½¨ WICBitmap ʧ°Ü"
_instance->_d2dContext.GetD2DDC()->CreateBitmap(
{ srcheader.width, srcheader.height },
BitmapProperties(PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)),
& _instance->_bmp
);
D2D1_RECT_U destRect = { 0, 0, srcheader.width, srcheader.height };
_instance->_bmp->CopyFromMemory(&destRect, srcdata, srcheader.stride);
return TRUE;
}
HWND _hwndMag = NULL;
const RECT& _srcRect;
IWICImagingFactory2* _wicImgFactory;
ComPtr<IWICBitmap> _wicBmp = nullptr;
ComPtr<ID2D1Bitmap> _bmp = nullptr;
static MagCallbackWindowCapturer* _instance;
};

View file

@ -86,10 +86,6 @@ private:
L"非法的抓取模式"
);
Debug::ThrowIfWin32Failed(
GetWindowRect(_hwndSrc, &_srcRect),
L"GetWindowRect ʧ°Ü"
);
Utils::GetClientScreenRect(_hwndSrc, _srcClient);
_RegisterHostWndClass();
@ -120,22 +116,23 @@ private:
if (captureMode == 0) {
_windowCapturer.reset(new WinRTCapturer(
*_d2dContext,
_hwndSrc,
_srcClient,
*_d2dContext
_srcClient
));
} else if (captureMode == 1) {
_windowCapturer.reset(new GDIWindowCapturer(
*_d2dContext,
_hwndSrc,
_srcClient,
_wicImgFactory.Get()
));
} else {
_windowCapturer.reset(new MagCallbackWindowCapturer(
*_d2dContext,
hInstance,
_hwndHost,
_srcClient,
_wicImgFactory.Get()
_srcClient
));
}
@ -168,7 +165,9 @@ private:
void _Render() {
try {
const auto& frame = _windowCapturer->GetFrame();
_renderManager->Render(frame, _srcRect, _srcClient);
if (frame) {
_renderManager->Render(frame);
}
} catch (const magpie_exception& e) {
Debug::WriteErrorMessage(L"渲染失败:" + e.what());
} catch (...) {
@ -285,7 +284,6 @@ private:
HINSTANCE _hInst;
HWND _hwndHost = NULL;
HWND _hwndSrc;
RECT _srcRect{};
RECT _srcClient{};
RECT _hostClient{};

View file

@ -46,56 +46,23 @@ public:
}
void Render(
const std::variant<ComPtr<IWICBitmapSource>, ComPtr<ID2D1Bitmap1>>& frame,
const RECT& srcRect,
const RECT& srcClient
ComPtr<ID2D1Bitmap> frame
) {
if (frame.index() == 0) {
_d2dContext.Render([&]() {
_d2dContext.GetD2DDC()->Clear();
assert(frame);
_effectManager->SetInput(std::get<0>(frame).Get());
_effectManager->Render();
_d2dContext.Render([&]() {
_d2dContext.GetD2DDC()->Clear();
if (_frameCatcher) {
_frameCatcher->Render();
}
if (_cursorManager) {
_cursorManager->Render();
}
});
} else {
const auto& f = std::get<1>(frame);
if (!f) {
return;
_effectManager->SetInput(frame);
_effectManager->Render();
if (_frameCatcher) {
_frameCatcher->Render();
}
_d2dContext.Render([&]() {
_d2dContext.GetD2DDC()->Clear();
RECT rect{};
_effectManager->SetInput(
f.Get(),
{
srcClient.left - srcRect.left,
srcClient.top - srcRect.top,
srcClient.right - srcRect.left,
srcClient.bottom - srcRect.top
}
);
_effectManager->Render();
if (_frameCatcher) {
_frameCatcher->Render();
}
if (_cursorManager) {
_cursorManager->Render();
}
});
}
if (_cursorManager) {
_cursorManager->Render();
}
});
}
private:
IDWriteFactory* _GetDWFactory() {

View file

@ -24,10 +24,10 @@ using namespace Windows::Graphics::DirectX::Direct3D11;
class WinRTCapturer : public WindowCapturerBase {
public:
WinRTCapturer(
D2DContext& d2dContext,
HWND hwndSrc,
const RECT& srcRect,
D2DContext& d2dContext
) : _srcRect(srcRect), _hwndSrc(hwndSrc), _d2dContext(d2dContext),
const RECT& srcClient
) : WindowCapturerBase(d2dContext), _srcClient(srcClient), _hwndSrc(hwndSrc),
_captureFramePool(nullptr), _captureSession(nullptr), _captureItem(nullptr), _wrappedDevice(nullptr)
{
winrt::init_apartment(winrt::apartment_type::multi_threaded);
@ -87,10 +87,10 @@ public:
winrt::uninit_apartment();
}
std::variant<ComPtr<IWICBitmapSource>, ComPtr<ID2D1Bitmap1>> GetFrame() override {
ComPtr<ID2D1Bitmap> GetFrame() override {
winrt::Direct3D11CaptureFrame frame = _captureFramePool.TryGetNextFrame();
if (!frame) {
return ComPtr<ID2D1Bitmap1>();
return ComPtr<ID2D1Bitmap>();
}
winrt::IDirect3DSurface d3dSurface = frame.Surface();
@ -106,18 +106,39 @@ public:
L"´Ó»ñÈ¡ IDirect3DSurface »ñÈ¡ IDXGISurface ʧ°Ü"
);
ComPtr<ID2D1Bitmap1> bmp;
ComPtr<ID2D1Bitmap> withFrame;
auto p = BitmapProperties(PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE));
_d2dContext.GetD2DDC()->CreateSharedBitmap(__uuidof(IDXGISurface), dxgiSurface.get(), &p, &withFrame);
RECT srcRect{};
Debug::ThrowIfComFailed(
_d2dContext.GetD2DDC()->CreateBitmapFromDxgiSurface(dxgiSurface.get(), BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)), &bmp),
L""
DwmGetWindowAttribute(_hwndSrc, DWMWA_EXTENDED_FRAME_BOUNDS, &srcRect, sizeof(srcRect)),
L"GetWindowRect ʧ°Ü"
);
return bmp;
ComPtr<ID2D1Bitmap> withoutFrame;
_d2dContext.GetD2DDC()->CreateBitmap(
{ UINT32(_srcClient.right - _srcClient.left), UINT32(_srcClient.bottom - _srcClient.top) },
BitmapProperties(PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)),
&withoutFrame
);
D2D1_POINT_2U destPoint{ 0,0 };
D2D1_RECT_U srcPoint{
UINT32(_srcClient.left - srcRect.left),
UINT32(_srcClient.top - srcRect.top),
UINT32(_srcClient.right - srcRect.left),
UINT32(_srcClient.bottom - srcRect.top)
};
withoutFrame->CopyFromBitmap(&destPoint, withFrame.Get(), &srcPoint);
return withoutFrame;
}
private:
const RECT& _srcRect;
HWND _hwndSrc;
D2DContext& _d2dContext;
const RECT& _srcClient;
winrt::Direct3D11CaptureFramePool _captureFramePool;
winrt::GraphicsCaptureSession _captureSession;

View file

@ -1,15 +1,18 @@
#pragma once
#include "pch.h"
#include <variant>
#include "D2DContext.h"
class WindowCapturerBase {
public:
WindowCapturerBase() {}
WindowCapturerBase(D2DContext& d2dContext): _d2dContext(d2dContext) {}
virtual ~WindowCapturerBase() {}
WindowCapturerBase(const WindowCapturerBase&) = delete;
WindowCapturerBase(WindowCapturerBase&&) = delete;
virtual std::variant<ComPtr<IWICBitmapSource>, ComPtr<ID2D1Bitmap1>> GetFrame() = 0;
virtual ComPtr<ID2D1Bitmap> GetFrame() = 0;
protected:
D2DContext& _d2dContext;
};

View file

@ -18,6 +18,7 @@
#include <dxgi1_6.h>
#include <dwrite_3.h>
#include <wrl.h>
#include <dwmapi.h>
// C 运行时头文件
#include <malloc.h>
@ -46,3 +47,4 @@
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "windowsapp")
#pragma comment(lib, "dwmapi.lib")