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 <win/salgdi.h>
21 #include <win/saldata.hxx>
24 #include <win/DWriteTextRenderer.hxx>
27 #include <sallayout.hxx>
32 #include <comphelper/windowserrorstring.hxx>
33 #include <sal/log.hxx>
38 D2DTextAntiAliasMode
lclGetSystemTextAntiAliasMode()
40 D2DTextAntiAliasMode eMode
= D2DTextAntiAliasMode::Default
;
43 if (!SystemParametersInfoW(SPI_GETFONTSMOOTHING
, 0, &bFontSmoothing
, 0))
48 eMode
= D2DTextAntiAliasMode::AntiAliased
;
51 if (SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE
, 0, &nType
, 0) && nType
== FE_FONTSMOOTHINGCLEARTYPE
)
52 eMode
= D2DTextAntiAliasMode::ClearType
;
56 eMode
= D2DTextAntiAliasMode::Aliased
;
62 IDWriteRenderingParams
* lclSetRenderingMode(IDWriteFactory
* pDWriteFactory
, DWRITE_RENDERING_MODE eRenderingMode
)
64 IDWriteRenderingParams
* pDefaultParameters
= nullptr;
65 pDWriteFactory
->CreateRenderingParams(&pDefaultParameters
);
67 IDWriteRenderingParams
* pParameters
= nullptr;
68 pDWriteFactory
->CreateCustomRenderingParams(
69 pDefaultParameters
->GetGamma(),
70 pDefaultParameters
->GetEnhancedContrast(),
71 pDefaultParameters
->GetClearTypeLevel(),
72 pDefaultParameters
->GetPixelGeometry(),
79 HRESULT
checkResult(HRESULT hr
, const char* file
, size_t line
)
83 OUString sLocationString
= OUString::createFromAscii(file
) + ":" + OUString::number(line
) + " ";
84 SAL_DETAIL_LOG_STREAM(SAL_DETAIL_ENABLE_LOG_WARN
, ::SAL_DETAIL_LOG_LEVEL_WARN
,
85 "vcl.gdi", sLocationString
.toUtf8().getStr(),
86 "HRESULT failed with: 0x" << OUString::number(hr
, 16) << ": " << WindowsErrorStringFromHRESULT(hr
));
91 #define CHECKHR(funct) checkResult(funct, __FILE__, __LINE__)
93 #define CHECKHR(funct) (funct)
97 } // end anonymous namespace
99 D2DWriteTextOutRenderer::D2DWriteTextOutRenderer()
100 : mpD2DFactory(nullptr),
101 mpDWriteFactory(nullptr),
102 mpGdiInterop(nullptr),
104 mRTProps(D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT
,
105 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM
, D2D1_ALPHA_MODE_PREMULTIPLIED
),
110 meTextAntiAliasMode(D2DTextAntiAliasMode::Default
)
113 hr
= D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED
, __uuidof(ID2D1Factory
), nullptr, reinterpret_cast<void **>(&mpD2DFactory
));
114 hr
= DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED
, __uuidof(IDWriteFactory
), reinterpret_cast<IUnknown
**>(&mpDWriteFactory
));
117 hr
= mpDWriteFactory
->GetGdiInterop(&mpGdiInterop
);
118 hr
= CreateRenderTarget();
120 meTextAntiAliasMode
= lclGetSystemTextAntiAliasMode();
123 D2DWriteTextOutRenderer::~D2DWriteTextOutRenderer()
128 mpGdiInterop
->Release();
130 mpDWriteFactory
->Release();
132 mpD2DFactory
->Release();
135 void D2DWriteTextOutRenderer::applyTextAntiAliasMode()
137 D2D1_TEXT_ANTIALIAS_MODE eTextAAMode
= D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
;
138 DWRITE_RENDERING_MODE eRenderingMode
= DWRITE_RENDERING_MODE_DEFAULT
;
139 switch (meTextAntiAliasMode
)
141 case D2DTextAntiAliasMode::Default
:
142 eRenderingMode
= DWRITE_RENDERING_MODE_DEFAULT
;
143 eTextAAMode
= D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
;
145 case D2DTextAntiAliasMode::Aliased
:
146 eRenderingMode
= DWRITE_RENDERING_MODE_ALIASED
;
147 eTextAAMode
= D2D1_TEXT_ANTIALIAS_MODE_ALIASED
;
149 case D2DTextAntiAliasMode::AntiAliased
:
150 eRenderingMode
= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC
;
151 eTextAAMode
= D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE
;
153 case D2DTextAntiAliasMode::ClearType
:
154 eRenderingMode
= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC
;
155 eTextAAMode
= D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
;
160 mpRT
->SetTextRenderingParams(lclSetRenderingMode(mpDWriteFactory
, eRenderingMode
));
161 mpRT
->SetTextAntialiasMode(eTextAAMode
);
164 HRESULT
D2DWriteTextOutRenderer::CreateRenderTarget()
171 HRESULT hr
= CHECKHR(mpD2DFactory
->CreateDCRenderTarget(&mRTProps
, &mpRT
));
173 applyTextAntiAliasMode();
177 void D2DWriteTextOutRenderer::changeTextAntiAliasMode(D2DTextAntiAliasMode eMode
)
179 if (meTextAntiAliasMode
!= eMode
)
181 meTextAntiAliasMode
= eMode
;
182 applyTextAntiAliasMode();
186 bool D2DWriteTextOutRenderer::Ready() const
188 return mpGdiInterop
&& mpRT
;
191 HRESULT
D2DWriteTextOutRenderer::BindDC(HDC hDC
, tools::Rectangle
const & rRect
)
193 RECT
const rc
= { rRect
.Left(), rRect
.Top(), rRect
.Right(), rRect
.Bottom() };
194 return CHECKHR(mpRT
->BindDC(hDC
, &rc
));
197 bool D2DWriteTextOutRenderer::operator ()(GenericSalLayout
const & rLayout
, SalGraphics
& rGraphics
, HDC hDC
)
200 bool bResult
= false;
205 bResult
= performRender(rLayout
, rGraphics
, hDC
, bRetry
);
207 } while (bRetry
&& nCount
< 3);
211 bool D2DWriteTextOutRenderer::performRender(GenericSalLayout
const & rLayout
, SalGraphics
& rGraphics
, HDC hDC
, bool& bRetry
)
219 if (hr
== D2DERR_RECREATE_TARGET
)
221 CreateRenderTarget();
227 // If for any reason we can't bind fallback to legacy APIs.
228 return ExTextOutRenderer()(rLayout
, rGraphics
, hDC
);
232 if (!GetDWriteFaceFromHDC(hDC
, &mpFontFace
, &mlfEmHeight
))
235 const WinFontInstance
& rWinFont
= static_cast<const WinFontInstance
&>(rLayout
.GetFont());
236 float fHScale
= rWinFont
.getHScale();
238 tools::Rectangle bounds
;
239 bool succeeded
= rLayout
.GetBoundRect(bounds
);
242 hr
= BindDC(hDC
, bounds
); // Update the bounding rect.
243 succeeded
&= SUCCEEDED(hr
);
246 ID2D1SolidColorBrush
* pBrush
= nullptr;
249 COLORREF bgrTextColor
= GetTextColor(hDC
);
250 D2D1::ColorF
aD2DColor(GetRValue(bgrTextColor
) / 255.0f
, GetGValue(bgrTextColor
) / 255.0f
, GetBValue(bgrTextColor
) / 255.0f
);
251 succeeded
&= SUCCEEDED(CHECKHR(mpRT
->CreateSolidColorBrush(aD2DColor
, &pBrush
)));
260 const GlyphItem
* pGlyph
;
261 while (rLayout
.GetNextGlyph(&pGlyph
, aPos
, nStart
))
263 UINT16 glyphIndices
[] = { pGlyph
->glyphId() };
264 FLOAT glyphAdvances
[] = { static_cast<FLOAT
>(pGlyph
->m_nNewWidth
) / fHScale
};
265 DWRITE_GLYPH_OFFSET glyphOffsets
[] = { { 0.0f
, 0.0f
}, };
266 D2D1_POINT_2F baseline
= { static_cast<FLOAT
>(aPos
.X() - bounds
.Left()) / fHScale
,
267 static_cast<FLOAT
>(aPos
.Y() - bounds
.Top()) };
268 WinFontTransformGuard
aTransformGuard(mpRT
, fHScale
, rLayout
, baseline
);
269 DWRITE_GLYPH_RUN glyphs
= {
280 mpRT
->DrawGlyphRun(baseline
, &glyphs
, pBrush
);
283 hr
= CHECKHR(mpRT
->EndDraw());
291 if (hr
== D2DERR_RECREATE_TARGET
)
293 CreateRenderTarget();
300 bool D2DWriteTextOutRenderer::BindFont(HDC hDC
)
302 // A TextOutRender can only be bound to one font at a time, so the
303 assert(mpFontFace
== nullptr);
310 // Initially bind to an empty rectangle to get access to the font face,
311 // we'll update it once we've calculated a bounding rect in DrawGlyphs
312 if (FAILED(BindDC(mhDC
= hDC
)))
316 return GetDWriteFaceFromHDC(hDC
, &mpFontFace
, &mlfEmHeight
);
319 bool D2DWriteTextOutRenderer::ReleaseFont()
321 mpFontFace
->Release();
322 mpFontFace
= nullptr;
329 // The inkboxes returned have their origin on the baseline, to a -ve value
330 // of Top() means the glyph extends abs(Top()) many pixels above the
331 // baseline, and +ve means the ink starts that many pixels below.
332 std::vector
<tools::Rectangle
> D2DWriteTextOutRenderer::GetGlyphInkBoxes(uint16_t const * pGid
, uint16_t const * pGidEnd
) const
334 ptrdiff_t nGlyphs
= pGidEnd
- pGid
;
336 return std::vector
<tools::Rectangle
>();
338 DWRITE_FONT_METRICS aFontMetrics
;
339 mpFontFace
->GetMetrics(&aFontMetrics
);
341 std::vector
<DWRITE_GLYPH_METRICS
> metrics(nGlyphs
);
342 if (!SUCCEEDED(CHECKHR(mpFontFace
->GetDesignGlyphMetrics(pGid
, nGlyphs
, metrics
.data()))))
343 return std::vector
<tools::Rectangle
>();
345 std::vector
<tools::Rectangle
> aOut(nGlyphs
);
346 auto pOut
= aOut
.begin();
347 for (auto &m
: metrics
)
349 const long left
= m
.leftSideBearing
,
350 top
= m
.topSideBearing
- m
.verticalOriginY
,
351 right
= m
.advanceWidth
- m
.rightSideBearing
,
352 bottom
= INT32(m
.advanceHeight
) - m
.verticalOriginY
- m
.bottomSideBearing
;
354 // Scale to screen space.
355 pOut
->SetLeft( std::floor(left
* mlfEmHeight
/ aFontMetrics
.designUnitsPerEm
) );
356 pOut
->SetTop( std::floor(top
* mlfEmHeight
/ aFontMetrics
.designUnitsPerEm
) );
357 pOut
->SetRight( std::ceil(right
* mlfEmHeight
/ aFontMetrics
.designUnitsPerEm
) );
358 pOut
->SetBottom( std::ceil(bottom
* mlfEmHeight
/ aFontMetrics
.designUnitsPerEm
) );
366 bool D2DWriteTextOutRenderer::GetDWriteFaceFromHDC(HDC hDC
, IDWriteFontFace
** ppFontFace
, float * lfSize
) const
368 bool succeeded
= SUCCEEDED(CHECKHR(mpGdiInterop
->CreateFontFaceFromHdc(hDC
, ppFontFace
)));
373 HFONT hFont
= static_cast<HFONT
>(::GetCurrentObject(hDC
, OBJ_FONT
));
375 GetObjectW(hFont
, sizeof(LOGFONTW
), &aLogFont
);
377 mpRT
->GetDpi(&dpix
, &dpiy
);
378 *lfSize
= aLogFont
.lfHeight
* 96.0f
/ dpiy
;
387 WinFontTransformGuard::WinFontTransformGuard(ID2D1RenderTarget
* pRenderTarget
, float fHScale
,
388 const GenericSalLayout
& rLayout
,
389 const D2D1_POINT_2F
& rBaseline
)
390 : mpRenderTarget(pRenderTarget
)
392 pRenderTarget
->GetTransform(&maTransform
);
393 D2D1::Matrix3x2F aTransform
= maTransform
;
397 = aTransform
* D2D1::Matrix3x2F::Scale(D2D1::Size(fHScale
, 1.0f
), D2D1::Point2F(0, 0));
400 if (rLayout
.GetOrientation() != 0)
402 // DWrite angle is in clockwise degrees, our orientation is in counter-clockwise 10th
404 aTransform
= aTransform
405 * D2D1::Matrix3x2F::Rotation(
406 -static_cast<FLOAT
>(rLayout
.GetOrientation()) / 10, rBaseline
);
408 mpRenderTarget
->SetTransform(aTransform
);
411 WinFontTransformGuard::~WinFontTransformGuard() { mpRenderTarget
->SetTransform(maTransform
); }
413 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */