implement custom renderer and make × semiopaque

This commit is contained in:
Andrey Nekrasov
2022-08-24 00:28:20 +02:00
parent 78d4f66669
commit 9128ca63c9
9 changed files with 234 additions and 3 deletions

View File

@@ -114,6 +114,7 @@ namespace
L"%.0f × %.0f",
width,
height);
std::optional<size_t> 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,

View File

@@ -68,10 +68,13 @@ D2DState::D2DState(HWND overlayWindow, std::vector<D2D1::ColorF> solidBrushesCol
winrt::check_hresult(deviceContext->CreateEffect(CLSID_D2D12DAffineTransform, &affineTransformEffect));
affineTransformEffect->SetInputEffect(0, shadowEffect.get());
textRenderer = winrt::make_self<PerGlyphOpacityTextRender>(d2dFactory, rt, solidBrushes[Brush::foreground]);
}
void D2DState::DrawTextBox(const wchar_t* text,
const uint32_t textLen,
const std::optional<size_t> 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<uint32_t>(*halfOpaqueSymbolPos), 2 };
auto opacityEffect = winrt::make_self<OpacityEffect>();
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));
}

View File

@@ -1,11 +1,14 @@
#pragma once
#include <optional>
#include <vector>
#include <d2d1_3.h>
#include <wil/com.h>
#include <windef.h>
#include <PerGlyphOpacityTextRender.h>
enum Brush : size_t
{
line,
@@ -21,14 +24,17 @@ struct D2DState
wil::com_ptr<ID2D1HwndRenderTarget> rt;
wil::com_ptr<ID2D1BitmapRenderTarget> bitmapRt;
wil::com_ptr<IDWriteTextFormat> textFormat;
winrt::com_ptr<PerGlyphOpacityTextRender> textRenderer;
std::vector<wil::com_ptr<ID2D1SolidColorBrush>> solidBrushes;
wil::com_ptr<ID2D1Effect> shadowEffect;
wil::com_ptr<ID2D1Effect> affineTransformEffect;
float dpiScale = 1.f;
D2DState(HWND window, std::vector<D2D1::ColorF> solidBrushesColors);
void DrawTextBox(const wchar_t* text,
const uint32_t textLen,
const std::optional<size_t> halfOpaqueSymbolPos,
const float centerX,
const float centerY,
const bool screenQuadrantAware,

View File

@@ -176,6 +176,7 @@ void DrawMeasureToolTick(const CommonState& commonState,
uint32_t measureStringBufLen = 0;
OverlayBoxText text;
std::optional<size_t> 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<float>(cursorPos.x),
static_cast<float>(cursorPos.y),
true,

View File

@@ -0,0 +1,148 @@
#include "pch.h"
#include "PerGlyphOpacityTextRender.h"
PerGlyphOpacityTextRender::PerGlyphOpacityTextRender(
wil::com_ptr<ID2D1Factory> pD2DFactory,
wil::com_ptr<ID2D1HwndRenderTarget> rt,
wil::com_ptr<ID2D1SolidColorBrush> 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<ID2D1PathGeometry> 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<ID2D1TransformedGeometry> 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<void**>(&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;
}

View File

@@ -0,0 +1,54 @@
#pragma once
#include <winrt/base.h>
#include <wil/resource.h>
#include <Windows.h>
#include <dwrite.h>
struct __declspec(uuid("{01557C9F-E3DD-4C28-AE64-E731EAB479CC}")) IDrawingEffect : IUnknown
{
};
struct OpacityEffect : winrt::implements<OpacityEffect, IDrawingEffect>
{
float alpha = 1.f;
};
struct PerGlyphOpacityTextRender : winrt::implements<PerGlyphOpacityTextRender, IDWriteTextRenderer>
{
wil::com_ptr<ID2D1Factory> _pD2DFactory;
wil::com_ptr<ID2D1HwndRenderTarget> _rt;
wil::com_ptr<ID2D1SolidColorBrush> _baseBrush;
PerGlyphOpacityTextRender(
wil::com_ptr<ID2D1Factory> pD2DFactory,
wil::com_ptr<ID2D1HwndRenderTarget> rt,
wil::com_ptr<ID2D1SolidColorBrush> 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;
};

View File

@@ -77,6 +77,7 @@
<ClInclude Include="constants.h" />
<ClInclude Include="D2DState.h" />
<ClInclude Include="MeasureToolOverlayUI.h" />
<ClInclude Include="PerGlyphOpacityTextRender.h" />
<ClInclude Include="Settings.h" />
<ClInclude Include="PowerToys.MeasureToolCore.h">
<DependentUpon>PowerToys.MeasureToolCore.idl</DependentUpon>
@@ -97,6 +98,7 @@
<ClCompile Include="EdgeDetection.cpp" />
<ClCompile Include="MeasureToolOverlayUI.cpp" />
<ClCompile Include="OverlayUI.cpp" />
<ClCompile Include="PerGlyphOpacityTextRender.cpp" />
<ClCompile Include="PowerToys.MeasureToolCore.cpp">
<DependentUpon>PowerToys.MeasureToolCore.idl</DependentUpon>
</ClCompile>

View File

@@ -15,6 +15,8 @@
<ClCompile Include="BoundsToolOverlayUI.cpp" />
<ClCompile Include="MeasureToolOverlayUI.cpp" />
<ClCompile Include="..\MeasureToolModuleInterface\trace.cpp" />
<ClCompile Include="Clipboard.cpp" />
<ClCompile Include="PerGlyphOpacityTextRender.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
@@ -29,6 +31,8 @@
<ClInclude Include="MeasureToolOverlayUI.h" />
<ClInclude Include="constants.h" />
<ClInclude Include="..\MeasureToolModuleInterface\trace.h" />
<ClInclude Include="Clipboard.h" />
<ClInclude Include="PerGlyphOpacityTextRender.h" />
</ItemGroup>
<ItemGroup>
<Filter Include="Assets">

View File

@@ -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;
}
}