[common, shortcutguide] move d2d files from common to scg (#7844)

Also remove d2d classes from common documentation
This commit is contained in:
Enrico Giordani
2020-11-05 10:38:46 +01:00
committed by GitHub
parent 1dc1531104
commit 50a8884c32
15 changed files with 39 additions and 54 deletions

View File

@@ -0,0 +1,117 @@
#include "pch.h"
#include "d2d_svg.h"
D2DSVG& D2DSVG::load(const std::wstring& filename, ID2D1DeviceContext5* d2d_dc)
{
svg = nullptr;
winrt::com_ptr<IStream> svg_stream;
winrt::check_hresult(SHCreateStreamOnFileEx(filename.c_str(),
STGM_READ,
FILE_ATTRIBUTE_NORMAL,
FALSE,
nullptr,
svg_stream.put()));
winrt::check_hresult(d2d_dc->CreateSvgDocument(
svg_stream.get(),
D2D1::SizeF(1, 1),
svg.put()));
winrt::com_ptr<ID2D1SvgElement> root;
svg->GetRoot(root.put());
float tmp;
winrt::check_hresult(root->GetAttributeValue(L"width", &tmp));
svg_width = (int)tmp;
winrt::check_hresult(root->GetAttributeValue(L"height", &tmp));
svg_height = (int)tmp;
return *this;
}
D2DSVG& D2DSVG::resize(int x, int y, int width, int height, float fill, float max_scale)
{
// Center
transform = D2D1::Matrix3x2F::Identity();
transform = transform * D2D1::Matrix3x2F::Translation((width - svg_width) / 2.0f, (height - svg_height) / 2.0f);
float h_scale = fill * height / svg_height;
float v_scale = fill * width / svg_width;
used_scale = std::min(h_scale, v_scale);
if (max_scale > 0)
{
used_scale = std::min(used_scale, max_scale);
}
transform = transform * D2D1::Matrix3x2F::Scale(used_scale, used_scale, D2D1::Point2F(width / 2.0f, height / 2.0f));
transform = transform * D2D1::Matrix3x2F::Translation((float)x, (float)y);
return *this;
}
D2DSVG& D2DSVG::recolor(uint32_t oldcolor, uint32_t newcolor)
{
auto new_color = D2D1::ColorF(newcolor & 0xFFFFFF, 1);
auto old_color = D2D1::ColorF(oldcolor & 0xFFFFFF, 1);
std::function<void(ID2D1SvgElement * element)> recurse = [&](ID2D1SvgElement* element) {
if (!element)
return;
if (element->IsAttributeSpecified(L"fill"))
{
D2D1_COLOR_F elem_fill;
winrt::com_ptr<ID2D1SvgPaint> paint;
element->GetAttributeValue(L"fill", paint.put());
paint->GetColor(&elem_fill);
if (elem_fill.r == old_color.r && elem_fill.g == old_color.g && elem_fill.b == old_color.b)
{
winrt::check_hresult(element->SetAttributeValue(L"fill", new_color));
}
}
winrt::com_ptr<ID2D1SvgElement> sub;
element->GetFirstChild(sub.put());
while (sub)
{
recurse(sub.get());
winrt::com_ptr<ID2D1SvgElement> next;
element->GetNextChild(sub.get(), next.put());
sub = next;
}
};
winrt::com_ptr<ID2D1SvgElement> root;
svg->GetRoot(root.put());
recurse(root.get());
return *this;
}
D2DSVG& D2DSVG::render(ID2D1DeviceContext5* d2d_dc)
{
D2D1_MATRIX_3X2_F current;
d2d_dc->GetTransform(&current);
d2d_dc->SetTransform(transform * current);
d2d_dc->DrawSvgDocument(svg.get());
d2d_dc->SetTransform(current);
return *this;
}
D2DSVG& D2DSVG::toggle_element(const wchar_t* id, bool visible)
{
winrt::com_ptr<ID2D1SvgElement> element;
if (svg->FindElementById(id, element.put()) != S_OK)
return *this;
if (!element)
return *this;
element->SetAttributeValue(L"display", visible ? D2D1_SVG_DISPLAY::D2D1_SVG_DISPLAY_INLINE : D2D1_SVG_DISPLAY::D2D1_SVG_DISPLAY_NONE);
return *this;
}
winrt::com_ptr<ID2D1SvgElement> D2DSVG::find_element(const std::wstring& id)
{
winrt::com_ptr<ID2D1SvgElement> element;
winrt::check_hresult(svg->FindElementById(id.c_str(), element.put()));
return element;
}
D2D1_RECT_F D2DSVG::rescale(D2D1_RECT_F rect)
{
D2D1_RECT_F result;
auto src = reinterpret_cast<D2D1_POINT_2F*>(&rect);
auto dst = reinterpret_cast<D2D1_POINT_2F*>(&result);
dst[0] = src[0] * transform;
dst[1] = src[1] * transform;
return result;
}

View File

@@ -0,0 +1,26 @@
#pragma once
#include <d2d1_3.h>
#include <d2d1_3helper.h>
#include <winrt/base.h>
#include <string>
class D2DSVG
{
public:
D2DSVG& load(const std::wstring& filename, ID2D1DeviceContext5* d2d_dc);
D2DSVG& resize(int x, int y, int width, int height, float fill, float max_scale = -1.0f);
D2DSVG& render(ID2D1DeviceContext5* d2d_dc);
D2DSVG& recolor(uint32_t oldcolor, uint32_t newcolor);
float get_scale() const { return used_scale; }
int width() const { return svg_width; }
int height() const { return svg_height; }
D2DSVG& toggle_element(const wchar_t* id, bool visible);
winrt::com_ptr<ID2D1SvgElement> find_element(const std::wstring& id);
D2D1_RECT_F rescale(D2D1_RECT_F rect);
protected:
float used_scale = 1.0f;
winrt::com_ptr<ID2D1SvgDocument> svg;
int svg_width = -1, svg_height = -1;
D2D1::Matrix3x2F transform;
};

View File

@@ -0,0 +1,54 @@
#include "pch.h"
#include "d2d_text.h"
D2DText::D2DText(float text_size, float scale)
{
winrt::check_hresult(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(factory), reinterpret_cast<IUnknown**>(factory.put_void())));
resize(text_size, scale);
winrt::check_hresult(format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER));
winrt::check_hresult(format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER));
}
D2DText& D2DText::resize(float text_size, float scale)
{
format = nullptr;
winrt::check_hresult(factory->CreateTextFormat(L"Segoe UI",
nullptr,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
text_size * scale,
L"en-us",
format.put()));
winrt::check_hresult(format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER));
return *this;
}
D2DText& D2DText::set_alignment_left()
{
winrt::check_hresult(format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING));
return *this;
}
D2DText& D2DText::set_alignment_center()
{
winrt::check_hresult(format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER));
return *this;
}
D2DText& D2DText::set_alignment_right()
{
winrt::check_hresult(format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING));
return *this;
}
void D2DText::write(ID2D1DeviceContext5* d2d_dc, D2D1_COLOR_F color, D2D1_RECT_F rect, std::wstring text)
{
winrt::com_ptr<ID2D1SolidColorBrush> brush;
d2d_dc->CreateSolidColorBrush(color, brush.put());
d2d_dc->DrawText(text.c_str(),
(UINT32)text.length(),
format.get(),
rect,
brush.get());
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include <winrt/base.h>
#include <dwrite.h>
class D2DText
{
public:
D2DText(float text_size = 15.0f, float scale = 1.0f);
D2DText& resize(float text_size, float scale);
D2DText& set_alignment_left();
D2DText& set_alignment_center();
D2DText& set_alignment_right();
void write(ID2D1DeviceContext5* d2d_dc, D2D1_COLOR_F color, D2D1_RECT_F rect, std::wstring text);
private:
winrt::com_ptr<IDWriteFactory> factory;
winrt::com_ptr<IDWriteTextFormat> format;
};

View File

@@ -0,0 +1,215 @@
#include "pch.h"
#include "d2d_window.h"
extern "C" IMAGE_DOS_HEADER __ImageBase;
D2DWindow::D2DWindow(std::optional<std::function<std::remove_pointer_t<WNDPROC>>> _pre_wnd_proc) :
pre_wnd_proc(std::move(_pre_wnd_proc))
{
static const WCHAR* class_name = L"PToyD2DPopup";
WNDCLASS wc = {};
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hInstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
wc.lpszClassName = class_name;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = d2d_window_proc;
RegisterClass(&wc);
hwnd = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOREDIRECTIONBITMAP | WS_EX_LAYERED,
wc.lpszClassName,
L"PToyD2DPopup",
WS_POPUP | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
nullptr,
nullptr,
wc.hInstance,
this);
WINRT_VERIFY(hwnd);
}
void D2DWindow::show(UINT x, UINT y, UINT width, UINT height)
{
if (!initialized)
{
base_init();
}
base_resize(width, height);
render_empty();
hidden = false;
on_show();
SetWindowPos(hwnd, HWND_TOPMOST, x, y, width, height, 0);
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
}
void D2DWindow::hide()
{
hidden = true;
ShowWindow(hwnd, SW_HIDE);
on_hide();
}
void D2DWindow::initialize()
{
base_init();
}
void D2DWindow::base_init()
{
std::unique_lock lock(mutex);
// D2D1Factory is independent from the device, no need to recreate it if we need to recreate the device.
if (!d2d_factory)
{
#ifdef _DEBUG
D2D1_FACTORY_OPTIONS options = { D2D1_DEBUG_LEVEL_INFORMATION };
#else
D2D1_FACTORY_OPTIONS options = {};
#endif
winrt::check_hresult(D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
__uuidof(d2d_factory),
&options,
d2d_factory.put_void()));
}
// For all other stuff - assign nullptr first to release the object, to reset the com_ptr.
d2d_dc = nullptr;
d2d_device = nullptr;
dxgi_factory = nullptr;
dxgi_device = nullptr;
d3d_device = nullptr;
winrt::check_hresult(D3D11CreateDevice(nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
nullptr,
0,
D3D11_SDK_VERSION,
d3d_device.put(),
nullptr,
nullptr));
winrt::check_hresult(d3d_device->QueryInterface(__uuidof(dxgi_device), dxgi_device.put_void()));
winrt::check_hresult(CreateDXGIFactory2(0, __uuidof(dxgi_factory), dxgi_factory.put_void()));
winrt::check_hresult(d2d_factory->CreateDevice(dxgi_device.get(), d2d_device.put()));
winrt::check_hresult(d2d_device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, d2d_dc.put()));
init();
initialized = true;
}
void D2DWindow::base_resize(UINT width, UINT height)
{
std::unique_lock lock(mutex);
if (!initialized)
{
return;
}
window_width = width;
window_height = height;
if (window_width == 0 || window_height == 0)
{
return;
}
DXGI_SWAP_CHAIN_DESC1 sc_description = {};
sc_description.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
sc_description.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sc_description.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
sc_description.BufferCount = 2;
sc_description.SampleDesc.Count = 1;
sc_description.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
sc_description.Width = window_width;
sc_description.Height = window_height;
dxgi_swap_chain = nullptr;
winrt::check_hresult(dxgi_factory->CreateSwapChainForComposition(dxgi_device.get(),
&sc_description,
nullptr,
dxgi_swap_chain.put()));
composition_device = nullptr;
winrt::check_hresult(DCompositionCreateDevice(dxgi_device.get(),
__uuidof(composition_device),
composition_device.put_void()));
composition_target = nullptr;
winrt::check_hresult(composition_device->CreateTargetForHwnd(hwnd, true, composition_target.put()));
composition_visual = nullptr;
winrt::check_hresult(composition_device->CreateVisual(composition_visual.put()));
winrt::check_hresult(composition_visual->SetContent(dxgi_swap_chain.get()));
winrt::check_hresult(composition_target->SetRoot(composition_visual.get()));
dxgi_surface = nullptr;
winrt::check_hresult(dxgi_swap_chain->GetBuffer(0, __uuidof(dxgi_surface), dxgi_surface.put_void()));
D2D1_BITMAP_PROPERTIES1 properties = {};
properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
properties.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW;
d2d_bitmap = nullptr;
winrt::check_hresult(d2d_dc->CreateBitmapFromDxgiSurface(dxgi_surface.get(),
properties,
d2d_bitmap.put()));
d2d_dc->SetTarget(d2d_bitmap.get());
resize();
}
void D2DWindow::base_render()
{
std::unique_lock lock(mutex);
if (!initialized || !d2d_dc || !d2d_bitmap)
return;
d2d_dc->BeginDraw();
render(d2d_dc.get());
winrt::check_hresult(d2d_dc->EndDraw());
winrt::check_hresult(dxgi_swap_chain->Present(1, 0));
winrt::check_hresult(composition_device->Commit());
}
void D2DWindow::render_empty()
{
std::unique_lock lock(mutex);
if (!initialized || !d2d_dc || !d2d_bitmap)
return;
d2d_dc->BeginDraw();
d2d_dc->Clear();
winrt::check_hresult(d2d_dc->EndDraw());
winrt::check_hresult(dxgi_swap_chain->Present(1, 0));
winrt::check_hresult(composition_device->Commit());
}
D2DWindow::~D2DWindow()
{
ShowWindow(hwnd, SW_HIDE);
DestroyWindow(hwnd);
}
D2DWindow* D2DWindow::this_from_hwnd(HWND window)
{
return reinterpret_cast<D2DWindow*>(GetWindowLongPtr(window, GWLP_USERDATA));
}
LRESULT __stdcall D2DWindow::d2d_window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam)
{
auto self = this_from_hwnd(window);
if (self && self->pre_wnd_proc.has_value())
{
(*self->pre_wnd_proc)(window, message, wparam, lparam);
}
switch (message)
{
case WM_NCCREATE:
{
auto create_struct = reinterpret_cast<CREATESTRUCT*>(lparam);
SetWindowLongPtr(window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(create_struct->lpCreateParams));
return TRUE;
}
case WM_MOVE:
case WM_SIZE:
self->base_resize((unsigned)lparam & 0xFFFF, (unsigned)lparam >> 16);
[[fallthrough]];
case WM_PAINT:
self->base_render();
return 0;
default:
return DefWindowProc(window, message, wparam, lparam);
}
}

View File

@@ -0,0 +1,67 @@
#pragma once
#include <winrt/base.h>
#include <Windows.h>
#include <dxgi1_3.h>
#include <d3d11_2.h>
#include <d2d1_3.h>
#include <d2d1_3helper.h>
#include <d2d1helper.h>
#include <dcomp.h>
#include <dwmapi.h>
#include <string>
#include "d2d_svg.h"
#include <functional>
#include <optional>
class D2DWindow
{
public:
D2DWindow(std::optional<std::function<std::remove_pointer_t<WNDPROC>>> pre_wnd_proc = std::nullopt);
void show(UINT x, UINT y, UINT width, UINT height);
void hide();
void initialize();
virtual ~D2DWindow();
protected:
// Implement this:
// Initialization - called when D2D device needs to be created.
// When called all D2DWindow members will be initialized, including d2d_dc
virtual void init() = 0;
// resize - when called, window_width and window_height will have current window size
virtual void resize() = 0;
// render - called on WM_PAIT, BeginPaint/EndPaint is handled by D2DWindow
virtual void render(ID2D1DeviceContext5* d2d_dc) = 0;
// on_show, on_hide - called when the window is about to be shown or about to be hidden
virtual void on_show() = 0;
virtual void on_hide() = 0;
static LRESULT __stdcall d2d_window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam);
static D2DWindow* this_from_hwnd(HWND window);
void base_init();
void base_resize(UINT width, UINT height);
void base_render();
void render_empty();
std::recursive_mutex mutex;
bool hidden = true;
bool initialized = false;
HWND hwnd;
UINT window_width, window_height;
winrt::com_ptr<ID3D11Device> d3d_device;
winrt::com_ptr<IDXGIDevice> dxgi_device;
winrt::com_ptr<IDXGIFactory2> dxgi_factory;
winrt::com_ptr<IDXGISwapChain1> dxgi_swap_chain;
winrt::com_ptr<IDCompositionDevice> composition_device;
winrt::com_ptr<IDCompositionTarget> composition_target;
winrt::com_ptr<IDCompositionVisual> composition_visual;
winrt::com_ptr<IDXGISurface2> dxgi_surface;
winrt::com_ptr<ID2D1Bitmap1> d2d_bitmap;
winrt::com_ptr<ID2D1Factory6> d2d_factory;
winrt::com_ptr<ID2D1Device5> d2d_device;
winrt::com_ptr<ID2D1DeviceContext5> d2d_dc;
std::optional<std::function<std::remove_pointer_t<WNDPROC>>> pre_wnd_proc;
};

View File

@@ -71,7 +71,7 @@ ScaleResult D2DOverlaySVG::get_thumbnail_rect_and_scale(int x_offset, int y_offs
}
float scale_h = fill * thumbnail_scaled_rect_width / window_cx;
float scale_v = fill * thumbnail_scaled_rect_heigh / window_cy;
float use_scale = min(scale_h, scale_v);
float use_scale = std::min(scale_h, scale_v);
RECT thumb_rect;
thumb_rect.left = thumbnail_scaled_rect.left + (int)(thumbnail_scaled_rect_width - use_scale * window_cx) / 2 + x_offset;
thumb_rect.right = thumbnail_scaled_rect.right - (int)(thumbnail_scaled_rect_width - use_scale * window_cx) / 2 + x_offset;
@@ -254,10 +254,10 @@ void D2DOverlayWindow::show(HWND active_window, bool snappable)
total_screen = ScreenSize(monitors[0].rect);
for (auto& monitor : monitors)
{
total_screen.rect.left = min(total_screen.rect.left, monitor.rect.left);
total_screen.rect.top = min(total_screen.rect.top, monitor.rect.top);
total_screen.rect.right = max(total_screen.rect.right, monitor.rect.right);
total_screen.rect.bottom = max(total_screen.rect.bottom, monitor.rect.bottom);
total_screen.rect.left = std::min(total_screen.rect.left, monitor.rect.left);
total_screen.rect.top = std::min(total_screen.rect.top, monitor.rect.top);
total_screen.rect.right = std::max(total_screen.rect.right, monitor.rect.right);
total_screen.rect.bottom = std::max(total_screen.rect.bottom, monitor.rect.bottom);
}
// make sure top-right corner of all the monitor rects is (0,0)
monitor_dx = -total_screen.left();
@@ -693,10 +693,10 @@ void D2DOverlayWindow::render(ID2D1DeviceContext5* d2d_dc)
auto total_monitor_with_screen = total_screen;
if (thumb_window)
{
total_monitor_with_screen.rect.left = min(total_monitor_with_screen.rect.left, thumb_window->left + monitor_dx);
total_monitor_with_screen.rect.top = min(total_monitor_with_screen.rect.top, thumb_window->top + monitor_dy);
total_monitor_with_screen.rect.right = max(total_monitor_with_screen.rect.right, thumb_window->right + monitor_dx);
total_monitor_with_screen.rect.bottom = max(total_monitor_with_screen.rect.bottom, thumb_window->bottom + monitor_dy);
total_monitor_with_screen.rect.left = std::min(total_monitor_with_screen.rect.left, thumb_window->left + monitor_dx);
total_monitor_with_screen.rect.top = std::min(total_monitor_with_screen.rect.top, thumb_window->top + monitor_dy);
total_monitor_with_screen.rect.right = std::max(total_monitor_with_screen.rect.right, thumb_window->right + monitor_dx);
total_monitor_with_screen.rect.bottom = std::max(total_monitor_with_screen.rect.bottom, thumb_window->bottom + monitor_dy);
}
// Only allow the new rect being slight bigger.
if (total_monitor_with_screen.width() - total_screen.width() > (thumb_window->right - thumb_window->left) / 2 ||

View File

@@ -1,7 +1,7 @@
#pragma once
#include "common/d2d_svg.h"
#include "common/d2d_window.h"
#include "common/d2d_text.h"
#include "d2d_svg.h"
#include "d2d_window.h"
#include "d2d_text.h"
#include "common/monitors.h"
#include "common/animation.h"
#include "common/windows_colors.h"

View File

@@ -1,4 +1,5 @@
#pragma once
#define NOMINMAX
#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>

View File

@@ -105,6 +105,9 @@
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="d2d_svg.h" />
<ClInclude Include="d2d_text.h" />
<ClInclude Include="d2d_window.h" />
<ClInclude Include="overlay_window.h" />
<ClInclude Include="keyboard_state.h" />
<ClInclude Include="Generated Files/resource.h" />
@@ -116,6 +119,9 @@
<ClInclude Include="trace.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="d2d_svg.cpp" />
<ClCompile Include="d2d_text.cpp" />
<ClCompile Include="d2d_window.cpp" />
<ClCompile Include="overlay_window.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="keyboard_state.cpp" />

View File

@@ -18,6 +18,15 @@
<ClCompile Include="trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d2d_svg.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d2d_text.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="d2d_window.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
@@ -39,6 +48,16 @@
<ClInclude Include="Generated Files/resource.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="ShortcutGuideConstants.h" />
<ClInclude Include="d2d_svg.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d2d_text.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="d2d_window.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Header Files">