Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwindowsfontenginedirectwrite.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
7
8#include <QtCore/QtEndian>
9#include <QtCore/QVarLengthArray>
10#include <QtCore/QFile>
11#include <private/qstringiterator_p.h>
12#include <QtCore/private/qsystemlibrary_p.h>
13#include <QtCore/private/qwinregistry_p.h>
14#include <QtCore/private/qcomptr_p.h>
15#include <QtGui/private/qguiapplication_p.h>
16#include <qpa/qplatformintegration.h>
17#include <QtGui/qpainterpath.h>
18
19#include <private/qcolrpaintgraphrenderer_p.h>
20
21#if QT_CONFIG(directwrite3)
22# include "qwindowsdirectwritefontdatabase_p.h"
23# include <dwrite_3.h>
24#endif
25
26#include <d2d1.h>
27
29
30// Clang does not consider __declspec(nothrow) as nothrow
31QT_WARNING_DISABLE_CLANG("-Wmicrosoft-exception-spec")
32
33// Convert from design units to logical pixels
34#define DESIGN_TO_LOGICAL(DESIGN_UNIT_VALUE)
35 QFixed::fromReal((qreal(DESIGN_UNIT_VALUE) / qreal(m_unitsPerEm)) * fontDef.pixelSize)
36
37namespace {
38
39 class GeometrySink: public IDWriteGeometrySink
40 {
41 Q_DISABLE_COPY_MOVE(GeometrySink)
42 public:
43 GeometrySink(QPainterPath *path)
44 : m_refCount(0), m_path(path)
45 {
46 Q_ASSERT(m_path != 0);
47 }
48 virtual ~GeometrySink() = default;
49
50 IFACEMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *beziers, UINT bezierCount) override;
51 IFACEMETHOD_(void, AddLines)(const D2D1_POINT_2F *points, UINT pointCount) override;
52 IFACEMETHOD_(void, BeginFigure)(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) override;
53 IFACEMETHOD(Close)() override;
54 IFACEMETHOD_(void, EndFigure)(D2D1_FIGURE_END figureEnd) override;
55 IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode) override;
56 IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags) override;
57
58 IFACEMETHOD_(unsigned long, AddRef)() override;
59 IFACEMETHOD_(unsigned long, Release)() override;
60 IFACEMETHOD(QueryInterface)(IID const &riid, void **ppvObject) override;
61
62 private:
63 inline static QPointF fromD2D1_POINT_2F(const D2D1_POINT_2F &inp)
64 {
65 return QPointF(inp.x, inp.y);
66 }
67
68 unsigned long m_refCount;
69 QPointF m_startPoint;
70 QPainterPath *m_path;
71 };
72
73 void GeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers,
74 UINT bezierCount) noexcept
75 {
76 for (uint i=0; i<bezierCount; ++i) {
77 QPointF c1 = fromD2D1_POINT_2F(beziers[i].point1);
78 QPointF c2 = fromD2D1_POINT_2F(beziers[i].point2);
79 QPointF p2 = fromD2D1_POINT_2F(beziers[i].point3);
80
81 m_path->cubicTo(c1, c2, p2);
82 }
83 }
84
85 void GeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount) noexcept
86 {
87 for (uint i=0; i<pointsCount; ++i)
88 m_path->lineTo(fromD2D1_POINT_2F(points[i]));
89 }
90
91 void GeometrySink::BeginFigure(D2D1_POINT_2F startPoint,
92 D2D1_FIGURE_BEGIN /*figureBegin*/) noexcept
93 {
94 m_startPoint = fromD2D1_POINT_2F(startPoint);
95 m_path->moveTo(m_startPoint);
96 }
97
98 IFACEMETHODIMP GeometrySink::Close() noexcept
99 {
100 return E_NOTIMPL;
101 }
102
103 void GeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) noexcept
104 {
105 if (figureEnd == D2D1_FIGURE_END_CLOSED)
106 m_path->closeSubpath();
107 }
108
109 void GeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) noexcept
110 {
111 m_path->setFillRule(fillMode == D2D1_FILL_MODE_ALTERNATE
112 ? Qt::OddEvenFill
113 : Qt::WindingFill);
114 }
115
116 void GeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT /*vertexFlags*/) noexcept
117 {
118 /* Not implemented */
119 }
120
121 IFACEMETHODIMP_(unsigned long) GeometrySink::AddRef() noexcept
122 {
123 return InterlockedIncrement(&m_refCount);
124 }
125
126 IFACEMETHODIMP_(unsigned long) GeometrySink::Release() noexcept
127 {
128 unsigned long newCount = InterlockedDecrement(&m_refCount);
129 if (newCount == 0)
130 {
131 delete this;
132 return 0;
133 }
134
135 return newCount;
136 }
137
138 IFACEMETHODIMP GeometrySink::QueryInterface(IID const &riid, void **ppvObject) noexcept
139 {
140 if (__uuidof(IDWriteGeometrySink) == riid) {
141 *ppvObject = this;
142 } else if (__uuidof(IUnknown) == riid) {
143 *ppvObject = this;
144 } else {
145 *ppvObject = NULL;
146 return E_FAIL;
147 }
148
149 AddRef();
150 return S_OK;
151 }
152
153}
154
155static DWRITE_MEASURING_MODE renderModeToMeasureMode(DWRITE_RENDERING_MODE renderMode)
156{
157 switch (renderMode) {
158 case DWRITE_RENDERING_MODE_GDI_CLASSIC:
159 return DWRITE_MEASURING_MODE_GDI_CLASSIC;
160 case DWRITE_RENDERING_MODE_GDI_NATURAL:
161 return DWRITE_MEASURING_MODE_GDI_NATURAL;
162 default:
163 return DWRITE_MEASURING_MODE_NATURAL;
164 }
165}
166
167static QFont::HintingPreference determineHinting(const QFontDef &fontDef)
168{
169 QFont::HintingPreference hintingPreference = QFont::HintingPreference(fontDef.hintingPreference);
170 if (hintingPreference == QFont::PreferDefaultHinting) {
171 if (!qFuzzyCompare(qApp->devicePixelRatio(), 1.0)) {
172 // Microsoft documentation recommends using asymmetric rendering for small fonts
173 // at pixel size 16 and less, and symmetric for larger fonts.
174 hintingPreference = fontDef.pixelSize > 16.0
175 ? QFont::PreferNoHinting
176 : QFont::PreferVerticalHinting;
177 } else {
178 hintingPreference = QFont::PreferFullHinting;
179 }
180 }
181
182 return hintingPreference;
183}
184
185bool QWindowsFontEngineDirectWrite::useSymmetricAntialiasing() const
186{
187 if (m_useSymmetricAntialiasing < 0) {
188 qCDebug(lcQpaFonts) << "Checking antialiasing strategy for:"
189 << fontDef.families;
190
191 // We default to symmetric antialiasing for fonts without hinting and for pixel sizes
192 // over 16.
193 m_useSymmetricAntialiasing = fontDef.pixelSize > 16 || !hasHinting();
194
195 qCDebug(lcQpaFonts) << " After checking pixel size and hinting, defaulting to:"
196 << bool(m_useSymmetricAntialiasing);
197
198 // Then we check if there is a GASP table and consult this if it is available
199 const QByteArray gasp = getSfntTable(QFont::Tag("gasp").value());
200 if (gasp.size() > 4) {
201 qCDebug(lcQpaFonts) << " Checking GASP table, size=" << gasp.size();
202
203 const uchar *start = reinterpret_cast<const uchar *>(gasp.constBegin());
204
205 const quint16 numRanges = qFromBigEndian<quint16>(start + 2);
206 if (gasp.size() >= 4 + numRanges * 4) {
207 qCDebug(lcQpaFonts) << " GASP ranges:" << numRanges;
208 for (int i = 0; i < numRanges; ++i) {
209 const quint16 rangeMaxPPEM = qFromBigEndian<quint16>(start + 4 + i * 4);
210 qCDebug(lcQpaFonts) << " GASP range " << i << "upper limit == " << rangeMaxPPEM;
211
212 if (rangeMaxPPEM >= fontDef.pixelSize) {
213 const quint16 rangeGaspBehavior = qFromBigEndian<quint16>(start + 4 + i * 4 + 2);
214 qCDebug(lcQpaFonts) << " GASP behavior " << i << "==" << rangeGaspBehavior;
215
216 // GASP_SYMMETRIC_SMOOTHING = 0x8
217 m_useSymmetricAntialiasing = !!(rangeGaspBehavior & 0x8);
218 qCDebug(lcQpaFonts) << " Symmetric antialiasing override: "
219 << bool(m_useSymmetricAntialiasing);
220
221 break;
222 }
223 }
224 }
225 }
226 }
227
228 return m_useSymmetricAntialiasing;
229}
230
231DWRITE_RENDERING_MODE QWindowsFontEngineDirectWrite::hintingPreferenceToRenderingMode(const QFontDef &fontDef) const
232{
233 if ((fontDef.styleStrategy & QFont::NoAntialias) && glyphFormat != QFontEngine::Format_ARGB)
234 return DWRITE_RENDERING_MODE_ALIASED;
235
236 QFont::HintingPreference hintingPreference = determineHinting(fontDef);
237 switch (hintingPreference) {
238 case QFont::PreferNoHinting:
239 return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
240 case QFont::PreferVerticalHinting:
241 return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
242 default:
243 if (useSymmetricAntialiasing())
244 return DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
245 else
246 return DWRITE_RENDERING_MODE_GDI_CLASSIC;
247 }
248}
249
250/*!
251 \class QWindowsFontEngineDirectWrite
252 \brief Windows font engine using Direct Write.
253 \internal
254
255 Font engine for subpixel positioned text on Windows Vista
256 (with platform update) and later. If selected during
257 configuration, the engine will be selected only when the hinting
258 preference of a font is set to None or Vertical hinting, or
259 when fontengine=directwrite is selected as platform option.
260*/
261
262QWindowsFontEngineDirectWrite::QWindowsFontEngineDirectWrite(IDWriteFontFace *directWriteFontFace,
263 qreal pixelSize,
264 const QSharedPointer<QWindowsFontEngineData> &d)
265 : QFontEngine(DirectWrite)
266 , m_fontEngineData(d)
267 , m_directWriteFontFace(directWriteFontFace)
268 , m_directWriteBitmapRenderTarget(0)
269 , m_lineThickness(-1)
270 , m_unitsPerEm(-1)
271 , m_capHeight(-1)
272 , m_xHeight(-1)
273{
274 qCDebug(lcQpaFonts) << __FUNCTION__ << pixelSize;
275
276 Q_ASSERT(m_directWriteFontFace);
277
278 m_fontEngineData->directWriteFactory->AddRef();
279 m_directWriteFontFace->AddRef();
280
281 IDWriteRenderingParams *renderingParams = nullptr;
282 if (SUCCEEDED(m_fontEngineData->directWriteFactory->CreateRenderingParams(&renderingParams))) {
283 m_pixelGeometry = renderingParams->GetPixelGeometry();
284 renderingParams->Release();
285 }
286
287 fontDef.pixelSize = pixelSize;
288 collectMetrics();
289 cache_cost = m_xHeight.toInt() * m_xHeight.toInt() * 2000;
290}
291
292QWindowsFontEngineDirectWrite::~QWindowsFontEngineDirectWrite()
293{
294 qCDebug(lcQpaFonts) << __FUNCTION__;
295
296 m_fontEngineData->directWriteFactory->Release();
297 m_directWriteFontFace->Release();
298
299 if (m_directWriteBitmapRenderTarget != 0)
300 m_directWriteBitmapRenderTarget->Release();
301
302 if (!m_uniqueFamilyName.isEmpty()) {
303 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
304 static_cast<QWindowsFontDatabase *>(pfdb)->derefUniqueFont(m_uniqueFamilyName);
305 }
306}
307
308#ifndef Q_CC_MINGW
310
312{
313 return __uuidof(IDWriteLocalFontFileLoader);
314}
315#else // !Q_CC_MINGW
316DECLARE_INTERFACE_(QIdWriteLocalFontFileLoader, IDWriteFontFileLoader)
317{
318 STDMETHOD(GetFilePathLengthFromKey)(THIS_ void const *, UINT32, UINT32*) PURE;
319 STDMETHOD(GetFilePathFromKey)(THIS_ void const *, UINT32, WCHAR *, UINT32) PURE;
320 STDMETHOD(GetLastWriteTimeFromKey)(THIS_ void const *, UINT32, FILETIME *) PURE;
321};
322
323static UUID uuidIdWriteLocalFontFileLoader()
324{
325 static const UUID result = { 0xb2d9f3ec, 0xc9fe, 0x4a11, {0xa2, 0xec, 0xd8, 0x62, 0x8, 0xf7, 0xc0, 0xa2}};
326 return result;
327}
328#endif // Q_CC_MINGW
329
330QString QWindowsFontEngineDirectWrite::filenameFromFontFile(IDWriteFontFile *fontFile)
331{
332 IDWriteFontFileLoader *loader = nullptr;
333
334 HRESULT hr = fontFile->GetLoader(&loader);
335 if (FAILED(hr)) {
336 qErrnoWarning("%s: GetLoader failed", __FUNCTION__);
337 return QString();
338 }
339
340 QIdWriteLocalFontFileLoader *localLoader = nullptr;
341 hr = loader->QueryInterface(uuidIdWriteLocalFontFileLoader(),
342 reinterpret_cast<void **>(&localLoader));
343
344 const void *fontFileReferenceKey = nullptr;
345 UINT32 fontFileReferenceKeySize = 0;
346 if (SUCCEEDED(hr)) {
347 hr = fontFile->GetReferenceKey(&fontFileReferenceKey,
348 &fontFileReferenceKeySize);
349 if (FAILED(hr))
350 qErrnoWarning(hr, "%s: GetReferenceKey failed", __FUNCTION__);
351 }
352
353 UINT32 filePathLength = 0;
354 if (SUCCEEDED(hr)) {
355 hr = localLoader->GetFilePathLengthFromKey(fontFileReferenceKey,
356 fontFileReferenceKeySize,
357 &filePathLength);
358 if (FAILED(hr))
359 qErrnoWarning(hr, "GetFilePathLength failed", __FUNCTION__);
360 }
361
362 QString ret;
363 if (SUCCEEDED(hr) && filePathLength > 0) {
364 QVarLengthArray<wchar_t> filePath(filePathLength + 1);
365
366 hr = localLoader->GetFilePathFromKey(fontFileReferenceKey,
367 fontFileReferenceKeySize,
368 filePath.data(),
369 filePathLength + 1);
370 if (FAILED(hr))
371 qErrnoWarning(hr, "%s: GetFilePathFromKey failed", __FUNCTION__);
372 else
373 ret = QString::fromWCharArray(filePath.data());
374 }
375
376 if (localLoader != nullptr)
377 localLoader->Release();
378
379 if (loader != nullptr)
380 loader->Release();
381 return ret;
382}
383
384HFONT QWindowsFontEngineDirectWrite::createHFONT() const
385{
386 if (m_fontEngineData == nullptr || m_directWriteFontFace == nullptr)
387 return NULL;
388
389 LOGFONT lf;
390 HRESULT hr = m_fontEngineData->directWriteGdiInterop->ConvertFontFaceToLOGFONT(m_directWriteFontFace,
391 &lf);
392 if (SUCCEEDED(hr)) {
393 lf.lfHeight = -qRound(fontDef.pixelSize);
394 return CreateFontIndirect(&lf);
395 } else {
396 return NULL;
397 }
398}
399
400void QWindowsFontEngineDirectWrite::initializeHeightMetrics() const
401{
402 DWRITE_FONT_METRICS metrics;
403 m_directWriteFontFace->GetMetrics(&metrics);
404
405 m_ascent = DESIGN_TO_LOGICAL(metrics.ascent);
406 m_descent = DESIGN_TO_LOGICAL(metrics.descent);
407 m_leading = DESIGN_TO_LOGICAL(metrics.lineGap);
408
409 QFontEngine::initializeHeightMetrics();
410}
411
412void QWindowsFontEngineDirectWrite::collectMetrics()
413{
414 DWRITE_FONT_METRICS metrics;
415
416 m_directWriteFontFace->GetMetrics(&metrics);
417 m_unitsPerEm = metrics.designUnitsPerEm;
418
419 // Something is wrong with this font. Set the em square size to the minimum value in
420 // the spec.
421 if (m_unitsPerEm == 0) {
422 qCWarning(lcQpaFonts) << "Font" << fontDef.families << "reports an em square size of 0."
423 << "Clamping to minimum value.";
424 m_unitsPerEm = 16;
425 }
426
427 m_lineThickness = DESIGN_TO_LOGICAL(metrics.underlineThickness);
428 m_capHeight = DESIGN_TO_LOGICAL(metrics.capHeight);
429 m_xHeight = DESIGN_TO_LOGICAL(metrics.xHeight);
430 m_underlinePosition = DESIGN_TO_LOGICAL(metrics.underlinePosition);
431
432 IDWriteFontFile *fontFile = nullptr;
433 UINT32 numberOfFiles = 1;
434 if (SUCCEEDED(m_directWriteFontFace->GetFiles(&numberOfFiles, &fontFile))) {
435 m_faceId.filename = QFile::encodeName(filenameFromFontFile(fontFile));
436 fontFile->Release();
437 }
438
439 QByteArray table = getSfntTable(QFont::Tag("hhea").value());
440 const int advanceWidthMaxLocation = 10;
441 if (table.size() >= advanceWidthMaxLocation + int(sizeof(quint16))) {
442 quint16 advanceWidthMax = qFromBigEndian<quint16>(table.constBegin() + advanceWidthMaxLocation);
443 m_maxAdvanceWidth = DESIGN_TO_LOGICAL(advanceWidthMax);
444 }
445
446 loadKerningPairs(emSquareSize() / QFixed::fromReal(fontDef.pixelSize));
447
448#if QT_CONFIG(directwrite3)
449 IDWriteFontFace5 *face5;
450 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace5),
451 reinterpret_cast<void **>(&face5)))) {
452
453 IDWriteFontResource *fontResource;
454 if (SUCCEEDED(face5->GetFontResource(&fontResource))) {
455 const UINT32 fontAxisCount = fontResource->GetFontAxisCount();
456 if (fontAxisCount > 0) {
457 QVarLengthArray<DWRITE_FONT_AXIS_VALUE, 8> axisValues(fontAxisCount);
458 HRESULT hres = fontResource->GetDefaultFontAxisValues(axisValues.data(), fontAxisCount);
459
460 QVarLengthArray<DWRITE_FONT_AXIS_RANGE, 8> axisRanges(fontAxisCount);
461 if (SUCCEEDED(hres))
462 hres = fontResource->GetFontAxisRanges(axisRanges.data(), fontAxisCount);
463
464 if (SUCCEEDED(hres)) {
465 for (UINT32 i = 0; i < fontAxisCount; ++i) {
466 const DWRITE_FONT_AXIS_VALUE &value = axisValues.at(i);
467 const DWRITE_FONT_AXIS_RANGE &range = axisRanges.at(i);
468
469 if (range.minValue < range.maxValue) {
470 QFontVariableAxis axis;
471 if (auto maybeTag = QFont::Tag::fromValue(qToBigEndian<UINT32>(value.axisTag))) {
472 axis.setTag(*maybeTag);
473 } else {
474 qWarning() << "QWindowsFontEngineDirectWrite::collectMetrics: Invalid tag" << value.axisTag;
475 }
476
477 axis.setDefaultValue(value.value);
478 axis.setMaximumValue(range.maxValue);
479 axis.setMinimumValue(range.minValue);
480
481 IDWriteLocalizedStrings *names;
482 if (SUCCEEDED(fontResource->GetAxisNames(i, &names))) {
483 wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
484 bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
485
486 QString name = hasDefaultLocale
487 ? QWindowsDirectWriteFontDatabase::localeString(names, defaultLocale)
488 : QString();
489 if (name.isEmpty()) {
490 wchar_t englishLocale[] = L"en-us";
491 name = QWindowsDirectWriteFontDatabase::localeString(names, englishLocale);
492 }
493
494 axis.setName(name);
495 names->Release();
496 }
497
498 m_variableAxes.append(axis);
499 }
500 }
501 }
502 }
503 fontResource->Release();
504 }
505 face5->Release();
506 }
507#endif
508}
509
510QFixed QWindowsFontEngineDirectWrite::underlinePosition() const
511{
512 if (m_underlinePosition > 0)
513 return m_underlinePosition;
514 else
515 return QFontEngine::underlinePosition();
516}
517
518QFixed QWindowsFontEngineDirectWrite::lineThickness() const
519{
520 if (m_lineThickness > 0)
521 return m_lineThickness;
522 else
523 return QFontEngine::lineThickness();
524}
525
526bool QWindowsFontEngineDirectWrite::getSfntTableData(uint tag, uchar *buffer, uint *length) const
527{
528 bool ret = false;
529
530 const void *tableData = 0;
531 UINT32 tableSize;
532 void *tableContext = 0;
533 BOOL exists;
534 HRESULT hr = m_directWriteFontFace->TryGetFontTable(qbswap<quint32>(tag),
535 &tableData, &tableSize,
536 &tableContext, &exists);
537 if (SUCCEEDED(hr)) {
538 if (exists) {
539 if (buffer && *length >= tableSize)
540 memcpy(buffer, tableData, tableSize);
541 *length = tableSize;
542 ret = (*length) > 0;
543 }
544 m_directWriteFontFace->ReleaseFontTable(tableContext);
545 } else {
546 qErrnoWarning("%s: TryGetFontTable failed", __FUNCTION__);
547 }
548
549 return ret;
550}
551
552QFixed QWindowsFontEngineDirectWrite::emSquareSize() const
553{
554 return m_unitsPerEm;
555}
556
557glyph_t QWindowsFontEngineDirectWrite::glyphIndex(uint ucs4) const
558{
559 UINT16 glyphIndex;
560
561 HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(&ucs4, 1, &glyphIndex);
562 if (FAILED(hr)) {
563 qErrnoWarning("%s: glyphIndex failed", __FUNCTION__);
564 glyphIndex = 0;
565 }
566
567 return glyphIndex;
568}
569
570int QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
571 int *nglyphs, QFontEngine::ShaperFlags flags) const
572{
573 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
574 if (*nglyphs < len) {
575 *nglyphs = len;
576 return -1;
577 }
578
579 QVarLengthArray<UINT32> codePoints(len);
580 int actualLength = 0;
581 QStringIterator it(str, str + len);
582 while (it.hasNext())
583 codePoints[actualLength++] = it.next();
584
585 QVarLengthArray<UINT16> glyphIndices(actualLength);
586 HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(codePoints.data(), actualLength,
587 glyphIndices.data());
588 if (FAILED(hr)) {
589 qErrnoWarning("%s: GetGlyphIndicesW failed", __FUNCTION__);
590 return -1;
591 }
592
593 int mappedGlyphs = 0;
594 for (int i = 0; i < actualLength; ++i) {
595 glyphs->glyphs[i] = glyphIndices.at(i);
596 if (glyphs->glyphs[i] != 0 || isIgnorableChar(codePoints.at(i)))
597 mappedGlyphs++;
598 }
599
600 *nglyphs = actualLength;
601 glyphs->numGlyphs = actualLength;
602
603 if (!(flags & GlyphIndicesOnly))
604 recalcAdvances(glyphs, {});
605
606 return mappedGlyphs;
607}
608
609QFontEngine::FaceId QWindowsFontEngineDirectWrite::faceId() const
610{
611 return m_faceId;
612}
613
614void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags shaperFlags) const
615{
616 QVarLengthArray<UINT16> glyphIndices(glyphs->numGlyphs);
617
618 // ### Caching?
619 for(int i=0; i<glyphs->numGlyphs; i++)
620 glyphIndices[i] = UINT16(glyphs->glyphs[i]);
621
622 QVarLengthArray<DWRITE_GLYPH_METRICS> glyphMetrics(glyphIndices.size());
623
624 HRESULT hr;
625 QFont::HintingPreference hint = determineHinting(fontDef);
626 bool needsDesignMetrics = shaperFlags & QFontEngine::DesignMetrics;
627 if (!needsDesignMetrics && hint == QFont::PreferFullHinting) {
628 const DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
629 const bool needsNaturalMetrics = renderMode == DWRITE_RENDERING_MODE_NATURAL
630 || renderMode == DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
631
632 hr = m_directWriteFontFace->GetGdiCompatibleGlyphMetrics(float(fontDef.pixelSize),
633 1.0f,
634 NULL,
635 needsNaturalMetrics,
636 glyphIndices.data(),
637 glyphIndices.size(),
638 glyphMetrics.data());
639 } else {
640 hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(),
641 glyphIndices.size(),
642 glyphMetrics.data());
643 }
644 if (SUCCEEDED(hr)) {
645 qreal stretch = fontDef.stretch != QFont::AnyStretch ? fontDef.stretch / 100.0 : 1.0;
646 for (int i = 0; i < glyphs->numGlyphs; ++i)
647 glyphs->advances[i] = DESIGN_TO_LOGICAL(glyphMetrics[i].advanceWidth * stretch);
648 } else {
649 qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__);
650 }
651}
652
653QPainterPath QWindowsFontEngineDirectWrite::unscaledGlyph(glyph_t glyph) const
654{
655 float advance = 0.0f;
656 UINT16 g = glyph;
657 DWRITE_GLYPH_OFFSET offset;
658 offset.advanceOffset = 0;
659 offset.ascenderOffset = 0;
660
661 QPainterPath ret;
662 GeometrySink geometrySink(&ret);
663 HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(m_unitsPerEm,
664 &g,
665 &advance,
666 &offset,
667 1,
668 false,
669 false,
670 &geometrySink);
671 if (FAILED(hr))
672 qErrnoWarning("%s: GetGlyphRunOutline failed", __FUNCTION__);
673
674 return ret;
675}
676
677void QWindowsFontEngineDirectWrite::getUnscaledGlyph(glyph_t glyph,
678 QPainterPath *path,
679 glyph_metrics_t *metric)
680{
681 *path = unscaledGlyph(glyph);
682
683 UINT16 g = glyph;
684 DWRITE_GLYPH_METRICS glyphMetrics;
685 HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&g, 1, &glyphMetrics);
686 if (FAILED(hr)) {
687 qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__);
688 return;
689 }
690
691 QFixed advanceWidth = QFixed(int(glyphMetrics.advanceWidth));
692 QFixed leftSideBearing = QFixed(glyphMetrics.leftSideBearing);
693 QFixed rightSideBearing = QFixed(glyphMetrics.rightSideBearing);
694 QFixed advanceHeight = QFixed(int(glyphMetrics.advanceHeight));
695 QFixed verticalOriginY = QFixed(glyphMetrics.verticalOriginY);
696 QFixed topSideBearing = QFixed(glyphMetrics.topSideBearing);
697 QFixed bottomSideBearing = QFixed(glyphMetrics.bottomSideBearing);
698 QFixed width = advanceWidth - leftSideBearing - rightSideBearing;
699 QFixed height = advanceHeight - topSideBearing - bottomSideBearing;
700 *metric = glyph_metrics_t(leftSideBearing,
701 -verticalOriginY + topSideBearing,
702 width,
703 height,
704 advanceWidth,
705 0);
706}
707
708void QWindowsFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
709 QPainterPath *path, QTextItem::RenderFlags flags)
710{
711 Q_UNUSED(flags);
712 QVarLengthArray<UINT16> glyphIndices(nglyphs);
713 QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(nglyphs);
714 QVarLengthArray<FLOAT> glyphAdvances(nglyphs);
715
716 for (int i=0; i<nglyphs; ++i) {
717 glyphIndices[i] = glyphs[i];
718 glyphOffsets[i].advanceOffset = positions[i].x.toReal();
719 glyphOffsets[i].ascenderOffset = -positions[i].y.toReal();
720 glyphAdvances[i] = 0.0;
721 }
722
723 GeometrySink geometrySink(path);
724 HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(
725 fontDef.pixelSize,
726 glyphIndices.data(),
727 glyphAdvances.data(),
728 glyphOffsets.data(),
729 nglyphs,
730 false,
731 false,
732 &geometrySink
733 );
734
735 if (FAILED(hr))
736 qErrnoWarning("%s: GetGlyphRunOutline failed", __FUNCTION__);
737}
738
739glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(const QGlyphLayout &glyphs)
740{
741 if (glyphs.numGlyphs == 0)
742 return glyph_metrics_t();
743 QFixed w = 0;
744 for (int i = 0; i < glyphs.numGlyphs; ++i)
745 w += glyphs.effectiveAdvance(i);
746
747 const QFixed leftBearing = firstLeftBearing(glyphs);
748 return glyph_metrics_t(leftBearing, -ascent(), w - leftBearing - lastRightBearing(glyphs),
749 ascent() + descent(), w, 0);
750}
751
752glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(glyph_t g)
753{
754 UINT16 glyphIndex = g;
755
756 DWRITE_GLYPH_METRICS glyphMetrics;
757 HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics);
758 if (SUCCEEDED(hr)) {
759 QFixed advanceWidth = DESIGN_TO_LOGICAL(glyphMetrics.advanceWidth);
760 QFixed leftSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.leftSideBearing);
761 QFixed rightSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.rightSideBearing);
762 QFixed advanceHeight = DESIGN_TO_LOGICAL(glyphMetrics.advanceHeight);
763 QFixed verticalOriginY = DESIGN_TO_LOGICAL(glyphMetrics.verticalOriginY);
764 QFixed topSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.topSideBearing);
765 QFixed bottomSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.bottomSideBearing);
766 QFixed width = advanceWidth - leftSideBearing - rightSideBearing;
767 QFixed height = advanceHeight - topSideBearing - bottomSideBearing;
768 return glyph_metrics_t(leftSideBearing,
769 -verticalOriginY + topSideBearing,
770 width,
771 height,
772 advanceWidth,
773 0);
774 } else {
775 qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__);
776 }
777
778 return glyph_metrics_t();
779}
780
781QFixed QWindowsFontEngineDirectWrite::capHeight() const
782{
783 if (m_capHeight <= 0)
784 return calculatedCapHeight();
785
786 return m_capHeight;
787}
788
789QFixed QWindowsFontEngineDirectWrite::xHeight() const
790{
791 return m_xHeight;
792}
793
794qreal QWindowsFontEngineDirectWrite::maxCharWidth() const
795{
796 return m_maxAdvanceWidth.toReal();
797}
798
799QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
800 const QFixedPoint &subPixelPosition,
801 const QTransform &t)
802{
803 QImage im = imageForGlyph(glyph, subPixelPosition, glyphMargin(Format_A8), t);
804
805 if (!im.isNull()) {
806 QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
807
808 for (int y=0; y<im.height(); ++y) {
809 const uint *src = reinterpret_cast<const uint *>(im.constScanLine(y));
810 uchar *dst = alphaMap.scanLine(y);
811 for (int x=0; x<im.width(); ++x) {
812 *dst = qGray(*src);
813 ++dst;
814 ++src;
815 }
816 }
817
818 return alphaMap;
819 } else {
820 return QFontEngine::alphaMapForGlyph(glyph, t);
821 }
822}
823
824QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
825 const QFixedPoint &subPixelPosition)
826{
827 return alphaMapForGlyph(glyph, subPixelPosition, QTransform());
828}
829
830bool QWindowsFontEngineDirectWrite::supportsHorizontalSubPixelPositions() const
831{
832 QFont::HintingPreference hinting = determineHinting(fontDef);
833 return (hinting != QFont::PreferFullHinting && !(fontDef.styleStrategy & QFont::NoAntialias));
834}
835
836QFontEngine::Properties QWindowsFontEngineDirectWrite::properties() const
837{
838 IDWriteFontFace2 *directWriteFontFace2;
839 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace2),
840 reinterpret_cast<void **>(&directWriteFontFace2)))) {
841 DWRITE_FONT_METRICS1 metrics;
842 directWriteFontFace2->GetMetrics(&metrics);
843
844 Properties p = QFontEngine::properties();
845 p.emSquare = metrics.designUnitsPerEm;
846 p.boundingBox = QRectF(metrics.glyphBoxLeft,
847 -metrics.glyphBoxTop,
848 metrics.glyphBoxRight - metrics.glyphBoxLeft,
849 metrics.glyphBoxTop - metrics.glyphBoxBottom);
850 p.ascent = metrics.ascent;
851 p.descent = metrics.descent;
852 p.leading = metrics.lineGap;
853 p.capHeight = metrics.capHeight;
854 p.lineWidth = metrics.underlineThickness;
855
856 directWriteFontFace2->Release();
857 return p;
858 } else {
859 return QFontEngine::properties();
860 }
861}
862
863bool QWindowsFontEngineDirectWrite::renderColr0GlyphRun(QImage *image,
864 const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun,
865 const DWRITE_MATRIX &transform,
866 DWRITE_RENDERING_MODE renderMode,
867 DWRITE_MEASURING_MODE measureMode,
868 DWRITE_GRID_FIT_MODE gridFitMode,
869 QColor color,
870 QRect boundingRect) const
871{
872 ComPtr<IDWriteFactory2> factory2;
873 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
874 &factory2);
875 if (FAILED(hr))
876 return false;
877
878 ComPtr<IDWriteGlyphRunAnalysis> colorGlyphsAnalysis;
879 hr = factory2->CreateGlyphRunAnalysis(
880 &colorGlyphRun->glyphRun,
881 &transform,
882 renderMode,
883 measureMode,
884 gridFitMode,
885 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
886 0.0, 0.0,
887 &colorGlyphsAnalysis
888 );
889
890 if (FAILED(hr)) {
891 qErrnoWarning(hr, "%s: CreateGlyphRunAnalysis failed for color run", __FUNCTION__);
892 return false;
893 }
894
895 float r, g, b, a;
896 if (colorGlyphRun->paletteIndex == 0xFFFF) {
897 r = float(color.redF());
898 g = float(color.greenF());
899 b = float(color.blueF());
900 a = 1.0;
901 } else {
902 r = qBound(0.0f, colorGlyphRun->runColor.r, 1.0f);
903 g = qBound(0.0f, colorGlyphRun->runColor.g, 1.0f);
904 b = qBound(0.0f, colorGlyphRun->runColor.b, 1.0f);
905 a = qBound(0.0f, colorGlyphRun->runColor.a, 1.0f);
906 }
907
908 if (!qFuzzyIsNull(a))
909 renderGlyphRun(image, r, g, b, a, colorGlyphsAnalysis.Get(), boundingRect, renderMode);
910
911 return true;
912}
913
914#if QT_CONFIG(directwritecolrv1)
915static inline QTransform matrixToTransform(const DWRITE_MATRIX &matrix,
916 int unitsPerEm = 1)
917{
918 return QTransform(matrix.m11, matrix.m12,
919 matrix.m21, matrix.m22,
920 matrix.dx * unitsPerEm, matrix.dy * unitsPerEm);
921}
922
923static inline QColor colorToColor(const DWRITE_COLOR_F &color, float alphaMultiplier = 1.0)
924{
925 return QColor::fromRgbF(color.r, color.g, color.b, color.a * alphaMultiplier);
926}
927
928static inline QGradient::Spread extendToSpread(UINT32 extendMode)
929{
930 switch (extendMode) {
931 case D2D1_EXTEND_MODE_WRAP: return QGradient::RepeatSpread;
932 case D2D1_EXTEND_MODE_MIRROR: return QGradient::ReflectSpread;
933 default: return QGradient::PadSpread;
934 };
935}
936
937static inline QPainter::CompositionMode compositeToCompositionMode(DWRITE_COLOR_COMPOSITE_MODE mode)
938{
939 switch (mode) {
940 case DWRITE_COLOR_COMPOSITE_CLEAR:
941 return QPainter::CompositionMode_Clear;
942
943 case DWRITE_COLOR_COMPOSITE_SRC:
944 return QPainter::CompositionMode_Source;
945
946 case DWRITE_COLOR_COMPOSITE_DEST:
947 return QPainter::CompositionMode_Destination;
948
949 case DWRITE_COLOR_COMPOSITE_SRC_OVER:
950 return QPainter::CompositionMode_SourceOver;
951
952 case DWRITE_COLOR_COMPOSITE_DEST_OVER:
953 return QPainter::CompositionMode_DestinationOver;
954
955 case DWRITE_COLOR_COMPOSITE_SRC_IN:
956 return QPainter::CompositionMode_SourceIn;
957
958 case DWRITE_COLOR_COMPOSITE_DEST_IN:
959 return QPainter::CompositionMode_DestinationIn;
960
961 case DWRITE_COLOR_COMPOSITE_SRC_OUT:
962 return QPainter::CompositionMode_SourceOut;
963
964 case DWRITE_COLOR_COMPOSITE_DEST_OUT:
965 return QPainter::CompositionMode_DestinationOut;
966
967 case DWRITE_COLOR_COMPOSITE_SRC_ATOP:
968 return QPainter::CompositionMode_SourceAtop;
969
970 case DWRITE_COLOR_COMPOSITE_DEST_ATOP:
971 return QPainter::CompositionMode_DestinationAtop;
972
973 case DWRITE_COLOR_COMPOSITE_XOR:
974 return QPainter::CompositionMode_Xor;
975
976 case DWRITE_COLOR_COMPOSITE_PLUS:
977 return QPainter::CompositionMode_Plus;
978
979 case DWRITE_COLOR_COMPOSITE_SCREEN:
980 return QPainter::CompositionMode_Screen;
981
982 case DWRITE_COLOR_COMPOSITE_OVERLAY:
983 return QPainter::CompositionMode_Overlay;
984
985 case DWRITE_COLOR_COMPOSITE_DARKEN:
986 return QPainter::CompositionMode_Darken;
987
988 case DWRITE_COLOR_COMPOSITE_LIGHTEN:
989 return QPainter::CompositionMode_Lighten;
990
991 case DWRITE_COLOR_COMPOSITE_COLOR_DODGE:
992 return QPainter::CompositionMode_ColorDodge;
993
994 case DWRITE_COLOR_COMPOSITE_COLOR_BURN:
995 return QPainter::CompositionMode_ColorBurn;
996
997 case DWRITE_COLOR_COMPOSITE_HARD_LIGHT:
998 return QPainter::CompositionMode_HardLight;
999
1000 case DWRITE_COLOR_COMPOSITE_SOFT_LIGHT:
1001 return QPainter::CompositionMode_SoftLight;
1002
1003 case DWRITE_COLOR_COMPOSITE_DIFFERENCE:
1004 return QPainter::CompositionMode_Difference;
1005
1006 case DWRITE_COLOR_COMPOSITE_EXCLUSION:
1007 return QPainter::CompositionMode_Exclusion;
1008
1009 case DWRITE_COLOR_COMPOSITE_MULTIPLY:
1010 return QPainter::CompositionMode_Multiply;
1011
1012 default:
1013 qCWarning(lcColrv1) << "Unhandled color composite mode:" << mode;
1014 return QPainter::CompositionMode_SourceOver;
1015 };
1016}
1017#endif // QT_CONFIG(directwritecolrv1)
1018
1019QRect QWindowsFontEngineDirectWrite::paintGraphBounds(glyph_t glyph,
1020 const DWRITE_MATRIX &matrix) const
1021{
1022#if QT_CONFIG(directwritecolrv1)
1023 ComPtr<IDWriteFontFace7> face7;
1024 HRESULT hr = m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace7),
1025 &face7);
1026 if (SUCCEEDED(hr)) {
1027 DWRITE_PAINT_FEATURE_LEVEL featureLevel = face7->GetPaintFeatureLevel(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE);
1028 if (featureLevel != DWRITE_PAINT_FEATURE_LEVEL_COLR_V1)
1029 return QRect{};
1030
1031 ComPtr<IDWritePaintReader> paintReader;
1032 hr = face7->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
1033 featureLevel,
1034 &paintReader);
1035 if (FAILED(hr)) {
1036 qErrnoWarning(hr, "%s: CreatePaintReader failed", __FUNCTION__);
1037 return QRect{};
1038 }
1039
1040 DWRITE_PAINT_ELEMENT paintElement;
1041 D2D_RECT_F clipBox;
1042 hr = paintReader->SetCurrentGlyph(glyph,
1043 &paintElement,
1044 sizeof(paintElement),
1045 &clipBox,
1046 nullptr);
1047
1048 if (FAILED(hr)) {
1049 qErrnoWarning(hr, "%s: SetCurrentGlyph failed", __FUNCTION__);
1050 return QRect{};
1051 }
1052
1053 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE)
1054 return QRect{};
1055
1056 QRectF boundingRect = QRectF(QPointF(clipBox.left, clipBox.top),
1057 QPointF(clipBox.right, clipBox.bottom));
1058 if (boundingRect.isNull()) {
1059 QColrPaintGraphRenderer boundingRectCalculator;
1060 boundingRectCalculator.beginCalculateBoundingBox();
1061 if (traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &boundingRectCalculator))
1062 boundingRect = boundingRectCalculator.boundingRect();
1063 }
1064
1065 QTransform initialTransform;
1066 initialTransform.scale(fontDef.pixelSize, fontDef.pixelSize);
1067 boundingRect = initialTransform.mapRect(boundingRect);
1068
1069 QTransform originalXform = matrixToTransform(matrix);
1070 boundingRect = originalXform.mapRect(boundingRect);
1071
1072 return boundingRect.toAlignedRect();
1073 } else {
1074 qCDebug(lcColrv1) << "Font face does not support IDWriteFontFace7 interface";
1075 }
1076#else
1077 Q_UNUSED(glyph);
1078 Q_UNUSED(matrix);
1079#endif
1080
1081 return QRect{};
1082}
1083
1084#if QT_CONFIG(directwritecolrv1)
1085bool QWindowsFontEngineDirectWrite::traverseColr1(IDWritePaintReader *paintReader,
1086 IDWriteFontFace7 *face7,
1087 const DWRITE_PAINT_ELEMENT *paintElement,
1088 QColrPaintGraphRenderer *paintGraphRenderer) const
1089{
1090 paintGraphRenderer->save();
1091 auto cleanup = qScopeGuard([&paintGraphRenderer]() {
1092 paintGraphRenderer->restore();
1093 });
1094
1095 auto traverseChildren = [&](quint32 childCount) {
1096 DWRITE_PAINT_ELEMENT childPaintElement;
1097 if (FAILED(paintReader->MoveToFirstChild(&childPaintElement, sizeof(DWRITE_PAINT_ELEMENT))))
1098 return false;
1099
1100 while (childCount-- > 0) {
1101 traverseColr1(paintReader, face7, &childPaintElement, paintGraphRenderer);
1102 if (childCount > 0) {
1103 if (FAILED(paintReader->MoveToNextSibling(&childPaintElement, sizeof(DWRITE_PAINT_ELEMENT)))) {
1104 return false;
1105 }
1106 }
1107 }
1108
1109 return SUCCEEDED(paintReader->MoveToParent());
1110 };
1111
1112 auto collectStops = [&paintReader](int stopCount) {
1113 QGradientStops ret;
1114
1115 QVarLengthArray<D2D1_GRADIENT_STOP> stops(stopCount);
1116 HRESULT hr = paintReader->GetGradientStops(0, stopCount, stops.data());
1117 if (FAILED(hr))
1118 return ret;
1119
1120 for (int i = 0; i < stopCount; ++i) {
1121 const D2D1_GRADIENT_STOP &stop = stops[i];
1122 QColor color = QColor::fromRgbF(stop.color.r, stop.color.g, stop.color.b, stop.color.a);
1123 ret.append({stop.position, color});
1124 }
1125
1126 return ret;
1127 };
1128
1129 switch (paintElement->paintType) {
1130 case DWRITE_PAINT_TYPE_LAYERS:
1131 {
1132 if (!traverseChildren(paintElement->paint.layers.childCount))
1133 return false;
1134
1135 break;
1136 }
1137
1138 case DWRITE_PAINT_TYPE_SOLID_GLYPH:
1139 {
1140 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.solidGlyph.glyphIndex);
1141
1142 QColor color = colorToColor(paintElement->paint.solidGlyph.color.value);
1143 paintGraphRenderer->setPath(glyphPath);
1144 paintGraphRenderer->setSolidColor(color);
1145 paintGraphRenderer->drawCurrentPath();
1146
1147 break;
1148 }
1149
1150 case DWRITE_PAINT_TYPE_SOLID:
1151 {
1152 QColor color = colorToColor(paintElement->paint.solid.value,
1153 paintElement->paint.solid.alphaMultiplier);
1154 paintGraphRenderer->setSolidColor(color);
1155 paintGraphRenderer->drawCurrentPath();
1156 break;
1157 }
1158
1159 case DWRITE_PAINT_TYPE_COMPOSITE:
1160 {
1161 if (!paintGraphRenderer->isRendering()) {
1162 traverseChildren(2);
1163 } else {
1164 DWRITE_PAINT_ELEMENT childElement;
1165
1166 HRESULT hr = paintReader->MoveToFirstChild(&childElement, sizeof(DWRITE_PAINT_ELEMENT));
1167 if (FAILED(hr)) {
1168 qErrnoWarning(hr, "%s: Cannot move to first child of composite node",
1169 __FUNCTION__);
1170 return false;
1171 }
1172
1173 // First draw back drop which is the second child
1174 hr = paintReader->MoveToNextSibling(&childElement, sizeof(DWRITE_PAINT_ELEMENT));
1175 if (FAILED(hr)) {
1176 qErrnoWarning(hr, "%s: Cannot move to second child of composite node",
1177 __FUNCTION__);
1178 return false;
1179 }
1180
1181 DWRITE_COLOR_COMPOSITE_MODE compositeMode = paintElement->paint.composite.mode;
1182
1183 QColrPaintGraphRenderer compositeRenderer;
1184 compositeRenderer.setBoundingRect(paintGraphRenderer->boundingRect());
1185 compositeRenderer.beginRender(fontDef.pixelSize / m_unitsPerEm,
1186 paintGraphRenderer->currentTransform());
1187 if (!traverseColr1(paintReader, face7, &childElement, &compositeRenderer))
1188 return false;
1189
1190 // Then draw source which is the first child
1191 hr = paintReader->MoveToParent();
1192 if (FAILED(hr)) {
1193 qErrnoWarning(hr, "%s: Cannot move back to parent composite node",
1194 __FUNCTION__);
1195 return false;
1196 }
1197
1198 hr = paintReader->MoveToFirstChild(&childElement, sizeof(DWRITE_PAINT_ELEMENT));
1199 if (FAILED(hr)) {
1200 qErrnoWarning(hr, "%s: Cannot move to first child of composite node",
1201 __FUNCTION__);
1202 return false;
1203 }
1204
1205 compositeRenderer.setCompositionMode(compositeToCompositionMode(compositeMode));
1206 if (!traverseColr1(paintReader, face7, &childElement, &compositeRenderer))
1207 return false;
1208
1209 hr = paintReader->MoveToParent();
1210 if (FAILED(hr)) {
1211 qErrnoWarning(hr, "%s: Cannot move back to parent composite node",
1212 __FUNCTION__);
1213 return false;
1214 }
1215
1216 paintGraphRenderer->drawImage(compositeRenderer.endRender());
1217 }
1218
1219 break;
1220 }
1221
1222 case DWRITE_PAINT_TYPE_RADIAL_GRADIENT:
1223 {
1224 const QPointF c0(paintElement->paint.radialGradient.x0 * m_unitsPerEm,
1225 paintElement->paint.radialGradient.y0 * m_unitsPerEm);
1226 const QPointF c1(paintElement->paint.radialGradient.x1 * m_unitsPerEm,
1227 paintElement->paint.radialGradient.y1 * m_unitsPerEm);
1228 const qreal r0 = paintElement->paint.radialGradient.radius0 * m_unitsPerEm;
1229 const qreal r1 = paintElement->paint.radialGradient.radius1 * m_unitsPerEm;
1230
1231 const QGradient::Spread spread
1232 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1233
1234 const QGradientStops gradientStops
1235 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1236
1237 paintGraphRenderer->setRadialGradient(c0, r0, c1, r1, spread, gradientStops);
1238 paintGraphRenderer->drawCurrentPath();
1239 break;
1240 }
1241
1242 case DWRITE_PAINT_TYPE_SWEEP_GRADIENT:
1243 {
1244 const QPointF center(paintElement->paint.sweepGradient.centerX * m_unitsPerEm,
1245 paintElement->paint.sweepGradient.centerY * m_unitsPerEm);
1246 const qreal startAngle = paintElement->paint.sweepGradient.startAngle;
1247 const qreal endAngle = paintElement->paint.sweepGradient.endAngle;
1248
1249 const QGradient::Spread spread
1250 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1251 const QGradientStops gradientStops
1252 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1253
1254 paintGraphRenderer->setConicalGradient(center,
1255 startAngle,
1256 endAngle,
1257 spread,
1258 gradientStops);
1259 paintGraphRenderer->drawCurrentPath();
1260 break;
1261 }
1262
1263 case DWRITE_PAINT_TYPE_LINEAR_GRADIENT:
1264 {
1265 const QPointF p0(paintElement->paint.linearGradient.x0 * m_unitsPerEm,
1266 paintElement->paint.linearGradient.y0 * m_unitsPerEm);
1267 const QPointF p1(paintElement->paint.linearGradient.x1 * m_unitsPerEm,
1268 paintElement->paint.linearGradient.y1 * m_unitsPerEm);
1269 const QPointF p2(paintElement->paint.linearGradient.x2 * m_unitsPerEm,
1270 paintElement->paint.linearGradient.y2 * m_unitsPerEm);
1271
1272 const QGradient::Spread spread
1273 = extendToSpread(paintElement->paint.linearGradient.extendMode);
1274 const QGradientStops gradientStops
1275 = collectStops(paintElement->paint.linearGradient.gradientStopCount);
1276
1277 paintGraphRenderer->setLinearGradient(p0, p1, p2, spread, gradientStops);
1278 paintGraphRenderer->drawCurrentPath();
1279 break;
1280 }
1281
1282 case DWRITE_PAINT_TYPE_GLYPH:
1283 {
1284 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.glyph.glyphIndex);
1285 paintGraphRenderer->appendPath(glyphPath);
1286 if (!traverseChildren(1))
1287 return false;
1288 break;
1289 }
1290
1291 case DWRITE_PAINT_TYPE_COLOR_GLYPH:
1292 {
1293 D2D_RECT_F rect = paintElement->paint.colorGlyph.clipBox;
1294 QRect clipBox = QRectF(QPointF(rect.left, rect.top),
1295 QPointF(rect.right, rect.bottom)).toAlignedRect();
1296 if (!clipBox.isEmpty()) {
1297 QTransform coordinatesTransform;
1298 coordinatesTransform.scale(m_unitsPerEm, m_unitsPerEm);
1299 clipBox = coordinatesTransform.mapRect(clipBox);
1300
1301 paintGraphRenderer->setClip(clipBox);
1302 }
1303
1304 DWRITE_PAINT_ELEMENT childElement;
1305 if (FAILED(paintReader->MoveToFirstChild(&childElement, sizeof(DWRITE_PAINT_ELEMENT))))
1306 return false;
1307
1308 if (!traverseColr1(paintReader, face7, &childElement, paintGraphRenderer))
1309 return false;
1310
1311 if (FAILED(paintReader->MoveToParent()))
1312 return false;
1313
1314 break;
1315 }
1316
1317 case DWRITE_PAINT_TYPE_TRANSFORM:
1318 {
1319 QTransform transform = matrixToTransform(paintElement->paint.transform, m_unitsPerEm);
1320 paintGraphRenderer->prependTransform(transform);
1321 if (!traverseChildren(1))
1322 return false;
1323
1324 break;
1325 }
1326
1327 default:
1328 qCDebug(lcColrv1) << "Unhandled paint graph node type" << paintElement->paintType;
1329 break;
1330 };
1331
1332 return true;
1333}
1334
1335bool QWindowsFontEngineDirectWrite::renderColr1GlyphRun(QImage *image,
1336 const DWRITE_GLYPH_RUN *glyphRun,
1337 const DWRITE_MATRIX &matrix,
1338 QColor color) const
1339{
1340 qCDebug(lcColrv1) << "renderColr1GlyphRun,"
1341 << "families:" << fontDef.families;
1342 ComPtr<IDWriteFontFace7> face7;
1343 HRESULT hr = m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace7),
1344 &face7);
1345 if (SUCCEEDED(hr)) {
1346 DWRITE_PAINT_FEATURE_LEVEL featureLevel =
1347 face7->GetPaintFeatureLevel(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE);
1348 if (featureLevel != DWRITE_PAINT_FEATURE_LEVEL_COLR_V1) {
1349 qCDebug(lcColrv1) << "Unsupported feature level:" << featureLevel;
1350 return true;
1351 }
1352
1353 ComPtr<IDWritePaintReader> paintReader;
1354 hr = face7->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
1355 featureLevel,
1356 &paintReader);
1357 if (FAILED(hr)) {
1358 qErrnoWarning(hr, "%s: CreatePaintReader failed", __FUNCTION__);
1359 return false;
1360 }
1361
1362 Q_ASSERT(glyphRun->glyphCount == 1);
1363 DWRITE_PAINT_ELEMENT paintElement;
1364 D2D_RECT_F clipBox;
1365 hr = paintReader->SetCurrentGlyph(glyphRun->glyphIndices[0],
1366 &paintElement,
1367 sizeof(DWRITE_PAINT_ELEMENT),
1368 &clipBox,
1369 nullptr);
1370 if (FAILED(hr)) {
1371 qErrnoWarning(hr, "%s: SetCurrentGlyph failed", __FUNCTION__);
1372 return false;
1373 }
1374
1375 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE) {
1376 qCDebug(lcColrv1) << "Glyph" << glyphRun->glyphIndices[0]
1377 << "does not have a paint graph";
1378 return false;
1379 }
1380
1381 DWRITE_COLOR_F dwColor;
1382 dwColor.r = color.redF();
1383 dwColor.g = color.greenF();
1384 dwColor.b = color.blueF();
1385 dwColor.a = color.alphaF();
1386 paintReader->SetTextColor(dwColor);
1387
1388 QRectF boundingRect = QRectF(QPointF(clipBox.left, clipBox.top),
1389 QPointF(clipBox.right, clipBox.bottom));
1390 if (boundingRect.isNull()) {
1391 QColrPaintGraphRenderer boundingRectCalculator;
1392 boundingRectCalculator.beginCalculateBoundingBox();
1393 if (traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &boundingRectCalculator))
1394 boundingRect = boundingRectCalculator.boundingRect();
1395 }
1396
1397 QTransform initialTransform;
1398 initialTransform.scale(fontDef.pixelSize, fontDef.pixelSize);
1399 boundingRect = initialTransform.mapRect(boundingRect);
1400
1401 QTransform originalXform = matrixToTransform(matrix);
1402 boundingRect = originalXform.mapRect(boundingRect);
1403
1404 qCDebug(lcColrv1).noquote() << "Bounds of"
1405 << glyphRun->glyphIndices[0]
1406 << " in device coordinates:"
1407 << boundingRect;
1408
1409 QColrPaintGraphRenderer graphRenderer;
1410 graphRenderer.setBoundingRect(boundingRect);
1411 graphRenderer.beginRender(fontDef.pixelSize / m_unitsPerEm, matrixToTransform(matrix));
1412 if (!traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &graphRenderer))
1413 return false;
1414
1415 *image = graphRenderer.endRender();
1416 } else {
1417 qCDebug(lcColrv1) << "Font face does not support IDWriteFontFace7 interface";
1418 }
1419
1420 return true;
1421}
1422#endif // QT_CONFIG(directwritecolrv1)
1423
1424QImage QWindowsFontEngineDirectWrite::renderColorGlyph(DWRITE_GLYPH_RUN *glyphRun,
1425 const DWRITE_MATRIX &transform,
1426 DWRITE_RENDERING_MODE renderMode,
1427 DWRITE_MEASURING_MODE measureMode,
1428 DWRITE_GRID_FIT_MODE gridFitMode,
1429 QColor color,
1430 QRect boundingRect) const
1431{
1432 QImage ret;
1433
1434#if QT_CONFIG(directwritecolrv1)
1435 // Start by trying COLRv1 glyph, where we need to get the paint nodes ourselves and render
1436 // them.
1437 renderColr1GlyphRun(&ret, glyphRun, transform, color);
1438#endif // QT_CONFIG(directwritecolrv1)
1439
1440#if QT_CONFIG(directwrite3)
1441 // If not successful, we try the modern API that supports both embedded pixmaps or COLRv0
1442 // glyphs, or a combination.
1443 if (ret.isNull()) {
1444 ComPtr<IDWriteFactory4> factory4;
1445 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory4),
1446 &factory4);
1447 if (SUCCEEDED(hr)) {
1448 const DWRITE_GLYPH_IMAGE_FORMATS supportedBitmapFormats =
1449 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_PNG
1450 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
1451 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF);
1452
1453 const DWRITE_GLYPH_IMAGE_FORMATS glyphFormats =
1454 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_COLR
1455 | supportedBitmapFormats
1456 | DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE
1457 | DWRITE_GLYPH_IMAGE_FORMATS_CFF);
1458
1459 ComPtr<IDWriteColorGlyphRunEnumerator1> enumerator;
1460 hr = factory4->TranslateColorGlyphRun(D2D1::Point2F(0.0f, 0.0f),
1461 glyphRun,
1462 NULL,
1463 glyphFormats,
1464 measureMode,
1465 NULL,
1466 0,
1467 &enumerator);
1468 BOOL ok = true;
1469 while (SUCCEEDED(hr) && ok) {
1470 hr = enumerator->MoveNext(&ok);
1471 if (!ok)
1472 break;
1473
1474 const DWRITE_COLOR_GLYPH_RUN1 *colorGlyphRun = nullptr;
1475 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1476 if (FAILED(hr)) {
1477 qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed", __FUNCTION__);
1478 return QImage{};
1479 }
1480
1481 if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_NONE) {
1482 // On some older platforms, we get a glyph run with format NONE before
1483 // the actual ones
1484 continue;
1485 } else if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_COLR) {
1486 if (ret.isNull()) {
1487 ret = QImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied);
1488 ret.fill(0);
1489 }
1490
1491 if (renderColr0GlyphRun(&ret,
1492 reinterpret_cast<const DWRITE_COLOR_GLYPH_RUN *>(colorGlyphRun), // Broken inheritance in MinGW
1493 transform,
1494 renderMode,
1495 measureMode,
1496 gridFitMode,
1497 color,
1498 boundingRect)) {
1499 break;
1500 }
1501 } else if (colorGlyphRun->glyphImageFormat & supportedBitmapFormats) {
1502 ComPtr<IDWriteFontFace4> face4;
1503 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
1504 &face4))) {
1505 DWRITE_GLYPH_IMAGE_DATA data;
1506 void *ctx;
1507 Q_ASSERT(glyphRun->glyphCount == 1);
1508 HRESULT hr = face4->GetGlyphImageData(glyphRun->glyphIndices[0],
1509 fontDef.pixelSize,
1510 DWRITE_GLYPH_IMAGE_FORMATS(colorGlyphRun->glyphImageFormat & supportedBitmapFormats),
1511 &data,
1512 &ctx);
1513 if (FAILED(hr)) {
1514 qErrnoWarning("%s: GetGlyphImageData failed", __FUNCTION__);
1515 return QImage{};
1516 }
1517
1518 auto fnc = qScopeGuard([&]() {
1519 if (data.imageData != nullptr)
1520 face4->ReleaseGlyphImageData(ctx);
1521 });
1522
1523 if (data.pixelsPerEm == 0 || data.imageData == nullptr || data.imageDataSize == 0) {
1524 qCWarning(lcQpaFonts) << __FUNCTION__
1525 << "Failed to retrieve image data for glyph"
1526 << glyphRun->glyphIndices[0]
1527 << "in font:"
1528 << fontDef.families
1529 << "with formats:"
1530 << colorGlyphRun->glyphImageFormat;
1531 } else {
1532 const char *format;
1533 switch (colorGlyphRun->glyphImageFormat) {
1534 case DWRITE_GLYPH_IMAGE_FORMATS_JPEG:
1535 format = "JPEG";
1536 break;
1537 case DWRITE_GLYPH_IMAGE_FORMATS_TIFF:
1538 format = "TIFF";
1539 break;
1540 default:
1541 format = "PNG";
1542 break;
1543 };
1544
1545 ret = QImage::fromData(reinterpret_cast<const uchar *>(data.imageData),
1546 data.imageDataSize,
1547 format);
1548
1549 QTransform matrix(transform.m11, transform.m12,
1550 transform.m21, transform.m22,
1551 transform.dx, transform.dy);
1552
1553 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
1554 matrix.scale(scale, scale);
1555
1556 if (!matrix.isIdentity())
1557 ret = ret.transformed(matrix, Qt::SmoothTransformation);
1558
1559 break;
1560 }
1561 }
1562
1563 } else {
1564 qCDebug(lcQpaFonts) << "Found glyph run with unsupported format"
1565 << colorGlyphRun->glyphImageFormat;
1566 }
1567 }
1568 }
1569 }
1570#endif // QT_CONFIG(directwrite3)
1571
1572 // If all else fails, we go through the pre-dwrite3 version, which just supports COLRv0.
1573 if (ret.isNull()) {
1574 ComPtr<IDWriteFactory2> factory2;
1575 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1576 &factory2);
1577 if (FAILED(hr))
1578 return ret;
1579
1580 ComPtr<IDWriteColorGlyphRunEnumerator> enumerator;
1581 hr = factory2->TranslateColorGlyphRun(0.0f,
1582 0.0f,
1583 glyphRun,
1584 NULL,
1585 measureMode,
1586 NULL,
1587 0,
1588 &enumerator);
1589 if (SUCCEEDED(hr)) {
1590 ret = QImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied);
1591 ret.fill(0);
1592
1593 BOOL ok = true;
1594 while (SUCCEEDED(hr) && ok) {
1595 hr = enumerator->MoveNext(&ok);
1596 if (FAILED(hr)) {
1597 qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::MoveNext failed", __FUNCTION__);
1598 return QImage{};
1599 }
1600
1601 if (ok) {
1602 const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun = nullptr;
1603 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1604 if (FAILED(hr)) { // No colored runs, only outline
1605 qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed", __FUNCTION__);
1606 return QImage{};
1607 }
1608
1609 if (!renderColr0GlyphRun(&ret,
1610 colorGlyphRun,
1611 transform,
1612 renderMode,
1613 measureMode,
1614 gridFitMode,
1615 color,
1616 boundingRect)) {
1617 return QImage{};
1618 }
1619 }
1620 }
1621 }
1622 }
1623
1624 return ret;
1625}
1626
1627QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
1628 const QFixedPoint &subPixelPosition,
1629 int margin,
1630 const QTransform &originalTransform,
1631 const QColor &color)
1632{
1633 UINT16 glyphIndex = t;
1634 FLOAT glyphAdvance = 0;
1635
1636 DWRITE_GLYPH_OFFSET glyphOffset;
1637 glyphOffset.advanceOffset = 0;
1638 glyphOffset.ascenderOffset = 0;
1639
1640 DWRITE_GLYPH_RUN glyphRun;
1641 glyphRun.fontFace = m_directWriteFontFace;
1642 glyphRun.fontEmSize = fontDef.pixelSize;
1643 glyphRun.glyphCount = 1;
1644 glyphRun.glyphIndices = &glyphIndex;
1645 glyphRun.glyphAdvances = &glyphAdvance;
1646 glyphRun.isSideways = false;
1647 glyphRun.bidiLevel = 0;
1648 glyphRun.glyphOffsets = &glyphOffset;
1649
1650 QTransform xform = originalTransform;
1651 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
1652 xform.scale(fontDef.stretch / 100.0, 1.0);
1653
1654 DWRITE_MATRIX transform;
1655 transform.dx = subPixelPosition.x.toReal();
1656 transform.dy = 0;
1657 transform.m11 = xform.m11();
1658 transform.m12 = xform.m12();
1659 transform.m21 = xform.m21();
1660 transform.m22 = xform.m22();
1661
1662 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1663 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1664 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1665 ? DWRITE_GRID_FIT_MODE_DISABLED
1666 : DWRITE_GRID_FIT_MODE_DEFAULT;
1667
1668 ComPtr<IDWriteFactory2> factory2;
1669 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1670 &factory2);
1671 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1672 if (!SUCCEEDED(hr)) {
1673 qErrnoWarning(hr, "%s: Failed to query IDWriteFactory2 interface.", __FUNCTION__);
1674 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1675 &glyphRun,
1676 1.0f,
1677 &transform,
1678 renderMode,
1679 measureMode,
1680 0.0, 0.0,
1681 &glyphAnalysis
1682 );
1683 } else {
1684 hr = factory2->CreateGlyphRunAnalysis(
1685 &glyphRun,
1686 &transform,
1687 renderMode,
1688 measureMode,
1689 gridFitMode,
1690 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1691 0.0, 0.0,
1692 &glyphAnalysis
1693 );
1694 }
1695
1696 if (SUCCEEDED(hr)) {
1697 QRect rect = paintGraphBounds(t, transform);
1698 if (rect.isEmpty())
1699 rect = alphaTextureBounds(t, transform);
1700 if (rect.isEmpty())
1701 rect = colorBitmapBounds(t, transform);
1702
1703 if (rect.isEmpty()) {
1704 qCDebug(lcQpaFonts) << __FUNCTION__ << "Cannot get alpha texture bounds. Falling back to slower rendering path.";
1705 return QImage();
1706 }
1707
1708 if (glyphBoundingBoxExceedsLimit(rect.width(),
1709 rect.height(),
1710 effectivePixelSizeSquared(xform))) {
1711 return QImage();
1712 }
1713
1714 QRect boundingRect = rect.adjusted(-margin, -margin, margin, margin);
1715
1716 QImage image;
1717 if (glyphFormat == QFontEngine::Format_ARGB) {
1718 image = renderColorGlyph(&glyphRun,
1719 transform,
1720 renderMode,
1721 measureMode,
1722 gridFitMode,
1723 color,
1724 boundingRect);
1725 }
1726
1727 // Not a color glyph, fall back to regular glyph rendering
1728 if (image.isNull()) {
1729 image = QImage(boundingRect.size(),
1730 glyphFormat == QFontEngine::Format_ARGB
1731 ? QImage::Format_ARGB32_Premultiplied
1732 : QImage::Format_RGB32);
1733 image.fill(image.format() == QImage::Format_ARGB32_Premultiplied ? 0x0 : 0xffffffff);
1734
1735 float r, g, b, a;
1736 if (glyphFormat == QFontEngine::Format_ARGB) {
1737 r = float(color.redF());
1738 g = float(color.greenF());
1739 b = float(color.blueF());
1740 a = float(color.alphaF());
1741 } else {
1742 r = g = b = a = 0.0;
1743 }
1744
1745 renderGlyphRun(&image,
1746 r,
1747 g,
1748 b,
1749 a,
1750 glyphAnalysis.Get(),
1751 boundingRect,
1752 renderMode);
1753 }
1754
1755 return image;
1756 } else {
1757 qErrnoWarning(hr, "%s: CreateGlyphRunAnalysis failed", __FUNCTION__);
1758 return QImage();
1759 }
1760}
1761
1762void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
1763 float r,
1764 float g,
1765 float b,
1766 float a,
1767 IDWriteGlyphRunAnalysis *glyphAnalysis,
1768 const QRect &boundingRect,
1769 DWRITE_RENDERING_MODE renderMode) const
1770{
1771 const int width = destination->width();
1772 const int height = destination->height();
1773
1774 r *= 255.0;
1775 g *= 255.0;
1776 b *= 255.0;
1777
1778 const int size = width * height * 3;
1779 if (size > 0) {
1780 RECT rect;
1781 rect.left = boundingRect.left();
1782 rect.top = boundingRect.top();
1783 rect.right = boundingRect.left() + boundingRect.width();
1784 rect.bottom = boundingRect.top() + boundingRect.height();
1785
1786 QVarLengthArray<BYTE, 1024> alphaValueArray(size);
1787 BYTE *alphaValues = alphaValueArray.data();
1788 memset(alphaValues, 0, size);
1789
1790 HRESULT hr = glyphAnalysis->CreateAlphaTexture(renderMode == DWRITE_RENDERING_MODE_ALIASED
1791 ? DWRITE_TEXTURE_ALIASED_1x1
1792 : DWRITE_TEXTURE_CLEARTYPE_3x1,
1793 &rect,
1794 alphaValues,
1795 size);
1796 if (SUCCEEDED(hr)) {
1797 if (destination->hasAlphaChannel()) { // Color glyphs
1798 for (int y = 0; y < height; ++y) {
1799 uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
1800 BYTE *src = alphaValues + width * 3 * y;
1801
1802 for (int x = 0; x < width; ++x) {
1803 float redAlpha = a * *src++ / 255.0;
1804 float greenAlpha = a * *src++ / 255.0;
1805 float blueAlpha = a * *src++ / 255.0;
1806 float averageAlpha = (redAlpha + greenAlpha + blueAlpha) / 3.0;
1807
1808 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1809 qSwap(redAlpha, blueAlpha);
1810
1811 QRgb currentRgb = dest[x];
1812 dest[x] = qRgba(qRound(qRed(currentRgb) * (1.0 - averageAlpha) + averageAlpha * r),
1813 qRound(qGreen(currentRgb) * (1.0 - averageAlpha) + averageAlpha * g),
1814 qRound(qBlue(currentRgb) * (1.0 - averageAlpha) + averageAlpha * b),
1815 qRound(qAlpha(currentRgb) * (1.0 - averageAlpha) + averageAlpha * 255));
1816 }
1817 }
1818 } else if (renderMode == DWRITE_RENDERING_MODE_ALIASED) {
1819 for (int y = 0; y < height; ++y) {
1820 uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
1821 BYTE *src = alphaValues + width * y;
1822
1823 for (int x = 0; x < width; ++x) {
1824 int alpha = *(src++);
1825 dest[x] = (alpha << 16) + (alpha << 8) + alpha;
1826 }
1827 }
1828 } else {
1829 for (int y = 0; y < height; ++y) {
1830 uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
1831 BYTE *src = alphaValues + width * 3 * y;
1832
1833 for (int x = 0; x < width; ++x) {
1834 BYTE redAlpha = *(src + 0);
1835 BYTE greenAlpha = *(src + 1);
1836 BYTE blueAlpha = *(src + 2);
1837
1838 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1839 qSwap(redAlpha, blueAlpha);
1840
1841 dest[x] = qRgb(redAlpha, greenAlpha, blueAlpha);
1842 src += 3;
1843 }
1844 }
1845 }
1846 } else {
1847 qErrnoWarning("%s: CreateAlphaTexture failed", __FUNCTION__);
1848 }
1849 }
1850}
1851
1852QImage QWindowsFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t,
1853 const QFixedPoint &subPixelPosition,
1854 const QTransform &xform)
1855{
1856 QImage mask = imageForGlyph(t,
1857 subPixelPosition,
1858 glyphMargin(QFontEngine::Format_A32),
1859 xform);
1860
1861 if (mask.isNull()) {
1862 if (xform.type() > QTransform::TxTranslate && glyphTransformExceedsLimit(t, xform))
1863 return QImage();
1864
1865 mask = QFontEngine::renderedPathForGlyph(t, Qt::white);
1866 if (!xform.isIdentity())
1867 mask = mask.transformed(xform);
1868 }
1869
1870 return mask.depth() == 32
1871 ? mask
1872 : mask.convertToFormat(QImage::Format_RGB32);
1873}
1874
1875QFontEngine *QWindowsFontEngineDirectWrite::cloneWithSize(qreal pixelSize) const
1876{
1877 QWindowsFontEngineDirectWrite *fontEngine = new QWindowsFontEngineDirectWrite(m_directWriteFontFace,
1878 pixelSize,
1879 m_fontEngineData);
1880
1881 fontEngine->fontDef = fontDef;
1882 fontEngine->fontDef.pixelSize = pixelSize;
1883 if (!m_uniqueFamilyName.isEmpty()) {
1884 fontEngine->setUniqueFamilyName(m_uniqueFamilyName);
1885 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
1886 static_cast<QWindowsFontDatabase *>(pfdb)->refUniqueFont(m_uniqueFamilyName);
1887 }
1888
1889 return fontEngine;
1890}
1891
1892Qt::HANDLE QWindowsFontEngineDirectWrite::handle() const
1893{
1894 return m_directWriteFontFace;
1895}
1896
1897void QWindowsFontEngineDirectWrite::initFontInfo(const QFontDef &request,
1898 int dpi)
1899{
1900 fontDef = request;
1901
1902 if (fontDef.pointSize < 0)
1903 fontDef.pointSize = fontDef.pixelSize * 72. / dpi;
1904 else if (fontDef.pixelSize == -1)
1905 fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.);
1906
1907 m_faceId.variableAxes = request.variableAxisValues;
1908
1909#if QT_CONFIG(directwrite3)
1910 IDWriteFontFace3 *face3 = nullptr;
1911 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace3),
1912 reinterpret_cast<void **>(&face3)))) {
1913
1914 fontDef.weight = QWindowsDirectWriteFontDatabase::fromDirectWriteWeight(face3->GetWeight());
1915 fontDef.style = QWindowsDirectWriteFontDatabase::fromDirectWriteStyle(face3->GetStyle());
1916
1917 IDWriteLocalizedStrings *names;
1918 if (SUCCEEDED(face3->GetFaceNames(&names))) {
1919 wchar_t englishLocale[] = L"en-us";
1920 fontDef.styleName = QWindowsDirectWriteFontDatabase::localeString(names, englishLocale);
1921 names->Release();
1922 }
1923
1924 // Color font
1925 if (face3->IsColorFont())
1926 glyphFormat = QFontEngine::Format_ARGB;
1927
1928 face3->Release();
1929 }
1930#endif
1931}
1932
1933QString QWindowsFontEngineDirectWrite::fontNameSubstitute(const QString &familyName)
1934{
1935 const QString substitute =
1936 QWinRegistryKey(HKEY_LOCAL_MACHINE,
1937 LR"(Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes)")
1938 .stringValue(familyName);
1939 return substitute.isEmpty() ? familyName : substitute;
1940}
1941
1942QRect QWindowsFontEngineDirectWrite::alphaTextureBounds(glyph_t glyph,
1943 const DWRITE_MATRIX &transform)
1944{
1945 UINT16 glyphIndex = glyph;
1946 FLOAT glyphAdvance = 0;
1947
1948 DWRITE_GLYPH_OFFSET glyphOffset;
1949 glyphOffset.advanceOffset = 0;
1950 glyphOffset.ascenderOffset = 0;
1951
1952 DWRITE_GLYPH_RUN glyphRun;
1953 glyphRun.fontFace = m_directWriteFontFace;
1954 glyphRun.fontEmSize = fontDef.pixelSize;
1955 glyphRun.glyphCount = 1;
1956 glyphRun.glyphIndices = &glyphIndex;
1957 glyphRun.glyphAdvances = &glyphAdvance;
1958 glyphRun.isSideways = false;
1959 glyphRun.bidiLevel = 0;
1960 glyphRun.glyphOffsets = &glyphOffset;
1961
1962 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1963 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1964 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1965 ? DWRITE_GRID_FIT_MODE_DISABLED
1966 : DWRITE_GRID_FIT_MODE_DEFAULT;
1967
1968 ComPtr<IDWriteFactory2> factory2 = nullptr;
1969 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1970 &factory2);
1971
1972 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1973 if (SUCCEEDED(hr)) {
1974 hr = factory2->CreateGlyphRunAnalysis(
1975 &glyphRun,
1976 &transform,
1977 renderMode,
1978 measureMode,
1979 gridFitMode,
1980 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1981 0.0, 0.0,
1982 &glyphAnalysis
1983 );
1984 } else {
1985 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1986 &glyphRun,
1987 1.0f,
1988 &transform,
1989 renderMode,
1990 measureMode,
1991 0.0, 0.0,
1992 &glyphAnalysis
1993 );
1994 }
1995
1996 if (SUCCEEDED(hr)) {
1997 RECT rect;
1998 hr = glyphAnalysis->GetAlphaTextureBounds(renderMode == DWRITE_RENDERING_MODE_ALIASED
1999 ? DWRITE_TEXTURE_ALIASED_1x1
2000 : DWRITE_TEXTURE_CLEARTYPE_3x1,
2001 &rect);
2002 if (FAILED(hr) || rect.left == rect.right || rect.top == rect.bottom)
2003 return QRect{};
2004
2005 return QRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
2006 } else {
2007 return QRect{};
2008 }
2009}
2010
2011QRect QWindowsFontEngineDirectWrite::colorBitmapBounds(glyph_t glyph, const DWRITE_MATRIX &transform)
2012{
2013#if QT_CONFIG(directwrite3)
2014 ComPtr<IDWriteFontFace4> face4;
2015 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
2016 &face4))) {
2017 DWRITE_GLYPH_IMAGE_FORMATS formats = face4->GetGlyphImageFormats();
2018
2019 const DWRITE_GLYPH_IMAGE_FORMATS supportedBitmapFormats =
2020 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_PNG
2021 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
2022 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF);
2023
2024 if (formats & supportedBitmapFormats) {
2025 DWRITE_GLYPH_IMAGE_DATA data;
2026 void *ctx;
2027 HRESULT hr = face4->GetGlyphImageData(glyph,
2028 fontDef.pixelSize,
2029 DWRITE_GLYPH_IMAGE_FORMATS(formats & supportedBitmapFormats),
2030 &data,
2031 &ctx);
2032 if (FAILED(hr)) {
2033 qErrnoWarning("%s: GetGlyphImageData failed", __FUNCTION__);
2034 return QRect{};
2035 }
2036
2037 auto fnc = qScopeGuard([&]() {
2038 if (data.imageData != nullptr)
2039 face4->ReleaseGlyphImageData(ctx);
2040 });
2041
2042 if (data.pixelsPerEm == 0 || data.imageData == nullptr || data.imageDataSize == 0)
2043 return QRect{};
2044
2045 QRect rect(-data.horizontalLeftOrigin.x,
2046 -data.horizontalLeftOrigin.y,
2047 data.pixelSize.width,
2048 data.pixelSize.height);
2049
2050 QTransform matrix(transform.m11, transform.m12,
2051 transform.m21, transform.m22,
2052 transform.dx, transform.dy);
2053
2054 // GetGlyphImageData returns the closest matching size, which we need to scale down
2055 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
2056 matrix.scale(scale, scale);
2057 rect = matrix.mapRect(rect);
2058
2059 return rect;
2060 }
2061 }
2062
2063 return QRect{};
2064#else
2065 Q_UNUSED(glyph);
2066 Q_UNUSED(transform);
2067 return QRect{};
2068#endif // QT_CONFIG(directwrite3)
2069}
2070
2071glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph,
2072 const QFixedPoint &subPixelPosition,
2073 const QTransform &originalTransform,
2074 GlyphFormat format)
2075{
2076 QTransform matrix = originalTransform;
2077 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
2078 matrix.scale(fontDef.stretch / 100.0, 1.0);
2079
2080 glyph_metrics_t bbox = QFontEngine::boundingBox(glyph, matrix); // To get transformed advance
2081
2082 DWRITE_MATRIX transform;
2083 transform.dx = subPixelPosition.x.toReal();
2084 transform.dy = 0;
2085 transform.m11 = matrix.m11();
2086 transform.m12 = matrix.m12();
2087 transform.m21 = matrix.m21();
2088 transform.m22 = matrix.m22();
2089
2090 // Try COLRv1 approach first
2091 QRect rect = paintGraphBounds(glyph, transform);
2092
2093 // Then try general approach (works with regular truetype glyphs as well as COLRv0)
2094 if (rect.isEmpty())
2095 rect = alphaTextureBounds(glyph, transform);
2096
2097 // If this fails, we check if it is an embedded color bitmap
2098 if (rect.isEmpty())
2099 rect = colorBitmapBounds(glyph, transform);
2100
2101 // If we are unable to find metrics, just return the design metrics
2102 if (rect.isEmpty())
2103 return bbox;
2104
2105 int margin = glyphMargin(format);
2106 return glyph_metrics_t(rect.left(),
2107 rect.top(),
2108 rect.width() + margin * 2,
2109 rect.height() + margin * 2,
2110 bbox.xoff, bbox.yoff);
2111}
2112
2113QImage QWindowsFontEngineDirectWrite::bitmapForGlyph(glyph_t glyph,
2114 const QFixedPoint &subPixelPosition,
2115 const QTransform &t,
2116 const QColor &color)
2117{
2118 return imageForGlyph(glyph, subPixelPosition, glyphMargin(QFontEngine::Format_ARGB), t, color);
2119}
2120
2121QList<QFontVariableAxis> QWindowsFontEngineDirectWrite::variableAxes() const
2122{
2123 return m_variableAxes;
2124}
2125
2126bool QWindowsFontEngineDirectWrite::expectsGammaCorrectedBlending(QFontEngine::GlyphFormat format) const
2127{
2128 return format == QFontEngine::Format_A32;
2129}
2130
2131QT_END_NAMESPACE
Combined button and popup list for selecting options.
static UUID uuidIdWriteLocalFontFileLoader()
IDWriteLocalFontFileLoader QIdWriteLocalFontFileLoader
static QFont::HintingPreference determineHinting(const QFontDef &fontDef)
#define DESIGN_TO_LOGICAL(DESIGN_UNIT_VALUE)
static DWRITE_MEASURING_MODE renderModeToMeasureMode(DWRITE_RENDERING_MODE renderMode)