1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
29 #include <sallayout.hxx>
34 #include <comphelper/windowserrorstring.hxx>
35 #include <o3tl/safeint.hxx>
36 #include <sal/log.hxx>
41 D2D1_TEXT_ANTIALIAS_MODE
lclGetSystemTextAntiAliasType()
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(),
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
,
74 "HRESULT failed with: 0x" << OUString::number(hr
, 16) << ": " << WindowsErrorStringFromHRESULT(hr
));
78 #define CHECKHR(funct) checkResult(funct, SAL_WHERE)
80 #define CHECKHR(funct) (funct)
84 // Sets and unsets the needed DirectWrite transform to support the font's rotation.
85 class WinFontTransformGuard
88 WinFontTransformGuard(ID2D1RenderTarget
* pRenderTarget
, float hscale
,
89 const GenericSalLayout
& rLayout
, const D2D1_POINT_2F
& rBaseline
,
91 ~WinFontTransformGuard();
94 ID2D1RenderTarget
* mpRenderTarget
;
95 std::optional
<D2D1::Matrix3x2F
> moTransform
;
98 } // end anonymous namespace
101 D2DWriteTextOutRenderer::MODE
D2DWriteTextOutRenderer::GetMode(bool bRenderingModeNatural
,
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
;
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),
129 mRTProps(D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT
,
130 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM
, D2D1_ALPHA_MODE_PREMULTIPLIED
),
132 maRenderingMode(mode
)
134 HRESULT hr
= D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED
, __uuidof(ID2D1Factory
), nullptr, IID_PPV_ARGS_Helper(&mpD2DFactory
));
136 hr
= CreateRenderTarget();
139 HRESULT
D2DWriteTextOutRenderer::CreateRenderTarget()
141 HRESULT hr
= CHECKHR(mpD2DFactory
->CreateDCRenderTarget(&mRTProps
, &mpRT
));
144 mpRT
->SetTextRenderingParams(lclSetRenderingMode(maRenderingMode
.second
));
145 mpRT
->SetTextAntialiasMode(maRenderingMode
.first
);
150 bool D2DWriteTextOutRenderer::Ready() const
155 HRESULT
D2DWriteTextOutRenderer::BindDC(HDC hDC
, tools::Rectangle
const & rRect
)
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
)
166 bool bResult
= false;
171 bResult
= performRender(rLayout
, rGraphics
, hDC
, bRetry
);
173 } while (bRetry
&& nCount
< 3);
177 bool D2DWriteTextOutRenderer::performRender(GenericSalLayout
const & rLayout
, SalGraphics
& rGraphics
, HDC hDC
, bool& bRetry
)
182 HRESULT hr
= BindDC(hDC
);
184 if (hr
== D2DERR_RECREATE_TARGET
)
186 CreateRenderTarget();
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
);
203 auto [succeeded
, bounds
] = [&rLayout
]()
205 basegfx::B2DRectangle r
;
206 bool result
= rLayout
.GetBoundRect(r
);
208 r
.grow(1); // plus 1 pixel to the tight range
209 return std::make_pair(result
, SalLayout::BoundRect2Rectangle(r
));
214 hr
= BindDC(hDC
, bounds
); // Update the bounding rect.
215 succeeded
= SUCCEEDED(hr
);
218 sal::systools::COMReference
<ID2D1SolidColorBrush
> pBrush
;
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
)));
230 const float hscale
= rWinFont
.getHScale();
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
= {
253 mpRT
->DrawGlyphRun(baseline
, &glyphs
, pBrush
);
256 hr
= CHECKHR(mpRT
->EndDraw());
259 if (hr
== D2DERR_RECREATE_TARGET
)
261 CreateRenderTarget();
268 IDWriteFontFace
* D2DWriteTextOutRenderer::GetDWriteFace(const WinFontInstance
& rWinFont
,
271 auto pFontFace
= rWinFont
.GetDWFontFace();
275 HFONT hFont
= rWinFont
.GetHFONT();
277 GetObjectW(hFont
, sizeof(LOGFONTW
), &aLogFont
);
279 mpRT
->GetDpi(&dpix
, &dpiy
);
280 *lfSize
= aLogFont
.lfHeight
* 96.0f
/ dpiy
;
289 WinFontTransformGuard::WinFontTransformGuard(ID2D1RenderTarget
* pRenderTarget
, float hscale
,
290 const GenericSalLayout
& rLayout
,
291 const D2D1_POINT_2F
& rBaseline
,
293 : mpRenderTarget(pRenderTarget
)
295 Degree10 angle
= rLayout
.GetOrientation();
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
311 aTransform
= aTransform
* D2D1::Matrix3x2F::Rotation(-toDegrees(angle
), rBaseline
);
313 mpRenderTarget
->SetTransform(aTransform
);
317 WinFontTransformGuard::~WinFontTransformGuard()
320 mpRenderTarget
->SetTransform(*moTransform
);
323 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */