diff --git a/src/modules/MeasureTool/MeasureToolCore/BoundsToolOverlayUI.cpp b/src/modules/MeasureTool/MeasureToolCore/BoundsToolOverlayUI.cpp index 349545be0f..a6dd7e7595 100644 --- a/src/modules/MeasureTool/MeasureToolCore/BoundsToolOverlayUI.cpp +++ b/src/modules/MeasureTool/MeasureToolCore/BoundsToolOverlayUI.cpp @@ -114,6 +114,7 @@ namespace L"%.0f × %.0f", width, height); + std::optional crossSymbolPos = wcsstr(text.buffer.data(), L" ") - text.buffer.data() + 1; commonState.overlayBoxText.Access([&](OverlayBoxText& v) { v = text; @@ -129,6 +130,7 @@ namespace d2dState.DrawTextBox(text.buffer.data(), textLen, + crossSymbolPos, cornerX, cornerY, screenQuadrantAware, diff --git a/src/modules/MeasureTool/MeasureToolCore/D2DState.cpp b/src/modules/MeasureTool/MeasureToolCore/D2DState.cpp index b9786c9251..4bf2462f29 100644 --- a/src/modules/MeasureTool/MeasureToolCore/D2DState.cpp +++ b/src/modules/MeasureTool/MeasureToolCore/D2DState.cpp @@ -68,10 +68,13 @@ D2DState::D2DState(HWND overlayWindow, std::vector solidBrushesCol winrt::check_hresult(deviceContext->CreateEffect(CLSID_D2D12DAffineTransform, &affineTransformEffect)); affineTransformEffect->SetInputEffect(0, shadowEffect.get()); + + textRenderer = winrt::make_self(d2dFactory, rt, solidBrushes[Brush::foreground]); } void D2DState::DrawTextBox(const wchar_t* text, const uint32_t textLen, + const std::optional halfOpaqueSymbolPos, const float centerX, const float centerY, const bool screenQuadrantAware, @@ -147,5 +150,13 @@ void D2DState::DrawTextBox(const wchar_t* text, textBoxRect.rect.right -= TEXT_BOX_PADDING; // Draw text & its box rt->FillRoundedRectangle(textBoxRect, solidBrushes[Brush::background].get()); - rt->DrawTextLayout(D2D1_POINT_2F{ .x = textRect.left, .y = textRect.top }, textLayout.get(), solidBrushes[Brush::foreground].get()); + + if (halfOpaqueSymbolPos.has_value()) + { + DWRITE_TEXT_RANGE textRange = { static_cast(*halfOpaqueSymbolPos), 2 }; + auto opacityEffect = winrt::make_self(); + opacityEffect->alpha = consts::CROSS_OPACITY; + winrt::check_hresult(textLayout->SetDrawingEffect(opacityEffect.get(), textRange)); + } + winrt::check_hresult(textLayout->Draw(nullptr, textRenderer.get(), textRect.left, textRect.top)); } diff --git a/src/modules/MeasureTool/MeasureToolCore/D2DState.h b/src/modules/MeasureTool/MeasureToolCore/D2DState.h index 4cd102bbf0..b2ff0911c5 100644 --- a/src/modules/MeasureTool/MeasureToolCore/D2DState.h +++ b/src/modules/MeasureTool/MeasureToolCore/D2DState.h @@ -1,11 +1,14 @@ #pragma once +#include #include #include #include #include +#include + enum Brush : size_t { line, @@ -21,14 +24,17 @@ struct D2DState wil::com_ptr rt; wil::com_ptr bitmapRt; wil::com_ptr textFormat; + winrt::com_ptr textRenderer; std::vector> solidBrushes; wil::com_ptr shadowEffect; wil::com_ptr affineTransformEffect; + float dpiScale = 1.f; D2DState(HWND window, std::vector solidBrushesColors); void DrawTextBox(const wchar_t* text, const uint32_t textLen, + const std::optional halfOpaqueSymbolPos, const float centerX, const float centerY, const bool screenQuadrantAware, diff --git a/src/modules/MeasureTool/MeasureToolCore/MeasureToolOverlayUI.cpp b/src/modules/MeasureTool/MeasureToolCore/MeasureToolOverlayUI.cpp index 62c862a584..3cbec96e99 100644 --- a/src/modules/MeasureTool/MeasureToolCore/MeasureToolOverlayUI.cpp +++ b/src/modules/MeasureTool/MeasureToolCore/MeasureToolOverlayUI.cpp @@ -176,6 +176,7 @@ void DrawMeasureToolTick(const CommonState& commonState, uint32_t measureStringBufLen = 0; OverlayBoxText text; + std::optional crossSymbolPos; switch (mts.mode) { case MeasureToolState::Mode::Cross: @@ -184,6 +185,7 @@ void DrawMeasureToolTick(const CommonState& commonState, L"%.0f × %.0f", hMeasure, vMeasure); + crossSymbolPos = wcsstr(text.buffer.data(), L" ") - text.buffer.data() + 1; break; case MeasureToolState::Mode::Vertical: measureStringBufLen = swprintf_s(text.buffer.data(), @@ -205,6 +207,7 @@ void DrawMeasureToolTick(const CommonState& commonState, d2dState.DrawTextBox(text.buffer.data(), measureStringBufLen, + crossSymbolPos, static_cast(cursorPos.x), static_cast(cursorPos.y), true, diff --git a/src/modules/MeasureTool/MeasureToolCore/PerGlyphOpacityTextRender.cpp b/src/modules/MeasureTool/MeasureToolCore/PerGlyphOpacityTextRender.cpp new file mode 100644 index 0000000000..d13955cdf3 --- /dev/null +++ b/src/modules/MeasureTool/MeasureToolCore/PerGlyphOpacityTextRender.cpp @@ -0,0 +1,148 @@ +#include "pch.h" + +#include "PerGlyphOpacityTextRender.h" + +PerGlyphOpacityTextRender::PerGlyphOpacityTextRender( + wil::com_ptr pD2DFactory, + wil::com_ptr rt, + wil::com_ptr baseBrush) : + _pD2DFactory{ pD2DFactory }, + _rt{ rt }, + _baseBrush{ baseBrush } +{ +} + +HRESULT __stdcall PerGlyphOpacityTextRender::DrawGlyphRun(void* /*clientDrawingContext*/, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_MEASURING_MODE measuringMode, + _In_ const DWRITE_GLYPH_RUN* glyphRun, + _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/, + IUnknown* clientDrawingEffect) noexcept +{ + HRESULT hr = S_OK; + if (!clientDrawingEffect) + { + _rt->DrawGlyphRun(D2D1_POINT_2F{ .x = baselineOriginX, .y = baselineOriginY }, glyphRun, _baseBrush.get(), measuringMode); + return hr; + } + + // Create the path geometry. + wil::com_ptr pathGeometry; + hr = _pD2DFactory->CreatePathGeometry(&pathGeometry); + + // Write to the path geometry using the geometry sink. + ID2D1GeometrySink* pSink = nullptr; + if (SUCCEEDED(hr)) + { + hr = pathGeometry->Open(&pSink); + } + + // Get the glyph run outline geometries back from DirectWrite and place them within the + // geometry sink. + if (SUCCEEDED(hr)) + { + hr = glyphRun->fontFace->GetGlyphRunOutline( + glyphRun->fontEmSize, + glyphRun->glyphIndices, + glyphRun->glyphAdvances, + glyphRun->glyphOffsets, + glyphRun->glyphCount, + glyphRun->isSideways, + glyphRun->bidiLevel % 2, + pSink); + } + + // Close the geometry sink + if (SUCCEEDED(hr)) + { + hr = pSink->Close(); + } + + // Initialize a matrix to translate the origin of the glyph run. + D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F( + 1.0f, 0.0f, 0.0f, 1.0f, baselineOriginX, baselineOriginY); + + // Create the transformed geometry + wil::com_ptr pTransformedGeometry; + if (SUCCEEDED(hr)) + { + hr = _pD2DFactory->CreateTransformedGeometry(pathGeometry.get(), &matrix, &pTransformedGeometry); + } + + float prevOpacity = _baseBrush->GetOpacity(); + OpacityEffect* opacityEffect = nullptr; + hr = clientDrawingEffect->QueryInterface(__uuidof(IDrawingEffect), reinterpret_cast(&opacityEffect)); + if (SUCCEEDED(hr)) + { + _baseBrush->SetOpacity(opacityEffect->alpha); + } + + if (SUCCEEDED(hr)) + { + _rt->DrawGeometry(pTransformedGeometry.get(), _baseBrush.get()); + _rt->FillGeometry(pTransformedGeometry.get(), _baseBrush.get()); + _baseBrush->SetOpacity(prevOpacity); + } + + return hr; +} + +HRESULT __stdcall PerGlyphOpacityTextRender::DrawUnderline(void* /*clientDrawingContext*/, + FLOAT /*baselineOriginX*/, + FLOAT /*baselineOriginY*/, + _In_ const DWRITE_UNDERLINE* /*underline*/, + IUnknown* /*clientDrawingEffect*/) noexcept +{ + return E_NOTIMPL; +} + +HRESULT __stdcall PerGlyphOpacityTextRender::DrawStrikethrough(void* /*clientDrawingContext*/, + FLOAT /*baselineOriginX*/, + FLOAT /*baselineOriginY*/, + _In_ const DWRITE_STRIKETHROUGH* /*strikethrough*/, + IUnknown* /*clientDrawingEffect*/) noexcept +{ + return E_NOTIMPL; +} + +HRESULT __stdcall PerGlyphOpacityTextRender::DrawInlineObject(void* /*clientDrawingContext*/, + FLOAT /*originX*/, + FLOAT /*originY*/, + IDWriteInlineObject* /*inlineObject*/, + BOOL /*isSideways*/, + BOOL /*isRightToLeft*/, + IUnknown* /*clientDrawingEffect*/) noexcept +{ + return E_NOTIMPL; +} + +HRESULT __stdcall PerGlyphOpacityTextRender::IsPixelSnappingDisabled(void* /*clientDrawingContext*/, BOOL* isDisabled) noexcept +{ + RETURN_HR_IF_NULL(E_INVALIDARG, isDisabled); + + *isDisabled = true; + return S_OK; +} + +HRESULT __stdcall PerGlyphOpacityTextRender::GetCurrentTransform(void* /*clientDrawingContext*/, DWRITE_MATRIX* transform) noexcept +{ + D2D1_MATRIX_3X2_F d2d1Matrix{}; + + _rt->GetTransform(&d2d1Matrix); + + transform->dx = d2d1Matrix.dx; + transform->dy = d2d1Matrix.dy; + transform->m11 = d2d1Matrix.m11; + transform->m12 = d2d1Matrix.m12; + transform->m21 = d2d1Matrix.m21; + transform->m22 = d2d1Matrix.m22; + + return S_OK; +} + +HRESULT __stdcall PerGlyphOpacityTextRender::GetPixelsPerDip(void* /*clientDrawingContext*/, FLOAT* pixelsPerDip) noexcept +{ + *pixelsPerDip = 1.f; + return S_OK; +} diff --git a/src/modules/MeasureTool/MeasureToolCore/PerGlyphOpacityTextRender.h b/src/modules/MeasureTool/MeasureToolCore/PerGlyphOpacityTextRender.h new file mode 100644 index 0000000000..ad65fcb523 --- /dev/null +++ b/src/modules/MeasureTool/MeasureToolCore/PerGlyphOpacityTextRender.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include + +struct __declspec(uuid("{01557C9F-E3DD-4C28-AE64-E731EAB479CC}")) IDrawingEffect : IUnknown +{ +}; + +struct OpacityEffect : winrt::implements +{ + float alpha = 1.f; +}; + +struct PerGlyphOpacityTextRender : winrt::implements +{ + wil::com_ptr _pD2DFactory; + wil::com_ptr _rt; + wil::com_ptr _baseBrush; + + PerGlyphOpacityTextRender( + wil::com_ptr pD2DFactory, + wil::com_ptr rt, + wil::com_ptr baseBrush); + HRESULT __stdcall DrawGlyphRun(void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_MEASURING_MODE measuringMode, + _In_ const DWRITE_GLYPH_RUN* glyphRun, + _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription, + IUnknown* clientDrawingEffect) noexcept override; + HRESULT __stdcall DrawUnderline(void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + _In_ const DWRITE_UNDERLINE* underline, + IUnknown* clientDrawingEffect) noexcept override; + HRESULT __stdcall DrawStrikethrough(void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + _In_ const DWRITE_STRIKETHROUGH* strikethrough, + IUnknown* clientDrawingEffect) noexcept override; + HRESULT __stdcall DrawInlineObject(void* clientDrawingContext, + FLOAT originX, + FLOAT originY, + IDWriteInlineObject* inlineObject, + BOOL isSideways, + BOOL isRightToLeft, + IUnknown* clientDrawingEffect) noexcept override; + HRESULT __stdcall IsPixelSnappingDisabled(void* clientDrawingContext, BOOL* isDisabled) noexcept override; + HRESULT __stdcall GetCurrentTransform(void* clientDrawingContext, DWRITE_MATRIX* transform) noexcept override; + HRESULT __stdcall GetPixelsPerDip(void* clientDrawingContext, FLOAT* pixelsPerDip) noexcept override; +}; diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj index 06a181044a..8c139815e0 100644 --- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj +++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj @@ -77,6 +77,7 @@ + PowerToys.MeasureToolCore.idl @@ -97,6 +98,7 @@ + PowerToys.MeasureToolCore.idl diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj.filters b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj.filters index b922daa4f6..4c266029c7 100644 --- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj.filters +++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj.filters @@ -15,6 +15,8 @@ + + @@ -29,6 +31,8 @@ + + diff --git a/src/modules/MeasureTool/MeasureToolCore/constants.h b/src/modules/MeasureTool/MeasureToolCore/constants.h index fa9b4d4bf0..ea69c74ef7 100644 --- a/src/modules/MeasureTool/MeasureToolCore/constants.h +++ b/src/modules/MeasureTool/MeasureToolCore/constants.h @@ -13,9 +13,10 @@ namespace consts constexpr inline float FEET_HALF_LENGTH = 2.f; constexpr inline float SHADOW_OPACITY = .4f; constexpr inline float SHADOW_RADIUS = 6.f; - const inline auto SHADOW_OFFSET = 5.f; + constexpr inline float SHADOW_OFFSET = 5.f; + constexpr inline float CROSS_OPACITY = .25f; /* Offset to not try not to use the cursor immediate pixels in measuring, but it seems only necessary for continuous mode. */ constexpr inline long CURSOR_OFFSET_AMOUNT_X = 4; constexpr inline long CURSOR_OFFSET_AMOUNT_Y = 4; -} \ No newline at end of file +}