{"id":307513,"date":"2020-07-26T15:00:44","date_gmt":"2020-07-26T15:00:44","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=307513"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=307513","title":{"rendered":"Direct2D \u0432 Rainmeter"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/512510\/\">\u0414\u043e\u0431\u0440\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u0443\u0442\u043e\u043a, \u0425\u0430\u0431\u0440. \u0412\u0441\u0435\u0433\u0434\u0430 \u0445\u043e\u0442\u0435\u043b \u0441\u0432\u043e\u0431\u043e\u0434\u044b \u0432 Rainmeter&#8217;\u0435. \u041e\u0434\u043d\u043e\u0442\u0438\u043f\u043d\u044b\u0435 \u0441\u043a\u0438\u043d\u044b, \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u044b \u2014 \u043d\u0435 \u0442\u043e.<\/p>\n<p>  \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u043e\u043b\u043d\u0443\u044e \u0432\u043b\u0430\u0441\u0442\u044c \u043d\u0430\u0434 Rainmeter&#8217;\u043e\u043c.<\/p>\n<p>  \u0417\u0430\u043b\u0435\u0437\u0435\u043c \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438 Rainmeter&#8217;\u0430 \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u0436\u0435 \u0442\u0430\u043c \u0432\u0441\u0451 \u0442\u0430\u043a \u043a\u0440\u0430\u0441\u0438\u0432\u043e \u0440\u0438\u0441\u0443\u0435\u0442\u0441\u044f\u2026<\/p>\n<div class=\"oembed\"><a href=\"https:\/\/github.com\/rainmeter\/rainmeter\/\">https:\/\/github.com\/rainmeter\/rainmeter\/<\/a><\/div>\n<p>  \u041d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0444\u0430\u0439\u043b Library\/Meter.cpp<br \/>  <a name=\"habracut\"><\/a>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"cpp\">bool Meter::Draw(Gfx::Canvas& canvas) { \tif (IsHidden()) return false;  \tcanvas.SetAntiAliasing(m_AntiAlias);  \tif (m_SolidColor.a != 0.0f || m_SolidColor2.a != 0.0f) \t{ \t\tconst FLOAT x = (FLOAT)GetX(); \t\tconst FLOAT y = (FLOAT)GetY();  \t\tconst D2D1_RECT_F r = D2D1::RectF(x, y, x + (FLOAT)m_W, y + (FLOAT)m_H);  \t\tif (m_SolidColor.r == m_SolidColor2.r && m_SolidColor.g == m_SolidColor2.g &&  \t\t\tm_SolidColor.b == m_SolidColor2.b && m_SolidColor.a == m_SolidColor2.a) \t\t{ \t\t\tcanvas.FillRectangle(r, m_SolidColor); \t\t} \t\telse \t\t{ \t\t\tcanvas.FillGradientRectangle(r, m_SolidColor, m_SolidColor2, (FLOAT)m_SolidAngle); \t\t} \t}  \tif (m_SolidBevel != BEVELTYPE_NONE) \t{ \t\tD2D1_COLOR_F lightColor = D2D1::ColorF(D2D1::ColorF::White); \t\tD2D1_COLOR_F darkColor = D2D1::ColorF(D2D1::ColorF::Black); \t\t \t\tif (m_SolidBevel == BEVELTYPE_DOWN) \t\t{ \t\t\tlightColor = D2D1::ColorF(D2D1::ColorF::Black); \t\t\tdarkColor = D2D1::ColorF(D2D1::ColorF::White); \t\t}  \t\t\/\/ The bevel is drawn outside the meter \t\tconst FLOAT x = (FLOAT)GetX(); \t\tconst FLOAT y = (FLOAT)GetY(); \t\tconst D2D1_RECT_F rect = D2D1::RectF(x - 2.0f, y - 2.0f, x + (FLOAT)m_W + 2.0f, y + (FLOAT)m_H + 2.0f); \t\tDrawBevel(canvas, rect, lightColor, darkColor); \t}  \treturn true; } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0443\u0437\u043d\u0430\u0435\u043c \u0447\u0442\u043e \u0436\u0435 \u0442\u0430\u043a\u043e\u0435 \u044d\u0442\u043e\u0442 Canvas<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Common\/Gfx\/Canvas.cpp<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"cpp\">\/* Copyright (C) 2013 Rainmeter Project Developers  *  * This Source Code Form is subject to the terms of the GNU General Public  * License; either version 2 of the License, or (at your option) any later  * version. If a copy of the GPL was not distributed with this file, You can  * obtain one at &lt;https:\/\/www.gnu.org\/licenses\/gpl-2.0.html&gt;. *\/  #include &quot;StdAfx.h&quot; #include &quot;Canvas.h&quot; #include &quot;TextFormatD2D.h&quot; #include &quot;D2DBitmap.h&quot; #include &quot;RenderTexture.h&quot; #include &quot;Util\/D2DUtil.h&quot; #include &quot;Util\/DWriteFontCollectionLoader.h&quot; #include &quot;..\/..\/Library\/Util.h&quot; #include &quot;..\/..\/Library\/Logger.h&quot;  namespace Gfx {  UINT Canvas::c_Instances = 0; D3D_FEATURE_LEVEL Canvas::c_FeatureLevel; Microsoft::WRL::ComPtr&lt;ID3D11Device&gt; Canvas::c_D3DDevice; Microsoft::WRL::ComPtr&lt;ID3D11DeviceContext&gt; Canvas::c_D3DContext; Microsoft::WRL::ComPtr&lt;ID2D1Device&gt; Canvas::c_D2DDevice; Microsoft::WRL::ComPtr&lt;IDXGIDevice1&gt; Canvas::c_DxgiDevice; Microsoft::WRL::ComPtr&lt;ID2D1Factory1&gt; Canvas::c_D2DFactory; Microsoft::WRL::ComPtr&lt;IDWriteFactory1&gt; Canvas::c_DWFactory; Microsoft::WRL::ComPtr&lt;IWICImagingFactory&gt; Canvas::c_WICFactory;  Canvas::Canvas() : \tm_W(0), \tm_H(0), \tm_MaxBitmapSize(0U), \tm_IsDrawing(false), \tm_EnableDrawAfterGdi(false), \tm_TextAntiAliasing(false), \tm_CanUseAxisAlignClip(true) { \tInitialize(true); }  Canvas::~Canvas() { \tFinalize(); }  bool Canvas::LogComError(HRESULT hr) { \t_com_error err(hr); \tLogErrorF(L&quot;Error 0x%08x: %s&quot;, hr, err.ErrorMessage()); \treturn false; }  bool Canvas::Initialize(bool hardwareAccelerated) { \t++c_Instances; \tif (c_Instances == 1U) \t{ \t\t\/\/ Required for Direct2D interopability. \t\tUINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;  #ifdef _DEBUG \t\tcreationFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif  \t\tauto tryCreateContext = [&](D3D_DRIVER_TYPE driverType, \t\t\tconst D3D_FEATURE_LEVEL* levels, UINT numLevels) \t\t{ \t\t\treturn D3D11CreateDevice( \t\t\t\tnullptr, \t\t\t\tdriverType, \t\t\t\tnullptr, \t\t\t\tcreationFlags, \t\t\t\tlevels, \t\t\t\tnumLevels, \t\t\t\tD3D11_SDK_VERSION, \t\t\t\tc_D3DDevice.GetAddressOf(), \t\t\t\t&c_FeatureLevel, \t\t\t\tc_D3DContext.GetAddressOf()); \t\t};  \t\t\/\/ D3D selects the best feature level automatically and sets it \t\t\/\/ to |c_FeatureLevel|. First, we try to use the hardware driver \t\t\/\/ and if that fails, we try the WARP rasterizer for cases \t\t\/\/ where there is no graphics card or other failures. \t\tconst D3D_FEATURE_LEVEL levels[] =  \t\t{ \t\t\tD3D_FEATURE_LEVEL_11_1, \t\t\tD3D_FEATURE_LEVEL_11_0, \t\t\tD3D_FEATURE_LEVEL_10_1, \t\t\tD3D_FEATURE_LEVEL_10_0, \t\t\tD3D_FEATURE_LEVEL_9_3, \t\t\tD3D_FEATURE_LEVEL_9_2, \t\t\tD3D_FEATURE_LEVEL_9_1 \t\t};  \t\tHRESULT hr = E_FAIL; \t\tif (hardwareAccelerated) \t\t{ \t\t\thr = tryCreateContext(D3D_DRIVER_TYPE_HARDWARE, levels, _countof(levels)); \t\t\tif (hr == E_INVALIDARG) \t\t\t{ \t\t\t\thr = tryCreateContext(D3D_DRIVER_TYPE_HARDWARE, &levels[1], _countof(levels) - 1); \t\t\t} \t\t}  \t\tif (FAILED(hr)) \t\t{ \t\t\thr = tryCreateContext(D3D_DRIVER_TYPE_WARP, nullptr, 0U); \t\t\tif (FAILED(hr)) return false; \t\t}  \t\thr = c_D3DDevice.As(&c_DxgiDevice); \t\tif (FAILED(hr)) return false;  \t\tD2D1_FACTORY_OPTIONS fo = {}; #ifdef _DEBUG \t\tfo.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; #endif  \t\thr = D2D1CreateFactory( \t\t\tD2D1_FACTORY_TYPE_SINGLE_THREADED, \t\t\tfo, \t\t\tc_D2DFactory.GetAddressOf()); \t\tif (FAILED(hr)) return false;  \t\thr = c_D2DFactory-&gt;CreateDevice( \t\t\tc_DxgiDevice.Get(), \t\t\tc_D2DDevice.GetAddressOf()); \t\tif (FAILED(hr)) return false;  \t\thr = CoCreateInstance( \t\t\tCLSID_WICImagingFactory, \t\t\tnullptr, \t\t\tCLSCTX_INPROC_SERVER, \t\t\tIID_IWICImagingFactory, \t\t\t(LPVOID*)c_WICFactory.GetAddressOf()); \t\tif (FAILED(hr)) return false;  \t\thr = DWriteCreateFactory( \t\t\tDWRITE_FACTORY_TYPE_SHARED, \t\t\t__uuidof(c_DWFactory), \t\t\t(IUnknown**)c_DWFactory.GetAddressOf()); \t\tif (FAILED(hr)) return false;  \t\thr = c_DWFactory-&gt;RegisterFontCollectionLoader(Util::DWriteFontCollectionLoader::GetInstance()); \t\tif (FAILED(hr)) return false; \t}  \treturn true; }  void Canvas::Finalize() { \t--c_Instances; \tif (c_Instances == 0U) \t{ \t\tc_D3DDevice.Reset(); \t\tc_D3DContext.Reset(); \t\tc_D2DDevice.Reset(); \t\tc_DxgiDevice.Reset(); \t\tc_D2DFactory.Reset(); \t\tc_WICFactory.Reset();  \t\tif (c_DWFactory) \t\t{ \t\t\tc_DWFactory-&gt;UnregisterFontCollectionLoader(Util::DWriteFontCollectionLoader::GetInstance()); \t\t\tc_DWFactory.Reset(); \t\t} \t} }  bool Canvas::InitializeRenderTarget(HWND hwnd) { \tDXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 }; \tswapChainDesc.Width = 1U; \tswapChainDesc.Height = 1U; \tswapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; \tswapChainDesc.Stereo = false; \tswapChainDesc.SampleDesc.Count = 1U; \tswapChainDesc.SampleDesc.Quality = 0U; \tswapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; \tswapChainDesc.BufferCount = 2U; \tswapChainDesc.Scaling = DXGI_SCALING_STRETCH; \tswapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; \tswapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE; \tswapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;  \tMicrosoft::WRL::ComPtr&lt;IDXGIAdapter&gt; dxgiAdapter; \tHRESULT hr = c_DxgiDevice-&gt;GetAdapter(dxgiAdapter.GetAddressOf()); \tif (FAILED(hr)) return LogComError(hr);  \t\/\/ Ensure that DXGI does not queue more than one frame at a time. \thr = c_DxgiDevice-&gt;SetMaximumFrameLatency(1U); \tif (FAILED(hr)) return LogComError(hr);  \tMicrosoft::WRL::ComPtr&lt;IDXGIFactory2&gt; dxgiFactory; \thr = dxgiAdapter-&gt;GetParent(IID_PPV_ARGS(dxgiFactory.GetAddressOf())); \tif (FAILED(hr)) return LogComError(hr);  \thr = dxgiFactory-&gt;CreateSwapChainForHwnd( \t\tc_DxgiDevice.Get(), \t\thwnd, \t\t&swapChainDesc, \t\tnullptr, \t\tnullptr, \t\tm_SwapChain.ReleaseAndGetAddressOf()); \tif (FAILED(hr)) return LogComError(hr);  \thr = CreateRenderTarget(); \tif (FAILED(hr)) return LogComError(hr);  \treturn CreateTargetBitmap(0U, 0U); }  void Canvas::Resize(int w, int h) { \t\/\/ Truncate the size of the skin if it's too big. \tif (w &gt; (int)m_MaxBitmapSize) w = (int)m_MaxBitmapSize; \tif (h &gt; (int)m_MaxBitmapSize) h = (int)m_MaxBitmapSize;  \tm_W = w; \tm_H = h;  \t\/\/ Check if target, targetbitmap, backbuffer, swap chain are valid?  \t\/\/ Unmap all resources tied to the swap chain. \tm_Target-&gt;SetTarget(nullptr); \tm_TargetBitmap.Reset(); \tm_BackBuffer.Reset();  \t\/\/ Resize swap chain. \tHRESULT hr = m_SwapChain-&gt;ResizeBuffers( \t\t0U, \t\t(UINT)w, \t\t(UINT)h, \t\tDXGI_FORMAT_B8G8R8A8_UNORM, \t\tDXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE); \tif (FAILED(hr)) return;  \tCreateTargetBitmap((UINT32)w, (UINT32)h); }  bool Canvas::BeginDraw() { \tif (!m_Target) \t{ \t\tHRESULT hr = CreateRenderTarget(); \t\tif (FAILED(hr)) \t\t{ \t\t\tm_IsDrawing = false; \t\t\treturn false; \t\t}  \t\t\/\/ Recreate target bitmap \t\tResize(m_W, m_H); \t}  \tm_Target-&gt;BeginDraw(); \tm_IsDrawing = true; \treturn true; }  void Canvas::EndDraw() { \tHRESULT hr = m_Target-&gt;EndDraw(); \tif (FAILED(hr)) \t{ \t\tm_Target.Reset(); \t}  \tm_IsDrawing = false; }  HDC Canvas::GetDC() { \tif (m_IsDrawing) \t{ \t\tm_EnableDrawAfterGdi = true; \t\tm_IsDrawing = false; \t\tEndDraw(); \t}  \tHDC hdc; \tHRESULT hr = m_BackBuffer-&gt;GetDC(FALSE, &hdc); \tif (FAILED(hr)) return nullptr;  \treturn hdc; }  void Canvas::ReleaseDC() { \tm_BackBuffer-&gt;ReleaseDC(nullptr);  \tif (m_EnableDrawAfterGdi) \t{ \t\tm_EnableDrawAfterGdi = false; \t\tm_IsDrawing = true; \t\tBeginDraw(); \t} }  bool Canvas::IsTransparentPixel(int x, int y) { \tif (!(x &gt;= 0 && y &gt;= 0 && x &lt; m_W && y &lt; m_H)) return false;  \tauto pixel = GetPixel(GetDC(), x, y); \tReleaseDC();  \treturn (pixel & 0xFF000000) == 0; }  void Canvas::GetTransform(D2D1_MATRIX_3X2_F* matrix) { \tif (m_Target) \t{ \t\tm_Target-&gt;GetTransform(matrix); \t} }  void Canvas::SetTransform(const D2D1_MATRIX_3X2_F& matrix) { \tm_Target-&gt;SetTransform(matrix);  \tm_CanUseAxisAlignClip = \t\t(matrix.m11 ==  1.0f && matrix.m12 ==  0.0f && matrix.m21 ==  0.0f && matrix.m22 ==  1.0f) ||\t\/\/ Angle: 0 \t\t(matrix.m11 ==  0.0f && matrix.m12 ==  1.0f && matrix.m21 == -1.0f && matrix.m22 ==  0.0f) ||\t\/\/ Angle: 90 \t\t(matrix.m11 == -1.0f && matrix.m12 ==  0.0f && matrix.m21 ==  0.0f && matrix.m22 == -1.0f) ||\t\/\/ Angle: 180 \t\t(matrix.m11 ==  0.0f && matrix.m12 == -1.0f && matrix.m21 ==  1.0f && matrix.m22 ==  0.0f);\t\t\/\/ Angle: 270 }  void Canvas::ResetTransform() { \tm_Target-&gt;SetTransform(D2D1::Matrix3x2F::Identity()); }  bool Canvas::SetTarget(RenderTexture* texture) { \tauto bitmap = texture-&gt;GetBitmap(); \tif (bitmap-&gt;m_Segments.size() == 0) return false;  \tauto image = bitmap-&gt;m_Segments[0].GetBitmap(); \tm_Target-&gt;SetTarget(image); \treturn true; }  void Canvas::ResetTarget() { \tm_Target-&gt;SetTarget(m_TargetBitmap.Get()); } void Canvas::SetAntiAliasing(bool enable) { \tm_Target-&gt;SetAntialiasMode(enable ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED); }  void Canvas::SetTextAntiAliasing(bool enable) { \t\/\/ TODO: Add support for D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE? \tm_TextAntiAliasing = enable; \tm_Target-&gt;SetTextAntialiasMode(enable ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : D2D1_TEXT_ANTIALIAS_MODE_ALIASED); }  void Canvas::Clear(const D2D1_COLOR_F& color) { \tif (!m_Target) return;  \tm_Target-&gt;Clear(color); } void Canvas::DrawTextW(const std::wstring& srcStr, const TextFormat& format, const D2D1_RECT_F& rect, \tconst D2D1_COLOR_F& color, bool applyInlineFormatting) { \tMicrosoft::WRL::ComPtr&lt;ID2D1SolidColorBrush&gt; solidBrush; \tHRESULT hr = m_Target-&gt;CreateSolidColorBrush(color, solidBrush.GetAddressOf()); \tif (FAILED(hr)) return;  \tTextFormatD2D& formatD2D = (TextFormatD2D&)format;  \tstatic std::wstring str; \tstr = srcStr; \tformatD2D.ApplyInlineCase(str);  \tif (!formatD2D.CreateLayout( \t\tm_Target.Get(), \t\tstr, \t\trect.right - rect.left, \t\trect.bottom - rect.top, \t\t!m_AccurateText && m_TextAntiAliasing)) return;  \tD2D1_POINT_2F drawPosition; \tdrawPosition.x = [&]() \t{ \t\tif (!m_AccurateText) \t\t{ \t\t\tconst float xOffset = formatD2D.m_TextFormat-&gt;GetFontSize() \/ 6.0f; \t\t\tswitch (formatD2D.GetHorizontalAlignment()) \t\t\t{ \t\t\tcase HorizontalAlignment::Left: return rect.left + xOffset; \t\t\tcase HorizontalAlignment::Right: return rect.left - xOffset; \t\t\t} \t\t}  \t\treturn rect.left; \t} ();  \tdrawPosition.y = [&]() \t{ \t\t\/\/ GDI+ compatibility. \t\tfloat yPos = rect.top - formatD2D.m_LineGap; \t\tswitch (formatD2D.GetVerticalAlignment()) \t\t{ \t\tcase VerticalAlignment::Bottom: yPos -= formatD2D.m_ExtraHeight; break; \t\tcase VerticalAlignment::Center: yPos -= formatD2D.m_ExtraHeight \/ 2.0f; break; \t\t}  \t\treturn yPos; \t} ();  \tif (formatD2D.m_Trimming) \t{ \t\tD2D1_RECT_F clipRect = rect;  \t\tif (m_CanUseAxisAlignClip) \t\t{ \t\t\tm_Target-&gt;PushAxisAlignedClip(clipRect, D2D1_ANTIALIAS_MODE_ALIASED); \t\t} \t\telse \t\t{ \t\t\tconst D2D1_LAYER_PARAMETERS1 layerParams = \t\t\t\tD2D1::LayerParameters1(clipRect, nullptr, D2D1_ANTIALIAS_MODE_ALIASED); \t\t\tm_Target-&gt;PushLayer(layerParams, nullptr); \t\t} \t}  \t\/\/ When different &quot;effects&quot; are used with inline coloring options, we need to \t\/\/ remove the previous inline coloring, then reapply them (if needed) - instead \t\/\/ of destroying\/recreating the text layout. \tUINT32 strLen = (UINT32)str.length(); \tformatD2D.ResetInlineColoring(solidBrush.Get(), strLen); \tif (applyInlineFormatting) \t{ \t\tformatD2D.ApplyInlineColoring(m_Target.Get(), &drawPosition);  \t\t\/\/ Draw any 'shadow' effects \t\tformatD2D.ApplyInlineShadow(m_Target.Get(), solidBrush.Get(), strLen, drawPosition); \t}  \tm_Target-&gt;DrawTextLayout(drawPosition, formatD2D.m_TextLayout.Get(), solidBrush.Get());  \tif (applyInlineFormatting) \t{ \t\t\/\/ Inline gradients require the drawing position, so in case that position \t\t\/\/ changes, we need a way to reset it after drawing time so on the next \t\t\/\/ iteration it will know the correct position. \t\tformatD2D.ResetGradientPosition(&drawPosition); \t}  \tif (formatD2D.m_Trimming) \t{ \t\tif (m_CanUseAxisAlignClip) \t\t{ \t\t\tm_Target-&gt;PopAxisAlignedClip(); \t\t} \t\telse \t\t{ \t\t\tm_Target-&gt;PopLayer(); \t\t} \t} } bool Canvas::MeasureTextW(const std::wstring& str, const TextFormat& format, D2D1_SIZE_F& size) { \tTextFormatD2D& formatD2D = (TextFormatD2D&)format;  \tstatic std::wstring formatStr; \tformatStr = str; \tformatD2D.ApplyInlineCase(formatStr);  \tconst DWRITE_TEXT_METRICS metrics = formatD2D.GetMetrics(formatStr, !m_AccurateText); \tsize.width = metrics.width; \tsize.height = metrics.height; \treturn true; }  bool Canvas::MeasureTextLinesW(const std::wstring& str, const TextFormat& format, D2D1_SIZE_F& size, UINT32& lines) { \tTextFormatD2D& formatD2D = (TextFormatD2D&)format; \tformatD2D.m_TextFormat-&gt;SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);  \tstatic std::wstring formatStr; \tformatStr = str; \tformatD2D.ApplyInlineCase(formatStr);  \tconst DWRITE_TEXT_METRICS metrics = formatD2D.GetMetrics(formatStr, !m_AccurateText, size.width); \tsize.width = metrics.width; \tsize.height = metrics.height; \tlines = metrics.lineCount;  \tif (size.height &gt; 0.0f) \t{ \t\t\/\/ GDI+ draws multi-line text even though the last line may be clipped slightly at the \t\t\/\/ bottom. This is a workaround to emulate that behaviour. \t\tsize.height += 1.0f; \t} \telse \t{ \t\t\/\/ GDI+ compatibility: Zero height text has no visible lines. \t\tlines = 0U; \t} \treturn true; }  void Canvas::DrawBitmap(const D2DBitmap* bitmap, const D2D1_RECT_F& dstRect, const D2D1_RECT_F& srcRect) { \tauto& segments = bitmap-&gt;m_Segments; \tfor (auto seg : segments) \t{ \t\tconst auto rSeg = seg.GetRect(); \t\tD2D1_RECT_F rSrc = (rSeg.left &lt; rSeg.right && rSeg.top &lt; rSeg.bottom) ? \t\t\tD2D1::RectF( \t\t\t\tmax(rSeg.left, srcRect.left), \t\t\t\tmax(rSeg.top, srcRect.top), \t\t\t\tmin(rSeg.right + rSeg.left, srcRect.right), \t\t\t\tmin(rSeg.bottom + rSeg.top, srcRect.bottom)) : \t\t\tD2D1::RectF(); \t\tif (rSrc.left == rSrc.right || rSrc.top == rSrc.bottom) continue;  \t\tconst D2D1_RECT_F rDst = D2D1::RectF( \t\t\t(rSrc.left   - srcRect.left) \/ (srcRect.right  - srcRect.left) * (dstRect.right  - dstRect.left) + dstRect.left, \t\t\t(rSrc.top    - srcRect.top)  \/ (srcRect.bottom - srcRect.top)  * (dstRect.bottom - dstRect.top)  + dstRect.top, \t\t\t(rSrc.right  - srcRect.left) \/ (srcRect.right  - srcRect.left) * (dstRect.right  - dstRect.left) + dstRect.left, \t\t\t(rSrc.bottom - srcRect.top)  \/ (srcRect.bottom - srcRect.top)  * (dstRect.bottom - dstRect.top)  + dstRect.top);  \t\twhile (rSrc.top &gt;= m_MaxBitmapSize) \t\t{ \t\t\trSrc.bottom -= m_MaxBitmapSize; \t\t\trSrc.top -= m_MaxBitmapSize; \t\t}  \t\twhile (rSrc.left &gt;= m_MaxBitmapSize) \t\t{ \t\t\trSrc.right -= m_MaxBitmapSize; \t\t\trSrc.left -= m_MaxBitmapSize; \t\t}  \t\tm_Target-&gt;DrawBitmap(seg.GetBitmap(), rDst, 1.0f, D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC, &rSrc); \t} }  void Canvas::DrawTiledBitmap(const D2DBitmap* bitmap, const D2D1_RECT_F& dstRect, const D2D1_RECT_F& srcRect) { \tconst FLOAT width = (FLOAT)bitmap-&gt;m_Width; \tconst FLOAT height = (FLOAT)bitmap-&gt;m_Height;  \tFLOAT x = dstRect.left; \tFLOAT y = dstRect.top;  \twhile (y &lt; dstRect.bottom) \t{ \t\tconst FLOAT w = (dstRect.right - x) &gt; width ? width : (dstRect.right - x); \t\tconst FLOAT h = (dstRect.bottom - y) &gt; height ? height : (dstRect.bottom - y);  \t\tconst auto dst = D2D1::RectF(x, y, x + w, y + h); \t\tconst auto src = D2D1::RectF(0.0f, 0.0f, w, h); \t\tDrawBitmap(bitmap, dst, src);  \t\tx += width; \t\tif (x &gt;= dstRect.right && y &lt; dstRect.bottom) \t\t{ \t\t\tx = dstRect.left; \t\t\ty += height; \t\t} \t} }  void Canvas::DrawMaskedBitmap(const D2DBitmap* bitmap, const D2DBitmap* maskBitmap, const D2D1_RECT_F& dstRect, \tconst D2D1_RECT_F& srcRect, const D2D1_RECT_F& srcRect2) { \tif (!bitmap || !maskBitmap) return;  \t\/\/ Create bitmap brush from original |bitmap|. \tMicrosoft::WRL::ComPtr&lt;ID2D1BitmapBrush1&gt; brush; \tD2D1_BITMAP_BRUSH_PROPERTIES1 propertiesXClampYClamp = D2D1::BitmapBrushProperties1( \t\tD2D1_EXTEND_MODE_CLAMP, \t\tD2D1_EXTEND_MODE_CLAMP, \t\tD2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC);  \tconst FLOAT width = (FLOAT)bitmap-&gt;m_Width; \tconst FLOAT height = (FLOAT)bitmap-&gt;m_Height;  \tauto getRectSubRegion = [&width, &height](const D2D1_RECT_F& r1, const D2D1_RECT_F& r2) -&gt; D2D1_RECT_F \t{ \t\treturn D2D1::RectF( \t\t\tr1.left \/ width * r2.right + r2.left, \t\t\tr1.top \/ height * r2.bottom + r2.top, \t\t\t(r1.right - r1.left) \/ width * r2.right, \t\t\t(r1.bottom - r1.top) \/ height * r2.bottom); \t};  \tfor (auto bseg : bitmap-&gt;m_Segments) \t{ \t\tconst auto rSeg = bseg.GetRect(); \t\tconst auto rDst = getRectSubRegion(rSeg, dstRect); \t\tconst auto rSrc = getRectSubRegion(rSeg, srcRect);  \t\tFLOAT s2Width = srcRect2.right - srcRect2.left; \t\tFLOAT s2Height = srcRect2.bottom - srcRect2.top;  \t\t\/\/ &quot;Move&quot; and &quot;scale&quot; the |bitmap| to match the destination. \t\tD2D1_MATRIX_3X2_F translateMask = D2D1::Matrix3x2F::Translation(-srcRect2.left, -srcRect2.top); \t\tD2D1_MATRIX_3X2_F translate = D2D1::Matrix3x2F::Translation(rDst.left, rDst.top); \t\tD2D1_MATRIX_3X2_F scale = D2D1::Matrix3x2F::Scale( \t\t\tD2D1::SizeF((rDst.right - rDst.left) \/ s2Width, (rDst.bottom - rDst.top) \/ s2Height)); \t\tD2D1_BRUSH_PROPERTIES brushProps = D2D1::BrushProperties(1.0f, translateMask * scale * translate);  \t\tHRESULT hr = m_Target-&gt;CreateBitmapBrush( \t\t\tbseg.GetBitmap(), \t\t\tpropertiesXClampYClamp, \t\t\tbrushProps, \t\t\tbrush.ReleaseAndGetAddressOf()); \t\tif (FAILED(hr)) return;  \t\tconst auto aaMode = m_Target-&gt;GetAntialiasMode(); \t\tm_Target-&gt;SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); \/\/ required  \t\tfor (auto mseg : maskBitmap-&gt;m_Segments) \t\t{ \t\t\tconst auto rmSeg = mseg.GetRect(); \t\t\tconst auto rmDst = getRectSubRegion(rmSeg, dstRect); \t\t\tconst auto rmSrc = getRectSubRegion(rmSeg, srcRect);  \t\t\t\/\/ If no overlap, don't draw \t\t\tif ((rmDst.left &lt; (rDst.left + rDst.right) && \t\t\t\t(rmDst.right + rmDst.left) &gt; rDst.left && \t\t\t\trmDst.top &gt; (rmDst.top + rmDst.bottom) && \t\t\t\t(rmDst.top + rmDst.bottom) &lt; rmDst.top)) continue;  \t\t\tm_Target-&gt;FillOpacityMask( \t\t\t\tmseg.GetBitmap(), \t\t\t\tbrush.Get(), \t\t\t\tD2D1_OPACITY_MASK_CONTENT_GRAPHICS, \t\t\t\t&rDst, \t\t\t\t&rSrc); \t\t}  \t\tm_Target-&gt;SetAntialiasMode(aaMode); \t} }  void Canvas::FillRectangle(const D2D1_RECT_F& rect, const D2D1_COLOR_F& color) { \tMicrosoft::WRL::ComPtr&lt;ID2D1SolidColorBrush&gt; solidBrush; \tHRESULT hr = m_Target-&gt;CreateSolidColorBrush(color, solidBrush.GetAddressOf()); \tif (SUCCEEDED(hr)) \t{ \t\tm_Target-&gt;FillRectangle(rect, solidBrush.Get()); \t} }  void Canvas::FillGradientRectangle(const D2D1_RECT_F& rect, const D2D1_COLOR_F& color1, const D2D1_COLOR_F& color2, const FLOAT& angle) { \t\/\/ D2D requires 2 points to draw the gradient along where GDI+ just requires a rectangle. To \t\/\/ mimic GDI+ for SolidColor2, we have to find and swap the starting and ending points of where \t\/\/ the gradient touches edge of the bounding rectangle. Normally we would offset the ending \t\/\/ point by 180, but we do this on starting point for SolidColor2. This differs from the other \t\/\/ D2D gradient options below: \t\/\/  Gfx::TextInlineFormat_GradientColor::BuildGradientBrushes \t\/\/  Gfx::Shape::CreateLinearGradient \tD2D1_POINT_2F start = Util::FindEdgePoint(angle + 180.0f, rect.left, rect.top, rect.right, rect.bottom); \tD2D1_POINT_2F end = Util::FindEdgePoint(angle, rect.left, rect.top, rect.right, rect.bottom);  \tMicrosoft::WRL::ComPtr&lt;ID2D1GradientStopCollection&gt; pGradientStops;  \tD2D1_GRADIENT_STOP gradientStops[2]; \tgradientStops[0].color = color1; \tgradientStops[0].position = 0.0f; \tgradientStops[1].color = color2; \tgradientStops[1].position = 1.0f;  \tHRESULT hr = m_Target-&gt;CreateGradientStopCollection( \t\tgradientStops, \t\t2U, \t\tD2D1_GAMMA_2_2, \t\tD2D1_EXTEND_MODE_CLAMP, \t\tpGradientStops.GetAddressOf()); \tif (FAILED(hr)) return;  \tMicrosoft::WRL::ComPtr&lt;ID2D1LinearGradientBrush&gt; brush; \thr = m_Target-&gt;CreateLinearGradientBrush( \t\tD2D1::LinearGradientBrushProperties(start, end), \t\tpGradientStops.Get(), \t\tbrush.GetAddressOf()); \tif (FAILED(hr)) return;  \tm_Target-&gt;FillRectangle(rect, brush.Get()); }  void Canvas::DrawLine(const D2D1_COLOR_F& color, FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2, FLOAT strokeWidth) { \tMicrosoft::WRL::ComPtr&lt;ID2D1SolidColorBrush&gt; solidBrush; \tHRESULT hr = m_Target-&gt;CreateSolidColorBrush(color, solidBrush.GetAddressOf()); \tif (FAILED(hr)) return;  \tm_Target-&gt;DrawLine(D2D1::Point2F(x1, y1), D2D1::Point2F(x2, y2), solidBrush.Get(), strokeWidth); }  void Canvas::DrawGeometry(Shape& shape, int xPos, int yPos) { \tD2D1_MATRIX_3X2_F worldTransform; \tm_Target-&gt;GetTransform(&worldTransform); \tm_Target-&gt;SetTransform( \t\tshape.GetShapeMatrix() * \t\tD2D1::Matrix3x2F::Translation((FLOAT)xPos, (FLOAT)yPos) * \t\tworldTransform);  \tauto fill = shape.GetFillBrush(m_Target.Get()); \tif (fill) \t{ \t\tm_Target-&gt;FillGeometry(shape.m_Shape.Get(), fill.Get()); \t}  \tauto stroke = shape.GetStrokeFillBrush(m_Target.Get()); \tif (stroke) \t{ \t\tm_Target-&gt;DrawGeometry( \t\t\tshape.m_Shape.Get(), \t\t\tstroke.Get(), \t\t\tshape.m_StrokeWidth, \t\t\tshape.m_StrokeStyle.Get()); \t}  \tm_Target-&gt;SetTransform(worldTransform); }  HRESULT Canvas::CreateRenderTarget() { \tHRESULT hr = E_FAIL; \tif (c_D2DDevice) \t{ \t\tc_D2DDevice-&gt;ClearResources();  \t\thr = c_D2DDevice-&gt;CreateDeviceContext( \t\t\tD2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, \t\t\tm_Target.ReleaseAndGetAddressOf()); \t\tif (FAILED(hr)) \t\t{ \t\t\thr = c_D2DDevice-&gt;CreateDeviceContext( \t\t\t\tD2D1_DEVICE_CONTEXT_OPTIONS_NONE, \t\t\t\tm_Target.ReleaseAndGetAddressOf()); \t\t} \t}  \t\/\/ Hardware accelerated targets have a hard limit to the size of bitmaps they can support. \t\/\/ The size will depend on the D3D feature level of the driver used. The WARP software \t\/\/ renderer has a limit of 16MP (16*1024*1024 = 16777216).  \t\/\/ https:\/\/docs.microsoft.com\/en-us\/windows\/desktop\/direct3d11\/overviews-direct3d-11-devices-downlevel-intro#overview-for-each-feature-level \t\/\/ Max Texture Dimension \t\/\/ D3D_FEATURE_LEVEL_11_1 = 16348 \t\/\/ D3D_FEATURE_LEVEL_11_0 = 16348 \t\/\/ D3D_FEATURE_LEVEL_10_1 = 8192 \t\/\/ D3D_FEATURE_LEVEL_10_0 = 8192 \t\/\/ D3D_FEATURE_LEVEL_9_3  = 4096 \t\/\/ D3D_FEATURE_LEVEL_9_2  = 2048 \t\/\/ D3D_FEATURE_LEVEL_9_1  = 2048  \tif (SUCCEEDED(hr)) \t{ \t\tm_MaxBitmapSize = m_Target-&gt;GetMaximumBitmapSize(); \t}  \treturn hr; }  bool Canvas::CreateTargetBitmap(UINT32 width, UINT32 height) { \tHRESULT hr = m_SwapChain-&gt;GetBuffer(0U, IID_PPV_ARGS(m_BackBuffer.GetAddressOf())); \tif (FAILED(hr)) return LogComError(hr);  \tD2D1_BITMAP_PROPERTIES1 bProps = D2D1::BitmapProperties1( \t\tD2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW, \t\tD2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED));  \thr = m_Target-&gt;CreateBitmapFromDxgiSurface( \t\tm_BackBuffer.Get(), \t\t&bProps, \t\tm_TargetBitmap.GetAddressOf()); \tif (FAILED(hr)) return LogComError(hr);  \tm_Target-&gt;SetTarget(m_TargetBitmap.Get()); \treturn true; }  }  \/\/ namespace Gfx <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0422\u043e \u0447\u0442\u043e \u043d\u0430\u0434\u043e:<\/p>\n<pre><code class=\"cpp\">Microsoft::WRL::ComPtr&lt;ID3D11Device&gt; Canvas::c_D3DDevice; Microsoft::WRL::ComPtr&lt;ID3D11DeviceContext&gt; Canvas::c_D3DContext; Microsoft::WRL::ComPtr&lt;ID2D1Device&gt; Canvas::c_D2DDevice; Microsoft::WRL::ComPtr&lt;IDXGIDevice1&gt; Canvas::c_DxgiDevice; Microsoft::WRL::ComPtr&lt;ID2D1Factory1&gt; Canvas::c_D2DFactory; Microsoft::WRL::ComPtr&lt;IDWriteFactory1&gt; Canvas::c_DWFactory; Microsoft::WRL::ComPtr&lt;IWICImagingFactory&gt; Canvas::c_WICFactory; . . . \u0415\u0449\u0451 \u0432\u0441\u044f\u043a\u043e\u0435 <\/code><\/pre>\n<p>  \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 Context \u0432 Common\/Gfx\/Canvas.h:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Context<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"cpp\">. . . class Canvas { public:  \tstruct Context \t{ \t\tMicrosoft::WRL::ComPtr&lt;ID3D11Device&gt; D3DDevice; \t\tMicrosoft::WRL::ComPtr&lt;ID3D11DeviceContext&gt; D3DContext; \t\tMicrosoft::WRL::ComPtr&lt;ID2D1Device&gt; D2DDevice; \t\tMicrosoft::WRL::ComPtr&lt;IDXGIDevice1&gt; DxgiDevice; \t\tMicrosoft::WRL::ComPtr&lt;ID2D1Factory1&gt; D2DFactory; \t\tMicrosoft::WRL::ComPtr&lt;IDWriteFactory1&gt; DWFactory; \t\tMicrosoft::WRL::ComPtr&lt;IWICImagingFactory&gt; WICFactory; \t\tMicrosoft::WRL::ComPtr&lt;ID2D1DeviceContext&gt; Target; \t\tMicrosoft::WRL::ComPtr&lt;IDXGISwapChain1&gt; SwapChain; \t\tMicrosoft::WRL::ComPtr&lt;IDXGISurface1&gt; BackBuffer; \t\tMicrosoft::WRL::ComPtr&lt;ID2D1Bitmap1&gt; TargetBitmap; \t\tint W; \t\tint H; \t\tUINT32 MaxBitmapSize; \t\tbool IsDrawing; \t\tbool EnableDrawAfterGdi; \t\tbool AccurateText; \t\tbool TextAntiAliasing; \t\tbool CanUseAxisAlignClip; \t};  \tContext GetContext(); \t. \t. \t. }; <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Common\/Canvas.cpp<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"cpp\">. . . Canvas::Context Canvas::GetContext() { \tContext context; \tcontext.D2DDevice = c_D2DDevice; \tcontext.D2DFactory = c_D2DFactory; \tcontext.D3DContext = c_D3DContext; \tcontext.D3DDevice = c_D3DDevice; \tcontext.DWFactory = c_DWFactory; \tcontext.WICFactory = c_WICFactory; \tcontext.DxgiDevice = c_DxgiDevice; \tcontext.AccurateText = m_AccurateText; \tcontext.BackBuffer = m_BackBuffer; \tcontext.CanUseAxisAlignClip = m_CanUseAxisAlignClip; \tcontext.EnableDrawAfterGdi = m_EnableDrawAfterGdi; \tcontext.H = m_H; \tcontext.IsDrawing = m_IsDrawing; \tcontext.MaxBitmapSize = m_MaxBitmapSize; \tcontext.SwapChain = m_SwapChain; \tcontext.Target = m_Target; \tcontext.TargetBitmap = m_TargetBitmap; \tcontext.TextAntiAliasing = m_TextAntiAliasing; \tcontext.W = m_W; \treturn context; } . . . <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u044b\u0439 meter \u2014 Canvas:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Library\/MeterCanvas.h<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"cpp\">#ifndef __METERCANVAS_H__ #define __METERCANVAS_H__  #include &quot;Meter.h&quot;  class MeterCanvas : public Meter { public: \tMeterCanvas(Skin* skin, const WCHAR* name);  \tUINT GetTypeID() override { return TypeID&lt;MeterCanvas&gt;(); }  \tvirtual bool Update(); \tvirtual bool Draw(Gfx::Canvas& canvas);  \tbool HitTest(int x, int y);  \t~MeterCanvas(); }; #endif <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Library\/MeterCanvas.cpp<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"cpp\">#include &quot;StdAfx.h&quot; #include &quot;MeterCanvas.h&quot; #include &quot;Logger.h&quot; #include &quot;windows.h&quot; #include &lt;iostream&gt; #include &quot;..\/Common\/Gfx\/Canvas.h&quot; #include &quot;..\/Common\/Gfx\/Util\/D2DUtil.h&quot; MeterCanvas::MeterCanvas(Skin* skin, const WCHAR* name) : Meter(skin, name) { \tMeter::Initialize(); }  bool MeterCanvas::Update() { \treturn Meter::Update(); }  bool MeterCanvas::Draw(Gfx::Canvas& canvas) { \treturn Meter::Draw(canvas); } bool MeterCanvas::HitTest(int x, int y) { \treturn Meter::HitTest(x, y); }  MeterCanvas::~MeterCanvas() { \t }  <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f meter&#8217;\u0430:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Library\/Meter.cpp<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"cpp\">#include &quot;MeterCanvas.h&quot; . . . Meter* Meter::Create(const WCHAR* meter, Skin* skin, const WCHAR* name) { \tif (_wcsicmp(L&quot;STRING&quot;, meter) == 0) \t{ \t\treturn new MeterString(skin, name); \t} \telse if (_wcsicmp(L&quot;IMAGE&quot;, meter) == 0) \t{ \t\treturn new MeterImage(skin, name); \t} \telse if (_wcsicmp(L&quot;HISTOGRAM&quot;, meter) == 0) \t{ \t\treturn new MeterHistogram(skin, name); \t} \telse if (_wcsicmp(L&quot;BAR&quot;, meter) == 0) \t{ \t\treturn new MeterBar(skin, name); \t} \telse if (_wcsicmp(L&quot;BITMAP&quot;, meter) == 0) \t{ \t\treturn new MeterBitmap(skin, name); \t} \telse if (_wcsicmp(L&quot;LINE&quot;, meter) == 0) \t{ \t\treturn new MeterLine(skin, name); \t} \telse if (_wcsicmp(L&quot;ROUNDLINE&quot;, meter) == 0) \t{ \t\treturn new MeterRoundLine(skin, name); \t} \telse if (_wcsicmp(L&quot;ROTATOR&quot;, meter) == 0) \t{ \t\treturn new MeterRotator(skin, name); \t} \telse if (_wcsicmp(L&quot;BUTTON&quot;, meter) == 0) \t{ \t\treturn new MeterButton(skin, name); \t} \telse if (_wcsicmp(L&quot;SHAPE&quot;, meter) == 0) \t{ \t\treturn new MeterShape(skin, name); \t} \telse if (_wcsicmp(L&quot;CANVAS&quot;, meter) == 0) \t{ \t\treturn new MeterCanvas(skin, name); \t}  \tLogErrorF(skin, L&quot;Meter=%s is not valid in [%s]&quot;, meter, name);  \treturn nullptr; } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0447\u0442\u043e-\u0442\u043e \u043d\u0430\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Meter.ini<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"plaintext\">[Rainmeter] Update=1000  [Text] Meter=CANVAS W=100 H=100 <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Library\/MeterCanvas.cpp<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"cpp\">bool MeterCanvas::Draw(Gfx::Canvas& canvas) { \tMicrosoft::WRL::ComPtr&lt;ID2D1SolidColorBrush&gt; brush; \tauto target = canvas.GetContext().Target; \ttarget-&gt;CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), brush.GetAddressOf()); \ttarget-&gt;FillRectangle(D2D1::RectF(100, 100), brush.Get()); \treturn Meter::Draw(canvas); } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/i.imgur.com\/NsQq2qU.png\" alt=\"image\"\/><\/p>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0438\u0448\u0435\u043c dll \u0434\u043b\u044f \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0438:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Draw.h<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"cpp\">#include &lt;d2d1_1.h&gt; #include &lt;wrl\\client.h&gt; #include &quot;Context.h&quot; #pragma once #ifdef LIBRARY_EXPORTS #define EXPORT_PLUGIN #else #define EXPORT_PLUGIN __declspec(dllexport) #endif extern &quot;C&quot; EXPORT_PLUGIN void Draw(Context context); <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Context.h<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"cpp\">#pragma once #include &lt;string&gt; #include &lt;d2d1_1.h&gt; #include &lt;dwrite_1.h&gt; #include &lt;wincodec.h&gt; #include &lt;wrl\/client.h&gt; #include &lt;d3d11.h&gt; #include &lt;DXGI1_2.h&gt; struct Context { \tMicrosoft::WRL::ComPtr&lt;ID3D11Device&gt; D3DDevice; \tMicrosoft::WRL::ComPtr&lt;ID3D11DeviceContext&gt; D3DContext; \tMicrosoft::WRL::ComPtr&lt;ID2D1Device&gt; D2DDevice; \tMicrosoft::WRL::ComPtr&lt;IDXGIDevice1&gt; DxgiDevice; \tMicrosoft::WRL::ComPtr&lt;ID2D1Factory1&gt; D2DFactory; \tMicrosoft::WRL::ComPtr&lt;IDWriteFactory1&gt; DWFactory; \tMicrosoft::WRL::ComPtr&lt;IWICImagingFactory&gt; WICFactory; \tMicrosoft::WRL::ComPtr&lt;ID2D1DeviceContext&gt; Target; \tMicrosoft::WRL::ComPtr&lt;IDXGISwapChain1&gt; SwapChain; \tMicrosoft::WRL::ComPtr&lt;IDXGISurface1&gt; BackBuffer; \tMicrosoft::WRL::ComPtr&lt;ID2D1Bitmap1&gt; TargetBitmap; \tint W; \tint H; \tUINT32 MaxBitmapSize; \tbool IsDrawing; \tbool EnableDrawAfterGdi; \tbool AccurateText; \tbool TextAntiAliasing; \tbool CanUseAxisAlignClip; }; <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Draw.cpp<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"cpp\">#include &quot;pch.h&quot; #include &quot;Draw.h&quot;  #pragma comment(lib,&quot;d2d1.lib&quot;) EXPORT_PLUGIN void Draw(Context context) { \tMicrosoft::WRL::ComPtr&lt;ID2D1SolidColorBrush&gt; brush; \tauto target = context.Target; \ttarget-&gt;CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), brush.GetAddressOf()); \ttarget-&gt;FillRectangle(D2D1::RectF(100, 100), brush.Get()); } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u041a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u0435\u043c Draw.dll:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Library\/MeterCanvas.cpp<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"cpp\">HMODULE hlib; void (*draw)(Gfx::Canvas::Context context); MeterCanvas::MeterCanvas(Skin* skin, const WCHAR* name) : Meter(skin, name) { \thlib = LoadLibrary(L&quot;Draw.dll&quot;); \t(FARPROC&)draw = GetProcAddress(hlib, &quot;Draw&quot;); \tMeter::Initialize(); } bool MeterCanvas::Draw(Gfx::Canvas& canvas) { \tif (draw != nullptr) \t\tdraw(canvas.GetContext()); \treturn Meter::Draw(canvas); } MeterCanvas::~MeterCanvas() { \tFreeLibrary(hlib); } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0422\u043e\u0436\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/i.imgur.com\/NsQq2qU.png\" alt=\"image\"\/><\/p>\n<p>  \u0414\u043e\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u0434, \u0434\u043b\u044f \u0432\u044b\u0431\u043e\u0440\u0430 dll&#8217;\u043a\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c:<\/p>\n<pre><code class=\"cpp\">std::wstring dll; . . . MeterCanvas::MeterCanvas(Skin* skin, const WCHAR* name) : Meter(skin, name) { \tdll = skin-&gt;GetParser().GetValue(name, L&quot;Dll&quot;, L&quot;&quot;);  \thlib = LoadLibrary(dll.c_str());  \t(FARPROC&)draw = GetProcAddress(hlib, &quot;Draw&quot;);  \tMeter::Initialize(); } <\/code><\/pre>\n<p>  Meter.ini  <\/p>\n<pre><code class=\"plaintext\">[Rainmeter] Update=1000  [Text] Meter=CANVAS Dll=Draw.dll W=100 H=100 <\/code><\/pre>\n<p>  \u041f\u043e\u0447\u0435\u043c\u0443 \u0431\u044b \u043d\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 bang?<br \/>  \u0421\u0434\u0435\u043b\u0430\u0435\u043c.<\/p>\n<p>  \u0414\u043e\u0431\u0430\u0432\u0438\u043c Action \u0432 \u0441\u043f\u0438\u0441\u043e\u043a bang&#8217;\u043e\u0432.<\/p>\n<p>  Library\/CommandHandler.h:<\/p>\n<pre><code class=\"cpp\">enum class Bang { \tAction, \t. \t. \t. } <\/code><\/pre>\n<p>  Library\/CommandHandler.cpp  <\/p>\n<pre><code class=\"cpp\">const BangInfo s_Bangs[] = { \t{Bang::Action, L&quot;Action&quot;, 1}, \t. \t. \t. } <\/code><\/pre>\n<p>  Library\/Skin.cpp:<\/p>\n<pre><code class=\"cpp\">void Skin::DoBang(Bang bang, const std::vector&lt;std::wstring&gt;& args) { \tswitch (bang) \t{ \tcase Bang::Action: \t\tGetMeters()[0]-&gt;Action(args[0]); \t\tbreak; \t. \t. \t. } <\/code><\/pre>\n<p>  Library\/Meter.h:<\/p>\n<pre><code class=\"cpp\">class __declspec(novtable) Meter : public Section { public: \tvirtual void Action(std::wstring arg) {}; \t. \t. \t. } <\/code><\/pre>\n<p>  Library\/MeterCanvas.h:<\/p>\n<pre><code class=\"cpp\">class MeterCanvas : public Meter { public: \tvirtual void Action(std::wstring arg) {}; \t. \t. \t. } <\/code><\/pre>\n<p>  Meter.ini:<\/p>\n<pre><code class=\"plaintext\">[Rainmeter] Update=1000  [Text] Meter=CANVAS Dll=Draw.dll W=100 H=100 LeftMouseDownAction=!Action Test <\/code><\/pre>\n<p>  <\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430<\/b>                         <\/p>\n<div class=\"spoiler_text\">Library\/MeterCanvas.cpp:<\/p>\n<pre><code class=\"cpp\">. . . void MeterCanvas::Action(std::wstring arg) { \tLogDebug(arg.c_str()); } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <img decoding=\"async\" src=\"https:\/\/i.imgur.com\/eYHrw72.png\" alt=\"image\"\/><\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0418\u0437 dll<\/b>                         <\/p>\n<div class=\"spoiler_text\">Library\/MeterCanvas.cpp  <\/p>\n<pre><code class=\"cpp\">void (*action)(std::wstring arg); . . . MeterCanvas::MeterCanvas(Skin* skin, const WCHAR* name) : Meter(skin, name) { \thlib = LoadLibrary(L&quot;Draw.dll&quot;); \t(FARPROC&)draw = GetProcAddress(hlib, &quot;Draw&quot;); \t(FARPROC&)action = GetProcAddress(hlib, &quot;Action&quot;); \tMeter::Initialize(); } void MeterCanvas::Action(std::wstring arg) { \tif (action != nullptr) \t\taction(arg); } <\/code><\/pre>\n<p>  Draw.h  <\/p>\n<pre><code class=\"cpp\">#include &lt;d2d1_1.h&gt; #include &lt;wrl\\client.h&gt; #include &lt;math.h&gt; #include &quot;Context.h&quot; #pragma once #ifdef LIBRARY_EXPORTS #define EXPORT_PLUGIN #else #define EXPORT_PLUGIN __declspec(dllexport) #endif extern &quot;C&quot; EXPORT_PLUGIN void Draw(Context context);  extern &quot;C&quot; EXPORT_PLUGIN void Action(std::wstring arg); <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u0422\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435:<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/b>                         <\/p>\n<div class=\"spoiler_text\">Draw.h  <\/p>\n<pre><code class=\"cpp\">#include &lt;d2d1_1.h&gt; #include &lt;wrl\\client.h&gt; #include &lt;math.h&gt; #include &quot;Context.h&quot; #pragma once #ifdef LIBRARY_EXPORTS #define EXPORT_PLUGIN #else #define EXPORT_PLUGIN __declspec(dllexport) #endif extern &quot;C&quot; EXPORT_PLUGIN void Init(Context context);  extern &quot;C&quot; EXPORT_PLUGIN void Draw(Context context);  extern &quot;C&quot; EXPORT_PLUGIN void Action(std::wstring arg); <\/code><\/pre>\n<p>  Library\/MeterCanvas.cpp  <\/p>\n<pre><code class=\"cpp\">void (*init)(Gfx::Canvas::Context context); . . . MeterCanvas::MeterCanvas(Skin* skin, const WCHAR* name) : Meter(skin, name) { \tdll = skin-&gt;GetParser().GetValue(name, L&quot;Dll&quot;, L&quot;&quot;);  \thlib = LoadLibrary(dll.c_str());  \t(FARPROC&)draw = GetProcAddress(hlib, &quot;Draw&quot;);  \t(FARPROC&)action = GetProcAddress(hlib, &quot;Action&quot;);  \t(FARPROC&)init = GetProcAddress(hlib, &quot;Init&quot;);                           \tinit(skin-&gt;GetCanvas().GetContext());  \tMeter::Initialize(); } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u041f\u043e\u0447\u0442\u0438 \u0431\u0435\u0437\u0433\u0440\u0430\u043d\u0438\u0447\u043d\u0430\u044f \u0432\u043b\u0430\u0441\u0442\u044c!<\/p>\n<p>  \u041d\u043e \u0447\u0435\u0433\u043e-\u0442\u043e \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u0435\u0442\u2026<\/p>\n<p>  \u0413\u0434\u0435 \u0436\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043d\u0430\u0436\u0430\u0442\u0438\u0439 \u043d\u0430 \u043a\u043b\u0430\u0432\u0443?<\/p>\n<p>  \u0410 \u0432\u043e\u0442 \u043e\u043d\u0430:<\/p>\n<div class=\"oembed\"><a href=\"https:\/\/forum.rainmeter.net\/viewtopic.php?t=18849\">https:\/\/forum.rainmeter.net\/viewtopic.php?t=18849<\/a><\/div>\n<p>  \u041d\u043e \u043e\u043d\u043e \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442.<br \/>  \u041d\u0435 \u0431\u0435\u0434\u0430.<\/p>\n<p>  \u0417\u0430\u0445\u043e\u0434\u0438\u043c \u0432 rainmeter-master\\x32-Debug\\Plugins<br \/>  \u041d\u0430\u043c \u043d\u0443\u0436\u043d\u044b \u0435\u0449\u0435 \u0444\u0430\u0439\u043b\u044b .pdb \u0438 .ilk<\/p>\n<p>  \u041a\u0430\u0447\u0430\u0435\u043c.<\/p>\n<div class=\"oembed\"><a href=\"https:\/\/github.com\/brianferguson\/HotKey.dll\">https:\/\/github.com\/brianferguson\/HotKey.dll<\/a><\/div>\n<p>  \u041a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u0435\u043c, \u043a\u0438\u0434\u0430\u0435\u043c \u043a \u043f\u043b\u0430\u0433\u0438\u043d\u0430\u043c.<\/p>\n<p>  \u0412\u043e\u0442 \u0438 \u0432\u0441\u0451.<br \/>  \u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0432\u043e\u043e\u0431\u0449\u0435 \u0432\u0441\u0451.<\/p>\n<p>  \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u043e\u043d\u0443\u0441\u0430 \u2014 \u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<p>  <a href=\"https:\/\/drive.google.com\/file\/d\/1Wsqx3Hzxh6mVru_v4r0CeqoW6wA_b-9S\/view?usp=sharing\">\u0412\u0438\u0434\u0435\u043e<\/a><\/p>\n<p>  <a href=\"https:\/\/github.com\/atrepalin\/Rainmeter\"><b>\u0418\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438<\/b><\/a><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/512510\/\"> https:\/\/habr.com\/ru\/post\/512510\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\" data-io-article-url=\"https:\/\/habr.com\/ru\/post\/512510\/\">\u0414\u043e\u0431\u0440\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0441\u0443\u0442\u043e\u043a, \u0425\u0430\u0431\u0440. \u0412\u0441\u0435\u0433\u0434\u0430 \u0445\u043e\u0442\u0435\u043b \u0441\u0432\u043e\u0431\u043e\u0434\u044b \u0432 Rainmeter&#8217;\u0435. \u041e\u0434\u043d\u043e\u0442\u0438\u043f\u043d\u044b\u0435 \u0441\u043a\u0438\u043d\u044b, \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u044b \u2014 \u043d\u0435 \u0442\u043e.<\/p>\n<p>  \u0421\u0435\u0433\u043e\u0434\u043d\u044f \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043a\u0430\u043a \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u043e\u043b\u043d\u0443\u044e \u0432\u043b\u0430\u0441\u0442\u044c \u043d\u0430\u0434 Rainmeter&#8217;\u043e\u043c.<\/p>\n<p>  \u0417\u0430\u043b\u0435\u0437\u0435\u043c \u0432 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438 Rainmeter&#8217;\u0430 \u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u0436\u0435 \u0442\u0430\u043c \u0432\u0441\u0451 \u0442\u0430\u043a \u043a\u0440\u0430\u0441\u0438\u0432\u043e \u0440\u0438\u0441\u0443\u0435\u0442\u0441\u044f\u2026<\/p>\n<div class=\"oembed\"><a href=\"https:\/\/github.com\/rainmeter\/rainmeter\/\">https:\/\/github.com\/rainmeter\/rainmeter\/<\/a><\/div>\n<p>  \u041d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0444\u0430\u0439\u043b Library\/Meter.cpp  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-307513","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/307513","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=307513"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/307513\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=307513"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=307513"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=307513"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}