/
githubmirror
/
PowerToys
Обзор
Документация
Войти
/
githubmirror
/
PowerToys
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/modules/MeasureTool/MeasureToolCore/D2DState.cpp
178 строк
8 KB
Wenjian Chern
[ScreenRuler]Add setting to show the measurement in an extra unit (#35887)
06 дек 2024, 01:02
Не верифицирован
06 дек 2024, 01:02
09ce610
Код
Авторство
О чём код?
#include "pch.h" #include "constants.h" #include "D2DState.h" #include "DxgiAPI.h" #include <common/Display/dpi_aware.h> #include <ToolState.h> namespace { void DetermineScreenQuadrant(const HWND window, long x, long y, bool& inLeftHalf, bool& inTopHalf) { RECT windowRect{}; GetWindowRect(window, &windowRect); const long w = windowRect.right - windowRect.left; const long h = windowRect.bottom - windowRect.top; inLeftHalf = x < w / 2; inTopHalf = y < h / 2; } } D2DState::D2DState(const DxgiAPI* dxgi, HWND window, std::vector<D2D1::ColorF> solidBrushesColors) { dxgiAPI = dxgi; unsigned dpi = DPIAware::DEFAULT_DPI; DPIAware::GetScreenDPIForWindow(window, dpi); dpiScale = dpi / static_cast<float>(DPIAware::DEFAULT_DPI); dxgiWindowState = dxgiAPI->CreateD2D1RenderTarget(window); winrt::check_hresult(dxgiWindowState.rt->CreateCompatibleRenderTarget(bitmapRt.put())); winrt::check_hresult(dxgiAPI->writeFactory->CreateTextFormat(L"Segoe UI Variable Text", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, consts::FONT_SIZE * dpiScale, L"en-US", textFormat.put())); winrt::check_hresult(textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); winrt::check_hresult(textFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER)); winrt::check_hresult(textFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP)); solidBrushes.resize(solidBrushesColors.size()); for (size_t i = 0; i < solidBrushes.size(); ++i) { winrt::check_hresult(dxgiWindowState.rt->CreateSolidColorBrush(solidBrushesColors[i], solidBrushes[i].put())); } const auto deviceContext = dxgiWindowState.rt.as<ID2D1DeviceContext>(); winrt::check_hresult(deviceContext->CreateEffect(CLSID_D2D1Shadow, shadowEffect.put())); winrt::check_hresult(shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, consts::SHADOW_RADIUS)); winrt::check_hresult(shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, D2D1::ColorF(0.f, 0.f, 0.f, consts::SHADOW_OPACITY))); winrt::check_hresult(deviceContext->CreateEffect(CLSID_D2D12DAffineTransform, affineTransformEffect.put())); affineTransformEffect->SetInputEffect(0, shadowEffect.get()); textRenderer = winrt::make_self<PerGlyphOpacityTextRender>(dxgi->d2dFactory2, dxgiWindowState.rt, solidBrushes[Brush::foreground]); } void D2DState::DrawTextBox(const wchar_t* text, const size_t textLen, const size_t halfOpaqueSymbolPos[2], const D2D_POINT_2F center, const bool screenQuadrantAware, const HWND window) const { wil::com_ptr<IDWriteTextLayout> textLayout; winrt::check_hresult( dxgiAPI->writeFactory->CreateTextLayout(text, static_cast<uint32_t>(textLen), textFormat.get(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), &textLayout)); DWRITE_TEXT_METRICS textMetrics = {}; winrt::check_hresult(textLayout->GetMetrics(&textMetrics)); const float lineHeight = textMetrics.height / (textMetrics.lineCount ? textMetrics.lineCount : 1); textMetrics.width += lineHeight; textMetrics.height += lineHeight * .5f; winrt::check_hresult(textLayout->SetMaxWidth(textMetrics.width)); winrt::check_hresult(textLayout->SetMaxHeight(textMetrics.height)); D2D1_RECT_F textRect{ .left = center.x - textMetrics.width / 2.f, .top = center.y - textMetrics.height / 2.f, .right = center.x + textMetrics.width / 2.f, .bottom = center.y + textMetrics.height / 2.f }; const float SHADOW_OFFSET = consts::SHADOW_OFFSET * dpiScale; if (screenQuadrantAware) { bool cursorInLeftScreenHalf = false; bool cursorInTopScreenHalf = false; DetermineScreenQuadrant(window, static_cast<long>(center.x), static_cast<long>(center.y), cursorInLeftScreenHalf, cursorInTopScreenHalf); float textQuadrantOffsetX = textMetrics.width / 2.f + SHADOW_OFFSET; float textQuadrantOffsetY = textMetrics.height / 2.f + SHADOW_OFFSET; if (!cursorInLeftScreenHalf) textQuadrantOffsetX *= -1.f; if (!cursorInTopScreenHalf) textQuadrantOffsetY *= -1.f; textRect.left += textQuadrantOffsetX; textRect.right += textQuadrantOffsetX; textRect.top += textQuadrantOffsetY; textRect.bottom += textQuadrantOffsetY; } // Draw shadow bitmapRt->BeginDraw(); bitmapRt->Clear(D2D1::ColorF(0.f, 0.f, 0.f, 0.f)); D2D1_ROUNDED_RECT textBoxRect; textBoxRect.radiusX = textBoxRect.radiusY = consts::TEXT_BOX_CORNER_RADIUS * dpiScale; textBoxRect.rect.bottom = textRect.bottom; textBoxRect.rect.top = textRect.top; textBoxRect.rect.left = textRect.left; textBoxRect.rect.right = textRect.right; bitmapRt->FillRoundedRectangle(textBoxRect, solidBrushes[Brush::border].get()); bitmapRt->EndDraw(); wil::com_ptr<ID2D1Bitmap> rtBitmap; bitmapRt->GetBitmap(&rtBitmap); shadowEffect->SetInput(0, rtBitmap.get()); const auto shadowMatrix = D2D1::Matrix3x2F::Translation(SHADOW_OFFSET, SHADOW_OFFSET); winrt::check_hresult(affineTransformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, shadowMatrix)); auto deviceContext = dxgiWindowState.rt.as<ID2D1DeviceContext>(); deviceContext->DrawImage(affineTransformEffect.get(), D2D1_INTERPOLATION_MODE_LINEAR); // Draw text box border rectangle dxgiWindowState.rt->DrawRoundedRectangle(textBoxRect, solidBrushes[Brush::border].get()); const float TEXT_BOX_PADDING = 1.f * dpiScale; textBoxRect.rect.bottom -= TEXT_BOX_PADDING; textBoxRect.rect.top += TEXT_BOX_PADDING; textBoxRect.rect.left += TEXT_BOX_PADDING; textBoxRect.rect.right -= TEXT_BOX_PADDING; // Draw text & its box dxgiWindowState.rt->FillRoundedRectangle(textBoxRect, solidBrushes[Brush::background].get()); if (halfOpaqueSymbolPos[0] > 0) { DWRITE_TEXT_RANGE textRange = { static_cast<uint32_t>(*halfOpaqueSymbolPos), 2 }; auto opacityEffect = winrt::make_self<OpacityEffect>(); opacityEffect->alpha = consts::CROSS_OPACITY; winrt::check_hresult(textLayout->SetDrawingEffect(opacityEffect.get(), textRange)); if (halfOpaqueSymbolPos[1] > halfOpaqueSymbolPos[0]) { textRange = { static_cast<uint32_t>(halfOpaqueSymbolPos[1]), 2 }; winrt::check_hresult(textLayout->SetDrawingEffect(opacityEffect.get(), textRange)); } } winrt::check_hresult(textLayout->Draw(nullptr, textRenderer.get(), textRect.left, textRect.top)); } void D2DState::ToggleAliasedLinesMode(const bool enabled) const { if (enabled) { // Draw lines in the middle of a pixel to avoid bleeding, since [0,0] pixel is // a rectangle filled from (0,0) to (1,1) and the lines use thickness = 1. dxgiWindowState.rt->SetTransform(D2D1::Matrix3x2F::Translation(.5f, .5f)); dxgiWindowState.rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); } else { dxgiWindowState.rt->SetTransform(D2D1::Matrix3x2F::Identity()); dxgiWindowState.rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); } }