tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / vcl / win / gdi / DWriteTextRenderer.cxx
blobcb3c1ba4c060f3f1529e231b8bcc8994b704c28d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <win/salgdi.h>
23 #include <win/saldata.hxx>
24 #include <ImplOutDevData.hxx>
26 #include <win/DWriteTextRenderer.hxx>
28 #include <sft.hxx>
29 #include <sallayout.hxx>
31 #include <shlwapi.h>
32 #include <winver.h>
34 #include <comphelper/windowserrorstring.hxx>
35 #include <o3tl/safeint.hxx>
36 #include <sal/log.hxx>
38 namespace
41 D2D1_TEXT_ANTIALIAS_MODE lclGetSystemTextAntiAliasType()
43 UINT t;
44 if (Application::GetSettings().GetStyleSettings().GetUseSubpixelAA()
45 && SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE, 0, &t, 0)
46 && t == FE_FONTSMOOTHINGCLEARTYPE)
47 return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
48 return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
51 IDWriteRenderingParams* lclSetRenderingMode(DWRITE_RENDERING_MODE eRenderingMode)
53 IDWriteFactory* pDWriteFactory = WinSalGraphics::getDWriteFactory();
55 IDWriteRenderingParams* pDefaultParameters = nullptr;
56 pDWriteFactory->CreateRenderingParams(&pDefaultParameters);
58 IDWriteRenderingParams* pParameters = nullptr;
59 pDWriteFactory->CreateCustomRenderingParams(
60 pDefaultParameters->GetGamma(),
61 pDefaultParameters->GetEnhancedContrast(),
62 pDefaultParameters->GetClearTypeLevel(),
63 pDefaultParameters->GetPixelGeometry(),
64 eRenderingMode,
65 &pParameters);
66 return pParameters;
69 #ifdef SAL_LOG_WARN
70 HRESULT checkResult(HRESULT hr, const char* location)
72 SAL_DETAIL_LOG_STREAM(SAL_DETAIL_ENABLE_LOG_WARN && FAILED(hr), ::SAL_DETAIL_LOG_LEVEL_WARN,
73 "vcl.gdi", location,
74 "HRESULT failed with: 0x" << OUString::number(hr, 16) << ": " << WindowsErrorStringFromHRESULT(hr));
75 return hr;
78 #define CHECKHR(funct) checkResult(funct, SAL_WHERE)
79 #else
80 #define CHECKHR(funct) (funct)
81 #endif
84 // Sets and unsets the needed DirectWrite transform to support the font's rotation.
85 class WinFontTransformGuard
87 public:
88 WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget, float hscale,
89 const GenericSalLayout& rLayout, const D2D1_POINT_2F& rBaseline,
90 bool bIsVertical);
91 ~WinFontTransformGuard();
93 private:
94 ID2D1RenderTarget* mpRenderTarget;
95 std::optional<D2D1::Matrix3x2F> moTransform;
98 } // end anonymous namespace
100 // static
101 D2DWriteTextOutRenderer::MODE D2DWriteTextOutRenderer::GetMode(bool bRenderingModeNatural,
102 bool bAntiAlias)
104 D2D1_TEXT_ANTIALIAS_MODE eTextMode;
105 if (!Application::GetSettings().GetStyleSettings().GetUseFontAAFromSystem())
106 // Currently only for file output - see GraphicExporter::filter
107 eTextMode = bAntiAlias ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
108 else if (BOOL bSmoothing; SystemParametersInfoW(SPI_GETFONTSMOOTHING, 0, &bSmoothing, 0))
109 eTextMode = bSmoothing ? lclGetSystemTextAntiAliasType() : D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
110 else
111 eTextMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
113 DWRITE_RENDERING_MODE eRenderingMode;
114 if (eTextMode == D2D1_TEXT_ANTIALIAS_MODE_ALIASED)
115 eRenderingMode = DWRITE_RENDERING_MODE_ALIASED; // no way to use bRenderingModeNatural
116 else if (bRenderingModeNatural)
117 eRenderingMode = DWRITE_RENDERING_MODE_NATURAL;
118 else if (eTextMode == D2D1_TEXT_ANTIALIAS_MODE_DEFAULT)
119 eRenderingMode = DWRITE_RENDERING_MODE_DEFAULT;
120 else // D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE || D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
121 eRenderingMode = DWRITE_RENDERING_MODE_GDI_CLASSIC;
123 return { eTextMode, eRenderingMode };
126 D2DWriteTextOutRenderer::D2DWriteTextOutRenderer(MODE mode)
127 : mpD2DFactory(nullptr),
128 mpRT(nullptr),
129 mRTProps(D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
130 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
131 0, 0)),
132 maRenderingMode(mode)
134 HRESULT hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), nullptr, IID_PPV_ARGS_Helper(&mpD2DFactory));
135 if (SUCCEEDED(hr))
136 hr = CreateRenderTarget();
139 HRESULT D2DWriteTextOutRenderer::CreateRenderTarget()
141 HRESULT hr = CHECKHR(mpD2DFactory->CreateDCRenderTarget(&mRTProps, &mpRT));
142 if (SUCCEEDED(hr))
144 mpRT->SetTextRenderingParams(lclSetRenderingMode(maRenderingMode.second));
145 mpRT->SetTextAntialiasMode(maRenderingMode.first);
147 return hr;
150 bool D2DWriteTextOutRenderer::Ready() const
152 return mpRT;
155 HRESULT D2DWriteTextOutRenderer::BindDC(HDC hDC, tools::Rectangle const & rRect)
157 RECT const rc = {
158 o3tl::narrowing<LONG>(rRect.Left()), o3tl::narrowing<LONG>(rRect.Top()),
159 o3tl::narrowing<LONG>(rRect.Right()), o3tl::narrowing<LONG>(rRect.Bottom()) };
160 return CHECKHR(mpRT->BindDC(hDC, &rc));
163 bool D2DWriteTextOutRenderer::operator()(GenericSalLayout const & rLayout, SalGraphics& rGraphics, HDC hDC)
165 bool bRetry = false;
166 bool bResult = false;
167 int nCount = 0;
170 bRetry = false;
171 bResult = performRender(rLayout, rGraphics, hDC, bRetry);
172 nCount++;
173 } while (bRetry && nCount < 3);
174 return bResult;
177 bool D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, SalGraphics& rGraphics, HDC hDC, bool& bRetry)
179 if (!Ready())
180 return false;
182 HRESULT hr = BindDC(hDC);
184 if (hr == D2DERR_RECREATE_TARGET)
186 CreateRenderTarget();
187 bRetry = true;
188 return false;
190 if (FAILED(hr))
192 // If for any reason we can't bind fallback to legacy APIs.
193 return ExTextOutRenderer()(rLayout, rGraphics, hDC);
196 const WinFontInstance& rWinFont = static_cast<const WinFontInstance&>(rLayout.GetFont());
198 float lfEmHeight = 0;
199 IDWriteFontFace* pFontFace = GetDWriteFace(rWinFont, &lfEmHeight);
200 if (!pFontFace)
201 return false;
203 auto [succeeded, bounds] = [&rLayout]()
205 basegfx::B2DRectangle r;
206 bool result = rLayout.GetBoundRect(r);
207 if (result)
208 r.grow(1); // plus 1 pixel to the tight range
209 return std::make_pair(result, SalLayout::BoundRect2Rectangle(r));
210 }();
212 if (succeeded)
214 hr = BindDC(hDC, bounds); // Update the bounding rect.
215 succeeded = SUCCEEDED(hr);
218 sal::systools::COMReference<ID2D1SolidColorBrush> pBrush;
219 if (succeeded)
221 COLORREF bgrTextColor = GetTextColor(hDC);
222 D2D1::ColorF aD2DColor(GetRValue(bgrTextColor) / 255.0f, GetGValue(bgrTextColor) / 255.0f, GetBValue(bgrTextColor) / 255.0f);
223 succeeded = SUCCEEDED(CHECKHR(mpRT->CreateSolidColorBrush(aD2DColor, &pBrush)));
226 if (succeeded)
228 mpRT->BeginDraw();
230 const float hscale = rWinFont.getHScale();
231 int nStart = 0;
232 basegfx::B2DPoint aPos;
233 const GlyphItem* pGlyph;
234 while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
236 UINT16 glyphIndices[] = { static_cast<UINT16>(pGlyph->glyphId()) };
237 FLOAT glyphAdvances[] = { static_cast<FLOAT>(pGlyph->newWidth()) / hscale };
238 DWRITE_GLYPH_OFFSET glyphOffsets[] = { { 0.0f, 0.0f }, };
239 D2D1_POINT_2F baseline = { static_cast<FLOAT>(aPos.getX() - bounds.Left()) / hscale,
240 static_cast<FLOAT>(aPos.getY() - bounds.Top()) };
241 WinFontTransformGuard aTransformGuard(mpRT, hscale, rLayout, baseline, pGlyph->IsVertical());
242 DWRITE_GLYPH_RUN glyphs = {
243 pFontFace,
244 lfEmHeight,
246 glyphIndices,
247 glyphAdvances,
248 glyphOffsets,
249 false,
253 mpRT->DrawGlyphRun(baseline, &glyphs, pBrush);
256 hr = CHECKHR(mpRT->EndDraw());
259 if (hr == D2DERR_RECREATE_TARGET)
261 CreateRenderTarget();
262 bRetry = true;
265 return succeeded;
268 IDWriteFontFace* D2DWriteTextOutRenderer::GetDWriteFace(const WinFontInstance& rWinFont,
269 float* lfSize) const
271 auto pFontFace = rWinFont.GetDWFontFace();
272 if (pFontFace)
274 LOGFONTW aLogFont;
275 HFONT hFont = rWinFont.GetHFONT();
277 GetObjectW(hFont, sizeof(LOGFONTW), &aLogFont);
278 float dpix, dpiy;
279 mpRT->GetDpi(&dpix, &dpiy);
280 *lfSize = aLogFont.lfHeight * 96.0f / dpiy;
282 assert(*lfSize < 0);
283 *lfSize *= -1;
286 return pFontFace;
289 WinFontTransformGuard::WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget, float hscale,
290 const GenericSalLayout& rLayout,
291 const D2D1_POINT_2F& rBaseline,
292 bool bIsVertical)
293 : mpRenderTarget(pRenderTarget)
295 Degree10 angle = rLayout.GetOrientation();
296 if (bIsVertical)
297 angle += 900_deg10;
299 if (hscale != 1.0f || angle)
301 D2D1::Matrix3x2F aTransform;
302 pRenderTarget->GetTransform(&aTransform);
303 moTransform = aTransform;
305 if (hscale != 1.0f) // basegfx::fTools::equal is useless with float
306 aTransform = aTransform * D2D1::Matrix3x2F::Scale(hscale, 1.0f, { 0, 0 });
308 // DWrite angle is in clockwise degrees, our orientation is in counter-clockwise 10th
309 // degrees.
310 if (angle)
311 aTransform = aTransform * D2D1::Matrix3x2F::Rotation(-toDegrees(angle), rBaseline);
313 mpRenderTarget->SetTransform(aTransform);
317 WinFontTransformGuard::~WinFontTransformGuard()
319 if (moTransform)
320 mpRenderTarget->SetTransform(*moTransform);
323 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */