mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
feat: WGC 支持脏矩形 (p1)
This commit is contained in:
parent
459c91f7fc
commit
b23964c2a8
9 changed files with 329 additions and 33 deletions
|
|
@ -328,6 +328,10 @@ void CursorManager::_RestoreCursorSpeed() noexcept {
|
|||
//
|
||||
// 这个函数使用 ClipCursor 将光标限制在目标位置一段时间,等待系统将输入队列处理完毕。
|
||||
void CursorManager::_ReliableSetCursorPos(POINT pos) const noexcept {
|
||||
if (ScalingWindow::Get().Options().IsDebugMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RECT originClipRect;
|
||||
GetClipCursor(&originClipRect);
|
||||
|
||||
|
|
|
|||
|
|
@ -172,10 +172,9 @@ bool DuplicateFrameChecker::Initialize(
|
|||
|
||||
HRESULT DuplicateFrameChecker::CheckFrame(
|
||||
ID3D12Resource* frameResource,
|
||||
const SmallVectorImpl<Rect>& dirtyRects,
|
||||
bool& isDuplicate
|
||||
SmallVectorImpl<Rect>& dirtyRects
|
||||
) noexcept {
|
||||
assert(!dirtyRects.empty() && !isDuplicate);
|
||||
assert(!dirtyRects.empty());
|
||||
|
||||
#ifdef _DEBUG
|
||||
{
|
||||
|
|
@ -195,7 +194,6 @@ HRESULT DuplicateFrameChecker::CheckFrame(
|
|||
}
|
||||
|
||||
if (_isFirstFrame) {
|
||||
isDuplicate = false;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
|
@ -243,8 +241,7 @@ HRESULT DuplicateFrameChecker::CheckFrame(
|
|||
{.uintVal = dirtyRect.left},
|
||||
{.uintVal = dirtyRect.top}
|
||||
};
|
||||
_commandList->SetComputeRoot32BitConstants(
|
||||
0, (UINT)std::size(constants), constants, 3 * sizeof(DirectXHelper::Constant32));
|
||||
_commandList->SetComputeRoot32BitConstants(0, (UINT)std::size(constants), constants, 3);
|
||||
}
|
||||
|
||||
constexpr uint32_t BLOCK_SIZE = 16;
|
||||
|
|
@ -288,6 +285,7 @@ HRESULT DuplicateFrameChecker::CheckFrame(
|
|||
}
|
||||
|
||||
// 读取结果
|
||||
SmallVector<uint32_t, 4> removeList;
|
||||
{
|
||||
CD3DX12_RANGE range(0, dirtyRectCount * sizeof(uint32_t));
|
||||
|
||||
|
|
@ -298,10 +296,10 @@ HRESULT DuplicateFrameChecker::CheckFrame(
|
|||
return hr;
|
||||
}
|
||||
|
||||
isDuplicate = true;
|
||||
for (uint32_t i = 0; i < dirtyRectCount; ++i) {
|
||||
if (((uint32_t*)pData)[i] == _curTargetValue) {
|
||||
isDuplicate = false;
|
||||
if (((uint32_t*)pData)[i] != _curTargetValue) {
|
||||
// 此矩形内画面无变化
|
||||
removeList.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -309,6 +307,14 @@ HRESULT DuplicateFrameChecker::CheckFrame(
|
|||
_resultReadbackBuffer->Unmap(0, &range);
|
||||
}
|
||||
|
||||
if (!removeList.empty()) {
|
||||
// 从后向前删除
|
||||
std::sort(removeList.begin(), removeList.end(), std::greater<uint32_t>());
|
||||
for (uint32_t idx : removeList) {
|
||||
dirtyRects.erase(dirtyRects.begin() + idx);
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,7 @@ public:
|
|||
Size frameSize
|
||||
) noexcept;
|
||||
|
||||
HRESULT CheckFrame(
|
||||
ID3D12Resource* frameResource,
|
||||
const SmallVectorImpl<Rect>& dirtyRects,
|
||||
bool& isDuplicate
|
||||
) noexcept;
|
||||
HRESULT CheckFrame(ID3D12Resource* frameResource, SmallVectorImpl<Rect>& dirtyRects) noexcept;
|
||||
|
||||
void OnFrameAdopted() noexcept;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "Logger.h"
|
||||
#include "ScalingWindow.h"
|
||||
#include "Win32Helper.h"
|
||||
#include "RectHelper.h"
|
||||
#include <dwmapi.h>
|
||||
#include <Windows.Graphics.Capture.Interop.h>
|
||||
#include <windows.graphics.directx.direct3d11.interop.h>
|
||||
|
|
@ -20,6 +21,211 @@ using namespace Windows::Graphics::DirectX::Direct3D11;
|
|||
|
||||
namespace Magpie {
|
||||
|
||||
static bool IsCornerInRect(Point p, const Rect& r) noexcept {
|
||||
return p.x >= r.left && p.x <= r.right && p.y >= r.top && p.y <= r.bottom;
|
||||
}
|
||||
|
||||
static bool OptimizeDirtyRectPair(Rect& rect1, Rect& rect2, bool reversed = false) noexcept {
|
||||
if (RectHelper::IsEmpty(rect1) || RectHelper::IsEmpty(rect2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 计算 rect2 有几个角在 rect1 内
|
||||
bool lt = IsCornerInRect(Point{ rect2.left, rect2.top }, rect1);
|
||||
bool rt = IsCornerInRect(Point{ rect2.right, rect2.top }, rect1);
|
||||
bool rb = IsCornerInRect(Point{ rect2.right, rect2.bottom }, rect1);
|
||||
bool lb = IsCornerInRect(Point{ rect2.left, rect2.bottom }, rect1);
|
||||
uint32_t count = (uint32_t)lt + (uint32_t)rt + (uint32_t)rb + (uint32_t)lb;
|
||||
|
||||
if (count == 0) {
|
||||
// 尝试反向
|
||||
if (!reversed) {
|
||||
return OptimizeDirtyRectPair(rect2, rect1, true);
|
||||
}
|
||||
} else if (count == 2) {
|
||||
// rect2 有两个角在 rect1 内时可以合并或裁剪
|
||||
if (lt) {
|
||||
if (rt) {
|
||||
if (rect2.left == rect1.left && rect2.right == rect1.right) {
|
||||
// rect2 合并进 rect1
|
||||
rect1.bottom = rect2.bottom;
|
||||
rect2.right = rect2.left;
|
||||
return true;
|
||||
} else if (rect2.top != rect1.bottom) {
|
||||
// 裁剪 rect2
|
||||
rect2.top = rect1.bottom;
|
||||
assert(rect2.bottom >= rect2.top);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
assert(lb);
|
||||
if (rect2.top == rect1.top && rect2.bottom == rect1.bottom) {
|
||||
rect1.right = rect2.right;
|
||||
rect2.right = rect2.left;
|
||||
return true;
|
||||
} else if (rect2.left != rect1.right) {
|
||||
rect2.left = rect1.right;
|
||||
assert(rect2.right >= rect2.left);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert(rb);
|
||||
if (rt) {
|
||||
if (rect2.top == rect1.top && rect2.bottom == rect1.bottom) {
|
||||
rect1.left = rect2.left;
|
||||
rect2.right = rect2.left;
|
||||
return true;
|
||||
} else if (rect2.right != rect1.left) {
|
||||
rect2.right = rect1.left;
|
||||
assert(rect2.right >= rect2.left);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (rect2.left == rect1.left && rect2.right == rect1.right) {
|
||||
rect1.top = rect2.top;
|
||||
rect2.right = rect2.left;
|
||||
return true;
|
||||
} else if (rect2.bottom != rect1.top) {
|
||||
rect2.bottom = rect1.top;
|
||||
assert(rect2.bottom >= rect2.top);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (count == 4) {
|
||||
// rect2 在 rect1 内
|
||||
rect2.right = rect2.left;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void OptimizeDirtyRects(SmallVectorImpl<Rect>& dirtyRects) noexcept {
|
||||
// 持续循环直到不再能优化
|
||||
while (true) {
|
||||
const uint32_t count = (uint32_t)dirtyRects.size();
|
||||
assert(count > 0);
|
||||
|
||||
bool optimized = false;
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
for (uint32_t j = i + 1; j < count; ++j) {
|
||||
if (OptimizeDirtyRectPair(dirtyRects[i], dirtyRects[j])) {
|
||||
optimized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!optimized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 从后向前删除空矩形
|
||||
for (int i = int(count - 1); i >= 0; --i) {
|
||||
const Rect& rect = dirtyRects[i];
|
||||
if (RectHelper::IsEmpty(rect)) {
|
||||
dirtyRects.erase(dirtyRects.begin() + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OptimizeDirtyRects2(SmallVectorImpl<Rect>& dirtyRects) noexcept {
|
||||
OptimizeDirtyRects(dirtyRects);
|
||||
|
||||
while (true) {
|
||||
const uint32_t count = (uint32_t)dirtyRects.size();
|
||||
|
||||
uint32_t originTotalPixels = 0;
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
originTotalPixels += RectHelper::CalcArea(dirtyRects[i]);
|
||||
}
|
||||
|
||||
uint32_t minTotalPixels = std::numeric_limits<uint32_t>::max();
|
||||
uint32_t targetRectCount = 0;
|
||||
uint32_t targetI = 0;
|
||||
uint32_t targetJ = 0;
|
||||
// 遍历所有两两合并的方式找出总像素数最少的
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
for (uint32_t j = i + 1; j < count; ++j) {
|
||||
Rect unionedRect = RectHelper::Union(dirtyRects[i], dirtyRects[j]);
|
||||
uint32_t totalPixels = 0;
|
||||
uint32_t rectCount = 0;
|
||||
|
||||
// 为了降低复杂度这里只优化一轮,而不是调用 OptimizeDirtyRects
|
||||
for (uint32_t k = 0; k < count; ++k) {
|
||||
if (k == i || k == j) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Rect curRect = dirtyRects[k];
|
||||
OptimizeDirtyRectPair(curRect, unionedRect);
|
||||
|
||||
if (!RectHelper::IsEmpty(curRect)) {
|
||||
totalPixels += RectHelper::CalcArea(curRect);
|
||||
++rectCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (!RectHelper::IsEmpty(unionedRect)) {
|
||||
totalPixels += RectHelper::CalcArea(unionedRect);
|
||||
++rectCount;
|
||||
}
|
||||
|
||||
if (totalPixels < minTotalPixels || (totalPixels == minTotalPixels && rectCount < targetRectCount)) {
|
||||
minTotalPixels = totalPixels;
|
||||
targetRectCount = rectCount;
|
||||
targetI = i;
|
||||
targetJ = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (minTotalPixels > originTotalPixels && count <= MAX_CAPTURE_DIRTY_RECTS) {
|
||||
break;
|
||||
}
|
||||
|
||||
Rect unionedRect = RectHelper::Union(dirtyRects[targetI], dirtyRects[targetJ]);
|
||||
dirtyRects.erase(dirtyRects.begin() + targetJ);
|
||||
dirtyRects.erase(dirtyRects.begin() + targetI);
|
||||
dirtyRects.push_back(unionedRect);
|
||||
|
||||
OptimizeDirtyRects(dirtyRects);
|
||||
|
||||
if (minTotalPixels > originTotalPixels && dirtyRects.size() <= MAX_CAPTURE_DIRTY_RECTS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
static Ignore _ = [] {
|
||||
auto rectComp = [](const Rect& l, const Rect& r) {
|
||||
return std::tuple(l.left, l.top, l.right, l.bottom) <
|
||||
std::tuple(r.left, r.top, r.right, r.bottom);
|
||||
};
|
||||
|
||||
SmallVector<Rect, 0> dirtyRects;
|
||||
dirtyRects.reserve(16);
|
||||
|
||||
dirtyRects.emplace_back(0, 0, 2, 2);
|
||||
dirtyRects.emplace_back(1, 1, 3, 4);
|
||||
dirtyRects.emplace_back(2, 1, 4, 3);
|
||||
dirtyRects.emplace_back(0, 1, 3, 2);
|
||||
dirtyRects.emplace_back(3, 3, 4, 4);
|
||||
|
||||
OptimizeDirtyRects(dirtyRects);
|
||||
|
||||
std::sort(dirtyRects.begin(), dirtyRects.end(), rectComp);
|
||||
assert(dirtyRects.size() == 2);
|
||||
assert((dirtyRects[0] == Rect{ 0, 0, 2, 2 }));
|
||||
assert((dirtyRects[1] == Rect{ 1, 1, 4, 4 }));
|
||||
|
||||
return Ignore();
|
||||
}();
|
||||
#endif
|
||||
|
||||
GraphicsCaptureFrameSource::~GraphicsCaptureFrameSource() noexcept {
|
||||
if (_captureSession) {
|
||||
_StopCapture();
|
||||
|
|
@ -150,6 +356,10 @@ bool GraphicsCaptureFrameSource::Initialize(
|
|||
return false;
|
||||
}
|
||||
|
||||
// 从 Win11 24H2 开始支持
|
||||
_isDirtyRegionSupported = winrt::ApiInformation::IsPropertyPresent(
|
||||
winrt::name_of<winrt::GraphicsCaptureSession>(), L"DirtyRegionMode");
|
||||
|
||||
{
|
||||
RECT frameBounds;
|
||||
if (!CalcWindowCapturedFrameBounds(ScalingWindow::Get().SrcHandle(), frameBounds)) {
|
||||
|
|
@ -311,6 +521,7 @@ HRESULT GraphicsCaptureFrameSource::CheckForNewFrame(bool& isNewFrameAvailable)
|
|||
|
||||
if (_latestFrame) {
|
||||
_newFrame = std::move(_latestFrame);
|
||||
_newFrameDirtyRects = std::move(_latestFrameDirtyRects);
|
||||
_latestFrame = nullptr;
|
||||
} else {
|
||||
isNewFrameAvailable = (bool)_newFrame;
|
||||
|
|
@ -331,22 +542,23 @@ HRESULT GraphicsCaptureFrameSource::CheckForNewFrame(bool& isNewFrameAvailable)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
bool isDuplicate = false;
|
||||
SmallVector<Rect, 1> dirtyRects = {
|
||||
Rect{_frameBox.left, _frameBox.top, _frameBox.right, _frameBox.bottom}
|
||||
};
|
||||
hr = _duplicateFrameChecker->CheckFrame(_newFrameResource.get(), dirtyRects, isDuplicate);
|
||||
// 不支持脏矩形时检查整个捕获区域
|
||||
if (!_isDirtyRegionSupported) {
|
||||
_newFrameDirtyRects.emplace_back(_frameBox.left, _frameBox.top, _frameBox.right, _frameBox.bottom);
|
||||
}
|
||||
|
||||
hr = _duplicateFrameChecker->CheckFrame(_newFrameResource.get(), _newFrameDirtyRects);
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("DuplicateFrameChecker::CheckFrame 失败", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (isDuplicate) {
|
||||
isNewFrameAvailable = !_newFrameDirtyRects.empty();
|
||||
if (!isNewFrameAvailable) {
|
||||
_newFrame = nullptr;
|
||||
_newFrameResource = nullptr;
|
||||
}
|
||||
|
||||
isNewFrameAvailable = !isDuplicate;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
|
@ -894,23 +1106,48 @@ void GraphicsCaptureFrameSource::_Direct3D11CaptureFramePool_FrameArrived(
|
|||
const winrt::Direct3D11CaptureFramePool& pool,
|
||||
const winrt::IInspectable&
|
||||
) {
|
||||
winrt::Direct3D11CaptureFrame frame = pool.TryGetNextFrame();
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
winrt::Direct3D11CaptureFrame frame{ nullptr };
|
||||
SmallVector<Rect> dirtyRects;
|
||||
|
||||
// 取最新帧
|
||||
while (true) {
|
||||
if (winrt::Direct3D11CaptureFrame nextFrame = pool.TryGetNextFrame()) {
|
||||
frame = std::move(nextFrame);
|
||||
} else {
|
||||
winrt::Direct3D11CaptureFrame nextFrame = pool.TryGetNextFrame();
|
||||
if (!nextFrame) {
|
||||
break;
|
||||
}
|
||||
|
||||
frame = std::move(nextFrame);
|
||||
|
||||
if (_isDirtyRegionSupported) {
|
||||
for (const winrt::RectInt32& dirtyRect : frame.DirtyRegions()) {
|
||||
RECT clipped = {
|
||||
std::max(dirtyRect.X, (int)_frameBox.left),
|
||||
std::max(dirtyRect.Y, (int)_frameBox.top),
|
||||
std::min(dirtyRect.X + dirtyRect.Width, (int)_frameBox.right),
|
||||
std::min(dirtyRect.Y + dirtyRect.Height, (int)_frameBox.bottom),
|
||||
};
|
||||
if (clipped.right <= clipped.left || clipped.bottom <= clipped.top) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dirtyRects.emplace_back((uint32_t)clipped.left, (uint32_t)clipped.top,
|
||||
(uint32_t)clipped.right, (uint32_t)clipped.bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!frame || (_isDirtyRegionSupported && dirtyRects.empty())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dirtyRects.size() > 1) {
|
||||
OptimizeDirtyRects2(dirtyRects);
|
||||
}
|
||||
|
||||
{
|
||||
auto lk = _latestFrameLock.lock_exclusive();
|
||||
_latestFrame = std::move(frame);
|
||||
_latestFrameDirtyRects = std::move(dirtyRects);
|
||||
|
||||
#ifdef MP_DEBUG_INFO
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#include "SmallVector.h"
|
||||
#include <ShlObj.h>
|
||||
#include <winrt/Windows.Graphics.Capture.h>
|
||||
|
||||
|
|
@ -86,6 +87,7 @@ private:
|
|||
|
||||
std::unique_ptr<DuplicateFrameChecker> _duplicateFrameChecker;
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame _newFrame{ nullptr };
|
||||
SmallVector<Rect> _newFrameDirtyRects;
|
||||
winrt::com_ptr<ID3D12Resource> _newFrameResource;
|
||||
|
||||
struct _FrameCrossAdapterResourceSlot {
|
||||
|
|
@ -104,6 +106,7 @@ private:
|
|||
|
||||
wil::srwlock _latestFrameLock;
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame _latestFrame{ nullptr };
|
||||
SmallVector<Rect> _latestFrameDirtyRects;
|
||||
|
||||
std::atomic<DWORD> _producerThreadId;
|
||||
|
||||
|
|
@ -123,6 +126,7 @@ private:
|
|||
bool _isScRGB = false;
|
||||
bool _isSrcStyleChanged = false;
|
||||
bool _isRoundCornerDisabled = false;
|
||||
bool _isDirtyRegionSupported = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@
|
|||
<ClInclude Include="include\Event.h" />
|
||||
<ClInclude Include="OverlayDrawer.h" />
|
||||
<ClInclude Include="PresenterBase.h" />
|
||||
<ClInclude Include="RectHelper.h" />
|
||||
<ClInclude Include="Renderer.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="Renderer2.h" />
|
||||
|
|
|
|||
|
|
@ -143,9 +143,6 @@
|
|||
<ClInclude Include="GraphicsContext.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ColorInfo.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FrameProducer.h">
|
||||
<Filter>Render</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -161,6 +158,10 @@
|
|||
<ClInclude Include="CatumullRomEffectDrawer.h">
|
||||
<Filter>EffectDrawer</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DuplicateFrameChecker.h">
|
||||
<Filter>Capture</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RectHelper.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="ScalingRuntime.cpp" />
|
||||
|
|
@ -270,6 +271,9 @@
|
|||
<ClCompile Include="CatumullRomEffectDrawer.cpp">
|
||||
<Filter>EffectDrawer</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="DuplicateFrameChecker.cpp">
|
||||
<Filter>Capture</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<FxCompile Include="shaders\SimpleVS.hlsl">
|
||||
|
|
|
|||
33
src/Magpie.Core/RectHelper.h
Normal file
33
src/Magpie.Core/RectHelper.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
namespace Magpie {
|
||||
|
||||
struct RectHelper {
|
||||
static bool IsOverlap(const Rect& r1, const Rect& r2) noexcept {
|
||||
return r1.right > r2.left && r1.bottom > r2.top && r1.left < r2.right && r1.top < r2.bottom;
|
||||
}
|
||||
|
||||
static bool Contains(const Rect& r1, const Rect& r2) noexcept {
|
||||
return r1.left <= r2.left && r1.top <= r2.top && r1.right >= r2.right && r1.bottom >= r2.bottom;
|
||||
}
|
||||
|
||||
static bool Contains(const Rect& rect, Point p) noexcept {
|
||||
return p.x >= rect.left && p.x < rect.right && p.y >= rect.top && p.y < rect.bottom;
|
||||
}
|
||||
|
||||
static bool IsEmpty(const Rect& rect) noexcept {
|
||||
return rect.left == rect.right || rect.top == rect.bottom;
|
||||
}
|
||||
|
||||
static Rect Union(const Rect& r1, const Rect& r2) noexcept {
|
||||
return Rect{ std::min(r1.left, r2.left), std::min(r1.top, r2.top),
|
||||
std::max(r1.right, r2.right), std::max(r1.bottom, r2.bottom) };
|
||||
}
|
||||
|
||||
static uint32_t CalcArea(const Rect& rect) noexcept {
|
||||
assert(rect.right >= rect.left && rect.bottom >= rect.top);
|
||||
return (rect.right - rect.left) * (rect.bottom - rect.top);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -77,6 +77,15 @@ enum class ComponentState {
|
|||
struct Size {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
|
||||
bool operator==(const Size& other) const = default;
|
||||
};
|
||||
|
||||
struct Point {
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
|
||||
bool operator==(const Point& other) const = default;
|
||||
};
|
||||
|
||||
struct Rect {
|
||||
|
|
@ -84,16 +93,18 @@ struct Rect {
|
|||
uint32_t top;
|
||||
uint32_t right;
|
||||
uint32_t bottom;
|
||||
|
||||
bool operator==(const Rect& other) const = default;
|
||||
};
|
||||
|
||||
struct ColorInfo {
|
||||
bool operator==(const ColorInfo& other) const = default;
|
||||
|
||||
winrt::AdvancedColorKind kind = winrt::AdvancedColorKind::StandardDynamicRange;
|
||||
// HDR 模式下最大亮度缩放
|
||||
float maxLuminance = 1.0f;
|
||||
// HDR 模式下 SDR 内容亮度缩放
|
||||
float sdrWhiteLevel = 1.0f;
|
||||
|
||||
bool operator==(const ColorInfo& other) const = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue