Русский
Русский
English
Статистика
Реклама

Из песочницы Direct2D в Rainmeter

Доброго времени суток, Хабр. Всегда хотел свободы в Rainmeter'е. Однотипные скины, простые плагины не то.

Сегодня я расскажу как получить полную власть над Rainmeter'ом.

Залезем в исходники Rainmeter'а и посмотрим, как же там всё так красиво рисуется


Нас интересует файл Library/Meter.cpp

Скрытый текст
bool Meter::Draw(Gfx::Canvas& canvas){if (IsHidden()) return false;canvas.SetAntiAliasing(m_AntiAlias);if (m_SolidColor.a != 0.0f || m_SolidColor2.a != 0.0f){const FLOAT x = (FLOAT)GetX();const FLOAT y = (FLOAT)GetY();const D2D1_RECT_F r = D2D1::RectF(x, y, x + (FLOAT)m_W, y + (FLOAT)m_H);if (m_SolidColor.r == m_SolidColor2.r && m_SolidColor.g == m_SolidColor2.g && m_SolidColor.b == m_SolidColor2.b && m_SolidColor.a == m_SolidColor2.a){canvas.FillRectangle(r, m_SolidColor);}else{canvas.FillGradientRectangle(r, m_SolidColor, m_SolidColor2, (FLOAT)m_SolidAngle);}}if (m_SolidBevel != BEVELTYPE_NONE){D2D1_COLOR_F lightColor = D2D1::ColorF(D2D1::ColorF::White);D2D1_COLOR_F darkColor = D2D1::ColorF(D2D1::ColorF::Black);if (m_SolidBevel == BEVELTYPE_DOWN){lightColor = D2D1::ColorF(D2D1::ColorF::Black);darkColor = D2D1::ColorF(D2D1::ColorF::White);}// The bevel is drawn outside the meterconst FLOAT x = (FLOAT)GetX();const FLOAT y = (FLOAT)GetY();const D2D1_RECT_F rect = D2D1::RectF(x - 2.0f, y - 2.0f, x + (FLOAT)m_W + 2.0f, y + (FLOAT)m_H + 2.0f);DrawBevel(canvas, rect, lightColor, darkColor);}return true;}


Теперь узнаем что же такое этот Canvas

Common/Gfx/Canvas.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 <https://www.gnu.org/licenses/gpl-2.0.html>. */#include "StdAfx.h"#include "Canvas.h"#include "TextFormatD2D.h"#include "D2DBitmap.h"#include "RenderTexture.h"#include "Util/D2DUtil.h"#include "Util/DWriteFontCollectionLoader.h"#include "../../Library/Util.h"#include "../../Library/Logger.h"namespace Gfx {UINT Canvas::c_Instances = 0;D3D_FEATURE_LEVEL Canvas::c_FeatureLevel;Microsoft::WRL::ComPtr<ID3D11Device> Canvas::c_D3DDevice;Microsoft::WRL::ComPtr<ID3D11DeviceContext> Canvas::c_D3DContext;Microsoft::WRL::ComPtr<ID2D1Device> Canvas::c_D2DDevice;Microsoft::WRL::ComPtr<IDXGIDevice1> Canvas::c_DxgiDevice;Microsoft::WRL::ComPtr<ID2D1Factory1> Canvas::c_D2DFactory;Microsoft::WRL::ComPtr<IDWriteFactory1> Canvas::c_DWFactory;Microsoft::WRL::ComPtr<IWICImagingFactory> Canvas::c_WICFactory;Canvas::Canvas() :m_W(0),m_H(0),m_MaxBitmapSize(0U),m_IsDrawing(false),m_EnableDrawAfterGdi(false),m_TextAntiAliasing(false),m_CanUseAxisAlignClip(true){Initialize(true);}Canvas::~Canvas(){Finalize();}bool Canvas::LogComError(HRESULT hr){_com_error err(hr);LogErrorF(L"Error 0x%08x: %s", hr, err.ErrorMessage());return false;}bool Canvas::Initialize(bool hardwareAccelerated){++c_Instances;if (c_Instances == 1U){// Required for Direct2D interopability.UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;#ifdef _DEBUGcreationFlags |= D3D11_CREATE_DEVICE_DEBUG;#endifauto tryCreateContext = [&](D3D_DRIVER_TYPE driverType,const D3D_FEATURE_LEVEL* levels, UINT numLevels){return D3D11CreateDevice(nullptr,driverType,nullptr,creationFlags,levels,numLevels,D3D11_SDK_VERSION,c_D3DDevice.GetAddressOf(),&c_FeatureLevel,c_D3DContext.GetAddressOf());};// D3D selects the best feature level automatically and sets it// to |c_FeatureLevel|. First, we try to use the hardware driver// and if that fails, we try the WARP rasterizer for cases// where there is no graphics card or other failures.const D3D_FEATURE_LEVEL levels[] = {D3D_FEATURE_LEVEL_11_1,D3D_FEATURE_LEVEL_11_0,D3D_FEATURE_LEVEL_10_1,D3D_FEATURE_LEVEL_10_0,D3D_FEATURE_LEVEL_9_3,D3D_FEATURE_LEVEL_9_2,D3D_FEATURE_LEVEL_9_1};HRESULT hr = E_FAIL;if (hardwareAccelerated){hr = tryCreateContext(D3D_DRIVER_TYPE_HARDWARE, levels, _countof(levels));if (hr == E_INVALIDARG){hr = tryCreateContext(D3D_DRIVER_TYPE_HARDWARE, &levels[1], _countof(levels) - 1);}}if (FAILED(hr)){hr = tryCreateContext(D3D_DRIVER_TYPE_WARP, nullptr, 0U);if (FAILED(hr)) return false;}hr = c_D3DDevice.As(&c_DxgiDevice);if (FAILED(hr)) return false;D2D1_FACTORY_OPTIONS fo = {};#ifdef _DEBUGfo.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;#endifhr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,fo,c_D2DFactory.GetAddressOf());if (FAILED(hr)) return false;hr = c_D2DFactory->CreateDevice(c_DxgiDevice.Get(),c_D2DDevice.GetAddressOf());if (FAILED(hr)) return false;hr = CoCreateInstance(CLSID_WICImagingFactory,nullptr,CLSCTX_INPROC_SERVER,IID_IWICImagingFactory,(LPVOID*)c_WICFactory.GetAddressOf());if (FAILED(hr)) return false;hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,__uuidof(c_DWFactory),(IUnknown**)c_DWFactory.GetAddressOf());if (FAILED(hr)) return false;hr = c_DWFactory->RegisterFontCollectionLoader(Util::DWriteFontCollectionLoader::GetInstance());if (FAILED(hr)) return false;}return true;}void Canvas::Finalize(){--c_Instances;if (c_Instances == 0U){c_D3DDevice.Reset();c_D3DContext.Reset();c_D2DDevice.Reset();c_DxgiDevice.Reset();c_D2DFactory.Reset();c_WICFactory.Reset();if (c_DWFactory){c_DWFactory->UnregisterFontCollectionLoader(Util::DWriteFontCollectionLoader::GetInstance());c_DWFactory.Reset();}}}bool Canvas::InitializeRenderTarget(HWND hwnd){DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };swapChainDesc.Width = 1U;swapChainDesc.Height = 1U;swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;swapChainDesc.Stereo = false;swapChainDesc.SampleDesc.Count = 1U;swapChainDesc.SampleDesc.Quality = 0U;swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;swapChainDesc.BufferCount = 2U;swapChainDesc.Scaling = DXGI_SCALING_STRETCH;swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE;swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;Microsoft::WRL::ComPtr<IDXGIAdapter> dxgiAdapter;HRESULT hr = c_DxgiDevice->GetAdapter(dxgiAdapter.GetAddressOf());if (FAILED(hr)) return LogComError(hr);// Ensure that DXGI does not queue more than one frame at a time.hr = c_DxgiDevice->SetMaximumFrameLatency(1U);if (FAILED(hr)) return LogComError(hr);Microsoft::WRL::ComPtr<IDXGIFactory2> dxgiFactory;hr = dxgiAdapter->GetParent(IID_PPV_ARGS(dxgiFactory.GetAddressOf()));if (FAILED(hr)) return LogComError(hr);hr = dxgiFactory->CreateSwapChainForHwnd(c_DxgiDevice.Get(),hwnd,&swapChainDesc,nullptr,nullptr,m_SwapChain.ReleaseAndGetAddressOf());if (FAILED(hr)) return LogComError(hr);hr = CreateRenderTarget();if (FAILED(hr)) return LogComError(hr);return CreateTargetBitmap(0U, 0U);}void Canvas::Resize(int w, int h){// Truncate the size of the skin if it's too big.if (w > (int)m_MaxBitmapSize) w = (int)m_MaxBitmapSize;if (h > (int)m_MaxBitmapSize) h = (int)m_MaxBitmapSize;m_W = w;m_H = h;// Check if target, targetbitmap, backbuffer, swap chain are valid?// Unmap all resources tied to the swap chain.m_Target->SetTarget(nullptr);m_TargetBitmap.Reset();m_BackBuffer.Reset();// Resize swap chain.HRESULT hr = m_SwapChain->ResizeBuffers(0U,(UINT)w,(UINT)h,DXGI_FORMAT_B8G8R8A8_UNORM,DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE);if (FAILED(hr)) return;CreateTargetBitmap((UINT32)w, (UINT32)h);}bool Canvas::BeginDraw(){if (!m_Target){HRESULT hr = CreateRenderTarget();if (FAILED(hr)){m_IsDrawing = false;return false;}// Recreate target bitmapResize(m_W, m_H);}m_Target->BeginDraw();m_IsDrawing = true;return true;}void Canvas::EndDraw(){HRESULT hr = m_Target->EndDraw();if (FAILED(hr)){m_Target.Reset();}m_IsDrawing = false;}HDC Canvas::GetDC(){if (m_IsDrawing){m_EnableDrawAfterGdi = true;m_IsDrawing = false;EndDraw();}HDC hdc;HRESULT hr = m_BackBuffer->GetDC(FALSE, &hdc);if (FAILED(hr)) return nullptr;return hdc;}void Canvas::ReleaseDC(){m_BackBuffer->ReleaseDC(nullptr);if (m_EnableDrawAfterGdi){m_EnableDrawAfterGdi = false;m_IsDrawing = true;BeginDraw();}}bool Canvas::IsTransparentPixel(int x, int y){if (!(x >= 0 && y >= 0 && x < m_W && y < m_H)) return false;auto pixel = GetPixel(GetDC(), x, y);ReleaseDC();return (pixel & 0xFF000000) == 0;}void Canvas::GetTransform(D2D1_MATRIX_3X2_F* matrix){if (m_Target){m_Target->GetTransform(matrix);}}void Canvas::SetTransform(const D2D1_MATRIX_3X2_F& matrix){m_Target->SetTransform(matrix);m_CanUseAxisAlignClip =(matrix.m11 ==  1.0f && matrix.m12 ==  0.0f && matrix.m21 ==  0.0f && matrix.m22 ==  1.0f) ||// Angle: 0(matrix.m11 ==  0.0f && matrix.m12 ==  1.0f && matrix.m21 == -1.0f && matrix.m22 ==  0.0f) ||// Angle: 90(matrix.m11 == -1.0f && matrix.m12 ==  0.0f && matrix.m21 ==  0.0f && matrix.m22 == -1.0f) ||// Angle: 180(matrix.m11 ==  0.0f && matrix.m12 == -1.0f && matrix.m21 ==  1.0f && matrix.m22 ==  0.0f);// Angle: 270}void Canvas::ResetTransform(){m_Target->SetTransform(D2D1::Matrix3x2F::Identity());}bool Canvas::SetTarget(RenderTexture* texture){auto bitmap = texture->GetBitmap();if (bitmap->m_Segments.size() == 0) return false;auto image = bitmap->m_Segments[0].GetBitmap();m_Target->SetTarget(image);return true;}void Canvas::ResetTarget(){m_Target->SetTarget(m_TargetBitmap.Get());}void Canvas::SetAntiAliasing(bool enable){m_Target->SetAntialiasMode(enable ? D2D1_ANTIALIAS_MODE_PER_PRIMITIVE : D2D1_ANTIALIAS_MODE_ALIASED);}void Canvas::SetTextAntiAliasing(bool enable){// TODO: Add support for D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE?m_TextAntiAliasing = enable;m_Target->SetTextAntialiasMode(enable ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : D2D1_TEXT_ANTIALIAS_MODE_ALIASED);}void Canvas::Clear(const D2D1_COLOR_F& color){if (!m_Target) return;m_Target->Clear(color);}void Canvas::DrawTextW(const std::wstring& srcStr, const TextFormat& format, const D2D1_RECT_F& rect,const D2D1_COLOR_F& color, bool applyInlineFormatting){Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> solidBrush;HRESULT hr = m_Target->CreateSolidColorBrush(color, solidBrush.GetAddressOf());if (FAILED(hr)) return;TextFormatD2D& formatD2D = (TextFormatD2D&)format;static std::wstring str;str = srcStr;formatD2D.ApplyInlineCase(str);if (!formatD2D.CreateLayout(m_Target.Get(),str,rect.right - rect.left,rect.bottom - rect.top,!m_AccurateText && m_TextAntiAliasing)) return;D2D1_POINT_2F drawPosition;drawPosition.x = [&](){if (!m_AccurateText){const float xOffset = formatD2D.m_TextFormat->GetFontSize() / 6.0f;switch (formatD2D.GetHorizontalAlignment()){case HorizontalAlignment::Left: return rect.left + xOffset;case HorizontalAlignment::Right: return rect.left - xOffset;}}return rect.left;} ();drawPosition.y = [&](){// GDI+ compatibility.float yPos = rect.top - formatD2D.m_LineGap;switch (formatD2D.GetVerticalAlignment()){case VerticalAlignment::Bottom: yPos -= formatD2D.m_ExtraHeight; break;case VerticalAlignment::Center: yPos -= formatD2D.m_ExtraHeight / 2.0f; break;}return yPos;} ();if (formatD2D.m_Trimming){D2D1_RECT_F clipRect = rect;if (m_CanUseAxisAlignClip){m_Target->PushAxisAlignedClip(clipRect, D2D1_ANTIALIAS_MODE_ALIASED);}else{const D2D1_LAYER_PARAMETERS1 layerParams =D2D1::LayerParameters1(clipRect, nullptr, D2D1_ANTIALIAS_MODE_ALIASED);m_Target->PushLayer(layerParams, nullptr);}}// When different "effects" are used with inline coloring options, we need to// remove the previous inline coloring, then reapply them (if needed) - instead// of destroying/recreating the text layout.UINT32 strLen = (UINT32)str.length();formatD2D.ResetInlineColoring(solidBrush.Get(), strLen);if (applyInlineFormatting){formatD2D.ApplyInlineColoring(m_Target.Get(), &drawPosition);// Draw any 'shadow' effectsformatD2D.ApplyInlineShadow(m_Target.Get(), solidBrush.Get(), strLen, drawPosition);}m_Target->DrawTextLayout(drawPosition, formatD2D.m_TextLayout.Get(), solidBrush.Get());if (applyInlineFormatting){// Inline gradients require the drawing position, so in case that position// changes, we need a way to reset it after drawing time so on the next// iteration it will know the correct position.formatD2D.ResetGradientPosition(&drawPosition);}if (formatD2D.m_Trimming){if (m_CanUseAxisAlignClip){m_Target->PopAxisAlignedClip();}else{m_Target->PopLayer();}}}bool Canvas::MeasureTextW(const std::wstring& str, const TextFormat& format, D2D1_SIZE_F& size){TextFormatD2D& formatD2D = (TextFormatD2D&)format;static std::wstring formatStr;formatStr = str;formatD2D.ApplyInlineCase(formatStr);const DWRITE_TEXT_METRICS metrics = formatD2D.GetMetrics(formatStr, !m_AccurateText);size.width = metrics.width;size.height = metrics.height;return true;}bool Canvas::MeasureTextLinesW(const std::wstring& str, const TextFormat& format, D2D1_SIZE_F& size, UINT32& lines){TextFormatD2D& formatD2D = (TextFormatD2D&)format;formatD2D.m_TextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_WRAP);static std::wstring formatStr;formatStr = str;formatD2D.ApplyInlineCase(formatStr);const DWRITE_TEXT_METRICS metrics = formatD2D.GetMetrics(formatStr, !m_AccurateText, size.width);size.width = metrics.width;size.height = metrics.height;lines = metrics.lineCount;if (size.height > 0.0f){// GDI+ draws multi-line text even though the last line may be clipped slightly at the// bottom. This is a workaround to emulate that behaviour.size.height += 1.0f;}else{// GDI+ compatibility: Zero height text has no visible lines.lines = 0U;}return true;}void Canvas::DrawBitmap(const D2DBitmap* bitmap, const D2D1_RECT_F& dstRect, const D2D1_RECT_F& srcRect){auto& segments = bitmap->m_Segments;for (auto seg : segments){const auto rSeg = seg.GetRect();D2D1_RECT_F rSrc = (rSeg.left < rSeg.right && rSeg.top < rSeg.bottom) ?D2D1::RectF(max(rSeg.left, srcRect.left),max(rSeg.top, srcRect.top),min(rSeg.right + rSeg.left, srcRect.right),min(rSeg.bottom + rSeg.top, srcRect.bottom)) :D2D1::RectF();if (rSrc.left == rSrc.right || rSrc.top == rSrc.bottom) continue;const D2D1_RECT_F rDst = D2D1::RectF((rSrc.left   - srcRect.left) / (srcRect.right  - srcRect.left) * (dstRect.right  - dstRect.left) + dstRect.left,(rSrc.top    - srcRect.top)  / (srcRect.bottom - srcRect.top)  * (dstRect.bottom - dstRect.top)  + dstRect.top,(rSrc.right  - srcRect.left) / (srcRect.right  - srcRect.left) * (dstRect.right  - dstRect.left) + dstRect.left,(rSrc.bottom - srcRect.top)  / (srcRect.bottom - srcRect.top)  * (dstRect.bottom - dstRect.top)  + dstRect.top);while (rSrc.top >= m_MaxBitmapSize){rSrc.bottom -= m_MaxBitmapSize;rSrc.top -= m_MaxBitmapSize;}while (rSrc.left >= m_MaxBitmapSize){rSrc.right -= m_MaxBitmapSize;rSrc.left -= m_MaxBitmapSize;}m_Target->DrawBitmap(seg.GetBitmap(), rDst, 1.0f, D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC, &rSrc);}}void Canvas::DrawTiledBitmap(const D2DBitmap* bitmap, const D2D1_RECT_F& dstRect, const D2D1_RECT_F& srcRect){const FLOAT width = (FLOAT)bitmap->m_Width;const FLOAT height = (FLOAT)bitmap->m_Height;FLOAT x = dstRect.left;FLOAT y = dstRect.top;while (y < dstRect.bottom){const FLOAT w = (dstRect.right - x) > width ? width : (dstRect.right - x);const FLOAT h = (dstRect.bottom - y) > height ? height : (dstRect.bottom - y);const auto dst = D2D1::RectF(x, y, x + w, y + h);const auto src = D2D1::RectF(0.0f, 0.0f, w, h);DrawBitmap(bitmap, dst, src);x += width;if (x >= dstRect.right && y < dstRect.bottom){x = dstRect.left;y += height;}}}void Canvas::DrawMaskedBitmap(const D2DBitmap* bitmap, const D2DBitmap* maskBitmap, const D2D1_RECT_F& dstRect,const D2D1_RECT_F& srcRect, const D2D1_RECT_F& srcRect2){if (!bitmap || !maskBitmap) return;// Create bitmap brush from original |bitmap|.Microsoft::WRL::ComPtr<ID2D1BitmapBrush1> brush;D2D1_BITMAP_BRUSH_PROPERTIES1 propertiesXClampYClamp = D2D1::BitmapBrushProperties1(D2D1_EXTEND_MODE_CLAMP,D2D1_EXTEND_MODE_CLAMP,D2D1_INTERPOLATION_MODE_HIGH_QUALITY_CUBIC);const FLOAT width = (FLOAT)bitmap->m_Width;const FLOAT height = (FLOAT)bitmap->m_Height;auto getRectSubRegion = [&width, &height](const D2D1_RECT_F& r1, const D2D1_RECT_F& r2) -> D2D1_RECT_F{return D2D1::RectF(r1.left / width * r2.right + r2.left,r1.top / height * r2.bottom + r2.top,(r1.right - r1.left) / width * r2.right,(r1.bottom - r1.top) / height * r2.bottom);};for (auto bseg : bitmap->m_Segments){const auto rSeg = bseg.GetRect();const auto rDst = getRectSubRegion(rSeg, dstRect);const auto rSrc = getRectSubRegion(rSeg, srcRect);FLOAT s2Width = srcRect2.right - srcRect2.left;FLOAT s2Height = srcRect2.bottom - srcRect2.top;// "Move" and "scale" the |bitmap| to match the destination.D2D1_MATRIX_3X2_F translateMask = D2D1::Matrix3x2F::Translation(-srcRect2.left, -srcRect2.top);D2D1_MATRIX_3X2_F translate = D2D1::Matrix3x2F::Translation(rDst.left, rDst.top);D2D1_MATRIX_3X2_F scale = D2D1::Matrix3x2F::Scale(D2D1::SizeF((rDst.right - rDst.left) / s2Width, (rDst.bottom - rDst.top) / s2Height));D2D1_BRUSH_PROPERTIES brushProps = D2D1::BrushProperties(1.0f, translateMask * scale * translate);HRESULT hr = m_Target->CreateBitmapBrush(bseg.GetBitmap(),propertiesXClampYClamp,brushProps,brush.ReleaseAndGetAddressOf());if (FAILED(hr)) return;const auto aaMode = m_Target->GetAntialiasMode();m_Target->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); // requiredfor (auto mseg : maskBitmap->m_Segments){const auto rmSeg = mseg.GetRect();const auto rmDst = getRectSubRegion(rmSeg, dstRect);const auto rmSrc = getRectSubRegion(rmSeg, srcRect);// If no overlap, don't drawif ((rmDst.left < (rDst.left + rDst.right) &&(rmDst.right + rmDst.left) > rDst.left &&rmDst.top > (rmDst.top + rmDst.bottom) &&(rmDst.top + rmDst.bottom) < rmDst.top)) continue;m_Target->FillOpacityMask(mseg.GetBitmap(),brush.Get(),D2D1_OPACITY_MASK_CONTENT_GRAPHICS,&rDst,&rSrc);}m_Target->SetAntialiasMode(aaMode);}}void Canvas::FillRectangle(const D2D1_RECT_F& rect, const D2D1_COLOR_F& color){Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> solidBrush;HRESULT hr = m_Target->CreateSolidColorBrush(color, solidBrush.GetAddressOf());if (SUCCEEDED(hr)){m_Target->FillRectangle(rect, solidBrush.Get());}}void Canvas::FillGradientRectangle(const D2D1_RECT_F& rect, const D2D1_COLOR_F& color1, const D2D1_COLOR_F& color2, const FLOAT& angle){// D2D requires 2 points to draw the gradient along where GDI+ just requires a rectangle. To// mimic GDI+ for SolidColor2, we have to find and swap the starting and ending points of where// the gradient touches edge of the bounding rectangle. Normally we would offset the ending// point by 180, but we do this on starting point for SolidColor2. This differs from the other// D2D gradient options below://  Gfx::TextInlineFormat_GradientColor::BuildGradientBrushes//  Gfx::Shape::CreateLinearGradientD2D1_POINT_2F start = Util::FindEdgePoint(angle + 180.0f, rect.left, rect.top, rect.right, rect.bottom);D2D1_POINT_2F end = Util::FindEdgePoint(angle, rect.left, rect.top, rect.right, rect.bottom);Microsoft::WRL::ComPtr<ID2D1GradientStopCollection> pGradientStops;D2D1_GRADIENT_STOP gradientStops[2];gradientStops[0].color = color1;gradientStops[0].position = 0.0f;gradientStops[1].color = color2;gradientStops[1].position = 1.0f;HRESULT hr = m_Target->CreateGradientStopCollection(gradientStops,2U,D2D1_GAMMA_2_2,D2D1_EXTEND_MODE_CLAMP,pGradientStops.GetAddressOf());if (FAILED(hr)) return;Microsoft::WRL::ComPtr<ID2D1LinearGradientBrush> brush;hr = m_Target->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(start, end),pGradientStops.Get(),brush.GetAddressOf());if (FAILED(hr)) return;m_Target->FillRectangle(rect, brush.Get());}void Canvas::DrawLine(const D2D1_COLOR_F& color, FLOAT x1, FLOAT y1, FLOAT x2, FLOAT y2, FLOAT strokeWidth){Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> solidBrush;HRESULT hr = m_Target->CreateSolidColorBrush(color, solidBrush.GetAddressOf());if (FAILED(hr)) return;m_Target->DrawLine(D2D1::Point2F(x1, y1), D2D1::Point2F(x2, y2), solidBrush.Get(), strokeWidth);}void Canvas::DrawGeometry(Shape& shape, int xPos, int yPos){D2D1_MATRIX_3X2_F worldTransform;m_Target->GetTransform(&worldTransform);m_Target->SetTransform(shape.GetShapeMatrix() *D2D1::Matrix3x2F::Translation((FLOAT)xPos, (FLOAT)yPos) *worldTransform);auto fill = shape.GetFillBrush(m_Target.Get());if (fill){m_Target->FillGeometry(shape.m_Shape.Get(), fill.Get());}auto stroke = shape.GetStrokeFillBrush(m_Target.Get());if (stroke){m_Target->DrawGeometry(shape.m_Shape.Get(),stroke.Get(),shape.m_StrokeWidth,shape.m_StrokeStyle.Get());}m_Target->SetTransform(worldTransform);}HRESULT Canvas::CreateRenderTarget(){HRESULT hr = E_FAIL;if (c_D2DDevice){c_D2DDevice->ClearResources();hr = c_D2DDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS,m_Target.ReleaseAndGetAddressOf());if (FAILED(hr)){hr = c_D2DDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE,m_Target.ReleaseAndGetAddressOf());}}// Hardware accelerated targets have a hard limit to the size of bitmaps they can support.// The size will depend on the D3D feature level of the driver used. The WARP software// renderer has a limit of 16MP (16*1024*1024 = 16777216).// https://docs.microsoft.com/en-us/windows/desktop/direct3d11/overviews-direct3d-11-devices-downlevel-intro#overview-for-each-feature-level// Max Texture Dimension// D3D_FEATURE_LEVEL_11_1 = 16348// D3D_FEATURE_LEVEL_11_0 = 16348// D3D_FEATURE_LEVEL_10_1 = 8192// D3D_FEATURE_LEVEL_10_0 = 8192// D3D_FEATURE_LEVEL_9_3  = 4096// D3D_FEATURE_LEVEL_9_2  = 2048// D3D_FEATURE_LEVEL_9_1  = 2048if (SUCCEEDED(hr)){m_MaxBitmapSize = m_Target->GetMaximumBitmapSize();}return hr;}bool Canvas::CreateTargetBitmap(UINT32 width, UINT32 height){HRESULT hr = m_SwapChain->GetBuffer(0U, IID_PPV_ARGS(m_BackBuffer.GetAddressOf()));if (FAILED(hr)) return LogComError(hr);D2D1_BITMAP_PROPERTIES1 bProps = D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED));hr = m_Target->CreateBitmapFromDxgiSurface(m_BackBuffer.Get(),&bProps,m_TargetBitmap.GetAddressOf());if (FAILED(hr)) return LogComError(hr);m_Target->SetTarget(m_TargetBitmap.Get());return true;}}  // namespace Gfx


То что надо:

Microsoft::WRL::ComPtr<ID3D11Device> Canvas::c_D3DDevice;Microsoft::WRL::ComPtr<ID3D11DeviceContext> Canvas::c_D3DContext;Microsoft::WRL::ComPtr<ID2D1Device> Canvas::c_D2DDevice;Microsoft::WRL::ComPtr<IDXGIDevice1> Canvas::c_DxgiDevice;Microsoft::WRL::ComPtr<ID2D1Factory1> Canvas::c_D2DFactory;Microsoft::WRL::ComPtr<IDWriteFactory1> Canvas::c_DWFactory;Microsoft::WRL::ComPtr<IWICImagingFactory> Canvas::c_WICFactory;...Ещё всякое

Добавим структуру Context в Common/Gfx/Canvas.h:

Context
...class Canvas{public:struct Context{Microsoft::WRL::ComPtr<ID3D11Device> D3DDevice;Microsoft::WRL::ComPtr<ID3D11DeviceContext> D3DContext;Microsoft::WRL::ComPtr<ID2D1Device> D2DDevice;Microsoft::WRL::ComPtr<IDXGIDevice1> DxgiDevice;Microsoft::WRL::ComPtr<ID2D1Factory1> D2DFactory;Microsoft::WRL::ComPtr<IDWriteFactory1> DWFactory;Microsoft::WRL::ComPtr<IWICImagingFactory> WICFactory;Microsoft::WRL::ComPtr<ID2D1DeviceContext> Target;Microsoft::WRL::ComPtr<IDXGISwapChain1> SwapChain;Microsoft::WRL::ComPtr<IDXGISurface1> BackBuffer;Microsoft::WRL::ComPtr<ID2D1Bitmap1> TargetBitmap;int W;int H;UINT32 MaxBitmapSize;bool IsDrawing;bool EnableDrawAfterGdi;bool AccurateText;bool TextAntiAliasing;bool CanUseAxisAlignClip;};Context GetContext();...};


Common/Canvas.cpp
...Canvas::Context Canvas::GetContext(){Context context;context.D2DDevice = c_D2DDevice;context.D2DFactory = c_D2DFactory;context.D3DContext = c_D3DContext;context.D3DDevice = c_D3DDevice;context.DWFactory = c_DWFactory;context.WICFactory = c_WICFactory;context.DxgiDevice = c_DxgiDevice;context.AccurateText = m_AccurateText;context.BackBuffer = m_BackBuffer;context.CanUseAxisAlignClip = m_CanUseAxisAlignClip;context.EnableDrawAfterGdi = m_EnableDrawAfterGdi;context.H = m_H;context.IsDrawing = m_IsDrawing;context.MaxBitmapSize = m_MaxBitmapSize;context.SwapChain = m_SwapChain;context.Target = m_Target;context.TargetBitmap = m_TargetBitmap;context.TextAntiAliasing = m_TextAntiAliasing;context.W = m_W;return context;}...


Создадим новый meter Canvas:

Library/MeterCanvas.h
#ifndef __METERCANVAS_H__#define __METERCANVAS_H__#include "Meter.h"class MeterCanvas : public Meter{public:MeterCanvas(Skin* skin, const WCHAR* name);UINT GetTypeID() override { return TypeID<MeterCanvas>(); }virtual bool Update();virtual bool Draw(Gfx::Canvas& canvas);bool HitTest(int x, int y);~MeterCanvas();};#endif


Library/MeterCanvas.cpp
#include "StdAfx.h"#include "MeterCanvas.h"#include "Logger.h"#include "windows.h"#include <iostream>#include "../Common/Gfx/Canvas.h"#include "../Common/Gfx/Util/D2DUtil.h"MeterCanvas::MeterCanvas(Skin* skin, const WCHAR* name) : Meter(skin, name){Meter::Initialize();}bool MeterCanvas::Update(){return Meter::Update();}bool MeterCanvas::Draw(Gfx::Canvas& canvas){return Meter::Draw(canvas);}bool MeterCanvas::HitTest(int x, int y){return Meter::HitTest(x, y);}MeterCanvas::~MeterCanvas(){}


Добавим возможность использования meter'а:

Library/Meter.cpp
#include "MeterCanvas.h"...Meter* Meter::Create(const WCHAR* meter, Skin* skin, const WCHAR* name){if (_wcsicmp(L"STRING", meter) == 0){return new MeterString(skin, name);}else if (_wcsicmp(L"IMAGE", meter) == 0){return new MeterImage(skin, name);}else if (_wcsicmp(L"HISTOGRAM", meter) == 0){return new MeterHistogram(skin, name);}else if (_wcsicmp(L"BAR", meter) == 0){return new MeterBar(skin, name);}else if (_wcsicmp(L"BITMAP", meter) == 0){return new MeterBitmap(skin, name);}else if (_wcsicmp(L"LINE", meter) == 0){return new MeterLine(skin, name);}else if (_wcsicmp(L"ROUNDLINE", meter) == 0){return new MeterRoundLine(skin, name);}else if (_wcsicmp(L"ROTATOR", meter) == 0){return new MeterRotator(skin, name);}else if (_wcsicmp(L"BUTTON", meter) == 0){return new MeterButton(skin, name);}else if (_wcsicmp(L"SHAPE", meter) == 0){return new MeterShape(skin, name);}else if (_wcsicmp(L"CANVAS", meter) == 0){return new MeterCanvas(skin, name);}LogErrorF(skin, L"Meter=%s is not valid in [%s]", meter, name);return nullptr;}


Попробуем что-то нарисовать:

Meter.ini
[Rainmeter]Update=1000[Text]Meter=CANVASW=100H=100


Library/MeterCanvas.cpp
bool MeterCanvas::Draw(Gfx::Canvas& canvas){Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> brush;auto target = canvas.GetContext().Target;target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), brush.GetAddressOf());target->FillRectangle(D2D1::RectF(100, 100), brush.Get());return Meter::Draw(canvas);}


Работает:

image

Теперь напишем dll для отрисовки:

Draw.h
#include <d2d1_1.h>#include <wrl\client.h>#include "Context.h"#pragma once#ifdef LIBRARY_EXPORTS#define EXPORT_PLUGIN#else#define EXPORT_PLUGIN __declspec(dllexport)#endifextern "C" EXPORT_PLUGIN void Draw(Context context);


Context.h
#pragma once#include <string>#include <d2d1_1.h>#include <dwrite_1.h>#include <wincodec.h>#include <wrl/client.h>#include <d3d11.h>#include <DXGI1_2.h>struct Context{Microsoft::WRL::ComPtr<ID3D11Device> D3DDevice;Microsoft::WRL::ComPtr<ID3D11DeviceContext> D3DContext;Microsoft::WRL::ComPtr<ID2D1Device> D2DDevice;Microsoft::WRL::ComPtr<IDXGIDevice1> DxgiDevice;Microsoft::WRL::ComPtr<ID2D1Factory1> D2DFactory;Microsoft::WRL::ComPtr<IDWriteFactory1> DWFactory;Microsoft::WRL::ComPtr<IWICImagingFactory> WICFactory;Microsoft::WRL::ComPtr<ID2D1DeviceContext> Target;Microsoft::WRL::ComPtr<IDXGISwapChain1> SwapChain;Microsoft::WRL::ComPtr<IDXGISurface1> BackBuffer;Microsoft::WRL::ComPtr<ID2D1Bitmap1> TargetBitmap;int W;int H;UINT32 MaxBitmapSize;bool IsDrawing;bool EnableDrawAfterGdi;bool AccurateText;bool TextAntiAliasing;bool CanUseAxisAlignClip;};


Draw.cpp
#include "pch.h"#include "Draw.h"#pragma comment(lib,"d2d1.lib")EXPORT_PLUGIN void Draw(Context context){Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> brush;auto target = context.Target;target->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), brush.GetAddressOf());target->FillRectangle(D2D1::RectF(100, 100), brush.Get());}


Компилируем Draw.dll:

Library/MeterCanvas.cpp
HMODULE hlib;void (*draw)(Gfx::Canvas::Context context);MeterCanvas::MeterCanvas(Skin* skin, const WCHAR* name) : Meter(skin, name){hlib = LoadLibrary(L"Draw.dll");(FARPROC&)draw = GetProcAddress(hlib, "Draw");Meter::Initialize();}bool MeterCanvas::Draw(Gfx::Canvas& canvas){if (draw != nullptr)draw(canvas.GetContext());return Meter::Draw(canvas);}MeterCanvas::~MeterCanvas(){FreeLibrary(hlib);}


Тоже работает:

image

Допишем код, для выбора dll'ки пользователем:

std::wstring dll;...MeterCanvas::MeterCanvas(Skin* skin, const WCHAR* name) : Meter(skin, name){dll = skin->GetParser().GetValue(name, L"Dll", L"");hlib = LoadLibrary(dll.c_str());(FARPROC&)draw = GetProcAddress(hlib, "Draw");Meter::Initialize();}


Meter.ini
[Rainmeter]Update=1000[Text]Meter=CANVASDll=Draw.dllW=100H=100

Почему бы не сделать отдельный bang?
Сделаем.

Добавим Action в список bang'ов.

Library/CommandHandler.h:

enum class Bang{Action,...}

Library/CommandHandler.cpp
const BangInfo s_Bangs[] ={{Bang::Action, L"Action", 1},...}

Library/Skin.cpp:

void Skin::DoBang(Bang bang, const std::vector<std::wstring>& args){switch (bang){case Bang::Action:GetMeters()[0]->Action(args[0]);break;...}

Library/Meter.h:

class __declspec(novtable) Meter : public Section{public:virtual void Action(std::wstring arg) {};...}

Library/MeterCanvas.h:

class MeterCanvas : public Meter{public:virtual void Action(std::wstring arg) {};...}

Meter.ini:

[Rainmeter]Update=1000[Text]Meter=CANVASDll=Draw.dllW=100H=100LeftMouseDownAction=!Action Test

Обработка
Library/MeterCanvas.cpp:

...void MeterCanvas::Action(std::wstring arg){LogDebug(arg.c_str());}


image

Из dll
Library/MeterCanvas.cpp
void (*action)(std::wstring arg);...MeterCanvas::MeterCanvas(Skin* skin, const WCHAR* name) : Meter(skin, name){hlib = LoadLibrary(L"Draw.dll");(FARPROC&)draw = GetProcAddress(hlib, "Draw");(FARPROC&)action = GetProcAddress(hlib, "Action");Meter::Initialize();}void MeterCanvas::Action(std::wstring arg){if (action != nullptr)action(arg);}

Draw.h
#include <d2d1_1.h>#include <wrl\client.h>#include <math.h>#include "Context.h"#pragma once#ifdef LIBRARY_EXPORTS#define EXPORT_PLUGIN#else#define EXPORT_PLUGIN __declspec(dllexport)#endifextern "C" EXPORT_PLUGIN void Draw(Context context);extern "C" EXPORT_PLUGIN void Action(std::wstring arg);


Также добавим действие при загрузке:

Скрытый текст
Draw.h
#include <d2d1_1.h>#include <wrl\client.h>#include <math.h>#include "Context.h"#pragma once#ifdef LIBRARY_EXPORTS#define EXPORT_PLUGIN#else#define EXPORT_PLUGIN __declspec(dllexport)#endifextern "C" EXPORT_PLUGIN void Init(Context context);extern "C" EXPORT_PLUGIN void Draw(Context context);extern "C" EXPORT_PLUGIN void Action(std::wstring arg);

Library/MeterCanvas.cpp
void (*init)(Gfx::Canvas::Context context);...MeterCanvas::MeterCanvas(Skin* skin, const WCHAR* name) : Meter(skin, name){dll = skin->GetParser().GetValue(name, L"Dll", L"");hlib = LoadLibrary(dll.c_str());(FARPROC&)draw = GetProcAddress(hlib, "Draw");(FARPROC&)action = GetProcAddress(hlib, "Action");(FARPROC&)init = GetProcAddress(hlib, "Init");                         init(skin->GetCanvas().GetContext());Meter::Initialize();}


Почти безграничная власть!

Но чего-то не хватает

Где же обработка нажатий на клаву?

А вот она:



Но оно не работает.
Не беда.

Заходим в rainmeter-master\x32-Debug\Plugins
Нам нужны еще файлы .pdb и .ilk

Качаем.


Компилируем, кидаем к плагинам.

Вот и всё.
Вы можете вообще всё.

В качестве бонуса пример:

Видео

Исходники
Источник: habr.com
К списку статей
Опубликовано: 26.07.2020 14:07:00
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

C++

Разработка под windows

Rainmeter

Windows

Direct2d

Категории

Последние комментарии

© 2006-2020, personeltest.ru