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, 0))
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,
1089 int depth) const
1090{
1091 if (maxColrv1NestingDepth() > 0 && depth > maxColrv1NestingDepth()) {
1092 qCWarning(lcColrv1) << "COLRv1 graph nested deeper than" << maxColrv1NestingDepth()
1093 << "levels; aborting traversal";
1094 return false;
1095 }
1096
1097 paintGraphRenderer->save();
1098 auto cleanup = qScopeGuard([&paintGraphRenderer]() {
1099 paintGraphRenderer->restore();
1100 });
1101
1102 auto traverseChildren = [&](quint32 childCount) {
1103 DWRITE_PAINT_ELEMENT childPaintElement;
1104 if (FAILED(paintReader->MoveToFirstChild(&childPaintElement, sizeof(DWRITE_PAINT_ELEMENT))))
1105 return false;
1106
1107 while (childCount-- > 0) {
1108 traverseColr1(paintReader, face7, &childPaintElement, paintGraphRenderer, depth + 1);
1109 if (childCount > 0) {
1110 if (FAILED(paintReader->MoveToNextSibling(&childPaintElement, sizeof(DWRITE_PAINT_ELEMENT)))) {
1111 return false;
1112 }
1113 }
1114 }
1115
1116 return SUCCEEDED(paintReader->MoveToParent());
1117 };
1118
1119 auto collectStops = [&paintReader](int stopCount) {
1120 QGradientStops ret;
1121
1122 QVarLengthArray<D2D1_GRADIENT_STOP> stops(stopCount);
1123 HRESULT hr = paintReader->GetGradientStops(0, stopCount, stops.data());
1124 if (FAILED(hr))
1125 return ret;
1126
1127 for (int i = 0; i < stopCount; ++i) {
1128 const D2D1_GRADIENT_STOP &stop = stops[i];
1129 QColor color = QColor::fromRgbF(stop.color.r, stop.color.g, stop.color.b, stop.color.a);
1130 ret.append({stop.position, color});
1131 }
1132
1133 return ret;
1134 };
1135
1136 switch (paintElement->paintType) {
1137 case DWRITE_PAINT_TYPE_LAYERS:
1138 {
1139 if (!traverseChildren(paintElement->paint.layers.childCount))
1140 return false;
1141
1142 break;
1143 }
1144
1145 case DWRITE_PAINT_TYPE_SOLID_GLYPH:
1146 {
1147 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.solidGlyph.glyphIndex);
1148
1149 QColor color = colorToColor(paintElement->paint.solidGlyph.color.value);
1150 paintGraphRenderer->setPath(glyphPath);
1151 paintGraphRenderer->setSolidColor(color);
1152 paintGraphRenderer->drawCurrentPath();
1153
1154 break;
1155 }
1156
1157 case DWRITE_PAINT_TYPE_SOLID:
1158 {
1159 QColor color = colorToColor(paintElement->paint.solid.value,
1160 paintElement->paint.solid.alphaMultiplier);
1161 paintGraphRenderer->setSolidColor(color);
1162 paintGraphRenderer->drawCurrentPath();
1163 break;
1164 }
1165
1166 case DWRITE_PAINT_TYPE_COMPOSITE:
1167 {
1168 if (!paintGraphRenderer->isRendering()) {
1169 traverseChildren(2);
1170 } else {
1171 DWRITE_PAINT_ELEMENT childElement;
1172
1173 HRESULT hr = paintReader->MoveToFirstChild(&childElement, sizeof(DWRITE_PAINT_ELEMENT));
1174 if (FAILED(hr)) {
1175 qErrnoWarning(hr, "%s: Cannot move to first child of composite node",
1176 __FUNCTION__);
1177 return false;
1178 }
1179
1180 // First draw back drop which is the second child
1181 hr = paintReader->MoveToNextSibling(&childElement, sizeof(DWRITE_PAINT_ELEMENT));
1182 if (FAILED(hr)) {
1183 qErrnoWarning(hr, "%s: Cannot move to second child of composite node",
1184 __FUNCTION__);
1185 return false;
1186 }
1187
1188 DWRITE_COLOR_COMPOSITE_MODE compositeMode = paintElement->paint.composite.mode;
1189
1190 QColrPaintGraphRenderer compositeRenderer;
1191 compositeRenderer.setBoundingRect(paintGraphRenderer->boundingRect());
1192 compositeRenderer.beginRender(fontDef.pixelSize / m_unitsPerEm,
1193 paintGraphRenderer->currentTransform());
1194 if (!traverseColr1(paintReader, face7, &childElement, &compositeRenderer, depth + 1))
1195 return false;
1196
1197 // Then draw source which is the first child
1198 hr = paintReader->MoveToParent();
1199 if (FAILED(hr)) {
1200 qErrnoWarning(hr, "%s: Cannot move back to parent composite node",
1201 __FUNCTION__);
1202 return false;
1203 }
1204
1205 hr = paintReader->MoveToFirstChild(&childElement, sizeof(DWRITE_PAINT_ELEMENT));
1206 if (FAILED(hr)) {
1207 qErrnoWarning(hr, "%s: Cannot move to first child of composite node",
1208 __FUNCTION__);
1209 return false;
1210 }
1211
1212 compositeRenderer.setCompositionMode(compositeToCompositionMode(compositeMode));
1213 if (!traverseColr1(paintReader, face7, &childElement, &compositeRenderer, depth + 1))
1214 return false;
1215
1216 hr = paintReader->MoveToParent();
1217 if (FAILED(hr)) {
1218 qErrnoWarning(hr, "%s: Cannot move back to parent composite node",
1219 __FUNCTION__);
1220 return false;
1221 }
1222
1223 paintGraphRenderer->drawImage(compositeRenderer.endRender());
1224 }
1225
1226 break;
1227 }
1228
1229 case DWRITE_PAINT_TYPE_RADIAL_GRADIENT:
1230 {
1231 const QPointF c0(paintElement->paint.radialGradient.x0 * m_unitsPerEm,
1232 paintElement->paint.radialGradient.y0 * m_unitsPerEm);
1233 const QPointF c1(paintElement->paint.radialGradient.x1 * m_unitsPerEm,
1234 paintElement->paint.radialGradient.y1 * m_unitsPerEm);
1235 const qreal r0 = paintElement->paint.radialGradient.radius0 * m_unitsPerEm;
1236 const qreal r1 = paintElement->paint.radialGradient.radius1 * m_unitsPerEm;
1237
1238 const QGradient::Spread spread
1239 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1240
1241 const QGradientStops gradientStops
1242 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1243
1244 paintGraphRenderer->setRadialGradient(c0, r0, c1, r1, spread, gradientStops);
1245 paintGraphRenderer->drawCurrentPath();
1246 break;
1247 }
1248
1249 case DWRITE_PAINT_TYPE_SWEEP_GRADIENT:
1250 {
1251 const QPointF center(paintElement->paint.sweepGradient.centerX * m_unitsPerEm,
1252 paintElement->paint.sweepGradient.centerY * m_unitsPerEm);
1253 const qreal startAngle = paintElement->paint.sweepGradient.startAngle;
1254 const qreal endAngle = paintElement->paint.sweepGradient.endAngle;
1255
1256 const QGradient::Spread spread
1257 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1258 const QGradientStops gradientStops
1259 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1260
1261 paintGraphRenderer->setConicalGradient(center,
1262 startAngle,
1263 endAngle,
1264 spread,
1265 gradientStops);
1266 paintGraphRenderer->drawCurrentPath();
1267 break;
1268 }
1269
1270 case DWRITE_PAINT_TYPE_LINEAR_GRADIENT:
1271 {
1272 const QPointF p0(paintElement->paint.linearGradient.x0 * m_unitsPerEm,
1273 paintElement->paint.linearGradient.y0 * m_unitsPerEm);
1274 const QPointF p1(paintElement->paint.linearGradient.x1 * m_unitsPerEm,
1275 paintElement->paint.linearGradient.y1 * m_unitsPerEm);
1276 const QPointF p2(paintElement->paint.linearGradient.x2 * m_unitsPerEm,
1277 paintElement->paint.linearGradient.y2 * m_unitsPerEm);
1278
1279 const QGradient::Spread spread
1280 = extendToSpread(paintElement->paint.linearGradient.extendMode);
1281 const QGradientStops gradientStops
1282 = collectStops(paintElement->paint.linearGradient.gradientStopCount);
1283
1284 paintGraphRenderer->setLinearGradient(p0, p1, p2, spread, gradientStops);
1285 paintGraphRenderer->drawCurrentPath();
1286 break;
1287 }
1288
1289 case DWRITE_PAINT_TYPE_GLYPH:
1290 {
1291 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.glyph.glyphIndex);
1292 paintGraphRenderer->appendPath(glyphPath);
1293 if (!traverseChildren(1))
1294 return false;
1295 break;
1296 }
1297
1298 case DWRITE_PAINT_TYPE_COLOR_GLYPH:
1299 {
1300 D2D_RECT_F rect = paintElement->paint.colorGlyph.clipBox;
1301 QRect clipBox = QRectF(QPointF(rect.left, rect.top),
1302 QPointF(rect.right, rect.bottom)).toAlignedRect();
1303 if (!clipBox.isEmpty()) {
1304 QTransform coordinatesTransform;
1305 coordinatesTransform.scale(m_unitsPerEm, m_unitsPerEm);
1306 clipBox = coordinatesTransform.mapRect(clipBox);
1307
1308 paintGraphRenderer->setClip(clipBox);
1309 }
1310
1311 DWRITE_PAINT_ELEMENT childElement;
1312 if (FAILED(paintReader->MoveToFirstChild(&childElement, sizeof(DWRITE_PAINT_ELEMENT))))
1313 return false;
1314
1315 if (!traverseColr1(paintReader, face7, &childElement, paintGraphRenderer, depth + 1))
1316 return false;
1317
1318 if (FAILED(paintReader->MoveToParent()))
1319 return false;
1320
1321 break;
1322 }
1323
1324 case DWRITE_PAINT_TYPE_TRANSFORM:
1325 {
1326 QTransform transform = matrixToTransform(paintElement->paint.transform, m_unitsPerEm);
1327 paintGraphRenderer->prependTransform(transform);
1328 if (!traverseChildren(1))
1329 return false;
1330
1331 break;
1332 }
1333
1334 default:
1335 qCDebug(lcColrv1) << "Unhandled paint graph node type" << paintElement->paintType;
1336 break;
1337 };
1338
1339 return true;
1340}
1341
1342bool QWindowsFontEngineDirectWrite::renderColr1GlyphRun(QImage *image,
1343 const DWRITE_GLYPH_RUN *glyphRun,
1344 const DWRITE_MATRIX &matrix,
1345 QColor color) const
1346{
1347 qCDebug(lcColrv1) << "renderColr1GlyphRun,"
1348 << "families:" << fontDef.families;
1349 ComPtr<IDWriteFontFace7> face7;
1350 HRESULT hr = m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace7),
1351 &face7);
1352 if (SUCCEEDED(hr)) {
1353 DWRITE_PAINT_FEATURE_LEVEL featureLevel =
1354 face7->GetPaintFeatureLevel(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE);
1355 if (featureLevel != DWRITE_PAINT_FEATURE_LEVEL_COLR_V1) {
1356 qCDebug(lcColrv1) << "Unsupported feature level:" << featureLevel;
1357 return true;
1358 }
1359
1360 ComPtr<IDWritePaintReader> paintReader;
1361 hr = face7->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
1362 featureLevel,
1363 &paintReader);
1364 if (FAILED(hr)) {
1365 qErrnoWarning(hr, "%s: CreatePaintReader failed", __FUNCTION__);
1366 return false;
1367 }
1368
1369 Q_ASSERT(glyphRun->glyphCount == 1);
1370 DWRITE_PAINT_ELEMENT paintElement;
1371 D2D_RECT_F clipBox;
1372 hr = paintReader->SetCurrentGlyph(glyphRun->glyphIndices[0],
1373 &paintElement,
1374 sizeof(DWRITE_PAINT_ELEMENT),
1375 &clipBox,
1376 nullptr);
1377 if (FAILED(hr)) {
1378 qErrnoWarning(hr, "%s: SetCurrentGlyph failed", __FUNCTION__);
1379 return false;
1380 }
1381
1382 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE) {
1383 qCDebug(lcColrv1) << "Glyph" << glyphRun->glyphIndices[0]
1384 << "does not have a paint graph";
1385 return false;
1386 }
1387
1388 DWRITE_COLOR_F dwColor;
1389 dwColor.r = color.redF();
1390 dwColor.g = color.greenF();
1391 dwColor.b = color.blueF();
1392 dwColor.a = color.alphaF();
1393 paintReader->SetTextColor(dwColor);
1394
1395 QRectF boundingRect = QRectF(QPointF(clipBox.left, clipBox.top),
1396 QPointF(clipBox.right, clipBox.bottom));
1397 if (boundingRect.isNull()) {
1398 QColrPaintGraphRenderer boundingRectCalculator;
1399 boundingRectCalculator.beginCalculateBoundingBox();
1400 if (traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &boundingRectCalculator, 0))
1401 boundingRect = boundingRectCalculator.boundingRect();
1402 }
1403
1404 QTransform initialTransform;
1405 initialTransform.scale(fontDef.pixelSize, fontDef.pixelSize);
1406 boundingRect = initialTransform.mapRect(boundingRect);
1407
1408 QTransform originalXform = matrixToTransform(matrix);
1409 boundingRect = originalXform.mapRect(boundingRect);
1410
1411 qCDebug(lcColrv1).noquote() << "Bounds of"
1412 << glyphRun->glyphIndices[0]
1413 << " in device coordinates:"
1414 << boundingRect;
1415
1416 QColrPaintGraphRenderer graphRenderer;
1417 graphRenderer.setBoundingRect(boundingRect);
1418 graphRenderer.beginRender(fontDef.pixelSize / m_unitsPerEm, matrixToTransform(matrix));
1419 if (!traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &graphRenderer, 0))
1420 return false;
1421
1422 *image = graphRenderer.endRender();
1423 } else {
1424 qCDebug(lcColrv1) << "Font face does not support IDWriteFontFace7 interface";
1425 }
1426
1427 return true;
1428}
1429#endif // QT_CONFIG(directwritecolrv1)
1430
1431QImage QWindowsFontEngineDirectWrite::renderColorGlyph(DWRITE_GLYPH_RUN *glyphRun,
1432 const DWRITE_MATRIX &transform,
1433 DWRITE_RENDERING_MODE renderMode,
1434 DWRITE_MEASURING_MODE measureMode,
1435 DWRITE_GRID_FIT_MODE gridFitMode,
1436 QColor color,
1437 QRect boundingRect) const
1438{
1439 QImage ret;
1440
1441#if QT_CONFIG(directwritecolrv1)
1442 // Start by trying COLRv1 glyph, where we need to get the paint nodes ourselves and render
1443 // them.
1444 renderColr1GlyphRun(&ret, glyphRun, transform, color);
1445#endif // QT_CONFIG(directwritecolrv1)
1446
1447#if QT_CONFIG(directwrite3)
1448 // If not successful, we try the modern API that supports both embedded pixmaps or COLRv0
1449 // glyphs, or a combination.
1450 if (ret.isNull()) {
1451 ComPtr<IDWriteFactory4> factory4;
1452 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory4),
1453 &factory4);
1454 if (SUCCEEDED(hr)) {
1455 const DWRITE_GLYPH_IMAGE_FORMATS supportedBitmapFormats =
1456 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_PNG
1457 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
1458 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF);
1459
1460 const DWRITE_GLYPH_IMAGE_FORMATS glyphFormats =
1461 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_COLR
1462 | supportedBitmapFormats
1463 | DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE
1464 | DWRITE_GLYPH_IMAGE_FORMATS_CFF);
1465
1466 ComPtr<IDWriteColorGlyphRunEnumerator1> enumerator;
1467 hr = factory4->TranslateColorGlyphRun(D2D1::Point2F(0.0f, 0.0f),
1468 glyphRun,
1469 NULL,
1470 glyphFormats,
1471 measureMode,
1472 NULL,
1473 0,
1474 &enumerator);
1475 BOOL ok = true;
1476 while (SUCCEEDED(hr) && ok) {
1477 hr = enumerator->MoveNext(&ok);
1478 if (!ok)
1479 break;
1480
1481 const DWRITE_COLOR_GLYPH_RUN1 *colorGlyphRun = nullptr;
1482 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1483 if (FAILED(hr)) {
1484 qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed", __FUNCTION__);
1485 return QImage{};
1486 }
1487
1488 if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_NONE) {
1489 // On some older platforms, we get a glyph run with format NONE before
1490 // the actual ones
1491 continue;
1492 } else if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_COLR) {
1493 if (ret.isNull()) {
1494 ret = QImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied);
1495 ret.fill(0);
1496 }
1497
1498 if (renderColr0GlyphRun(&ret,
1499 reinterpret_cast<const DWRITE_COLOR_GLYPH_RUN *>(colorGlyphRun), // Broken inheritance in MinGW
1500 transform,
1501 renderMode,
1502 measureMode,
1503 gridFitMode,
1504 color,
1505 boundingRect)) {
1506 break;
1507 }
1508 } else if (colorGlyphRun->glyphImageFormat & supportedBitmapFormats) {
1509 ComPtr<IDWriteFontFace4> face4;
1510 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
1511 &face4))) {
1512 DWRITE_GLYPH_IMAGE_DATA data;
1513 void *ctx;
1514 Q_ASSERT(glyphRun->glyphCount == 1);
1515 HRESULT hr = face4->GetGlyphImageData(glyphRun->glyphIndices[0],
1516 fontDef.pixelSize,
1517 DWRITE_GLYPH_IMAGE_FORMATS(colorGlyphRun->glyphImageFormat & supportedBitmapFormats),
1518 &data,
1519 &ctx);
1520 if (FAILED(hr)) {
1521 qErrnoWarning("%s: GetGlyphImageData failed", __FUNCTION__);
1522 return QImage{};
1523 }
1524
1525 auto fnc = qScopeGuard([&]() {
1526 if (data.imageData != nullptr)
1527 face4->ReleaseGlyphImageData(ctx);
1528 });
1529
1530 if (data.pixelsPerEm == 0 || data.imageData == nullptr || data.imageDataSize == 0) {
1531 qCWarning(lcQpaFonts) << __FUNCTION__
1532 << "Failed to retrieve image data for glyph"
1533 << glyphRun->glyphIndices[0]
1534 << "in font:"
1535 << fontDef.families
1536 << "with formats:"
1537 << colorGlyphRun->glyphImageFormat;
1538 } else {
1539 const char *format;
1540 switch (colorGlyphRun->glyphImageFormat) {
1541 case DWRITE_GLYPH_IMAGE_FORMATS_JPEG:
1542 format = "JPEG";
1543 break;
1544 case DWRITE_GLYPH_IMAGE_FORMATS_TIFF:
1545 format = "TIFF";
1546 break;
1547 default:
1548 format = "PNG";
1549 break;
1550 };
1551
1552 ret = QImage::fromData(reinterpret_cast<const uchar *>(data.imageData),
1553 data.imageDataSize,
1554 format);
1555
1556 QTransform matrix(transform.m11, transform.m12,
1557 transform.m21, transform.m22,
1558 transform.dx, transform.dy);
1559
1560 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
1561 matrix.scale(scale, scale);
1562
1563 if (!matrix.isIdentity())
1564 ret = ret.transformed(matrix, Qt::SmoothTransformation);
1565
1566 break;
1567 }
1568 }
1569
1570 } else {
1571 qCDebug(lcQpaFonts) << "Found glyph run with unsupported format"
1572 << colorGlyphRun->glyphImageFormat;
1573 }
1574 }
1575 }
1576 }
1577#endif // QT_CONFIG(directwrite3)
1578
1579 // If all else fails, we go through the pre-dwrite3 version, which just supports COLRv0.
1580 if (ret.isNull()) {
1581 ComPtr<IDWriteFactory2> factory2;
1582 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1583 &factory2);
1584 if (FAILED(hr))
1585 return ret;
1586
1587 ComPtr<IDWriteColorGlyphRunEnumerator> enumerator;
1588 hr = factory2->TranslateColorGlyphRun(0.0f,
1589 0.0f,
1590 glyphRun,
1591 NULL,
1592 measureMode,
1593 NULL,
1594 0,
1595 &enumerator);
1596 if (SUCCEEDED(hr)) {
1597 ret = QImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied);
1598 ret.fill(0);
1599
1600 BOOL ok = true;
1601 while (SUCCEEDED(hr) && ok) {
1602 hr = enumerator->MoveNext(&ok);
1603 if (FAILED(hr)) {
1604 qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::MoveNext failed", __FUNCTION__);
1605 return QImage{};
1606 }
1607
1608 if (ok) {
1609 const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun = nullptr;
1610 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1611 if (FAILED(hr)) { // No colored runs, only outline
1612 qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed", __FUNCTION__);
1613 return QImage{};
1614 }
1615
1616 if (!renderColr0GlyphRun(&ret,
1617 colorGlyphRun,
1618 transform,
1619 renderMode,
1620 measureMode,
1621 gridFitMode,
1622 color,
1623 boundingRect)) {
1624 return QImage{};
1625 }
1626 }
1627 }
1628 }
1629 }
1630
1631 return ret;
1632}
1633
1634QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
1635 const QFixedPoint &subPixelPosition,
1636 int margin,
1637 const QTransform &originalTransform,
1638 const QColor &color)
1639{
1640 UINT16 glyphIndex = t;
1641 FLOAT glyphAdvance = 0;
1642
1643 DWRITE_GLYPH_OFFSET glyphOffset;
1644 glyphOffset.advanceOffset = 0;
1645 glyphOffset.ascenderOffset = 0;
1646
1647 DWRITE_GLYPH_RUN glyphRun;
1648 glyphRun.fontFace = m_directWriteFontFace;
1649 glyphRun.fontEmSize = fontDef.pixelSize;
1650 glyphRun.glyphCount = 1;
1651 glyphRun.glyphIndices = &glyphIndex;
1652 glyphRun.glyphAdvances = &glyphAdvance;
1653 glyphRun.isSideways = false;
1654 glyphRun.bidiLevel = 0;
1655 glyphRun.glyphOffsets = &glyphOffset;
1656
1657 QTransform xform = originalTransform;
1658 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
1659 xform.scale(fontDef.stretch / 100.0, 1.0);
1660
1661 DWRITE_MATRIX transform;
1662 transform.dx = subPixelPosition.x.toReal();
1663 transform.dy = 0;
1664 transform.m11 = xform.m11();
1665 transform.m12 = xform.m12();
1666 transform.m21 = xform.m21();
1667 transform.m22 = xform.m22();
1668
1669 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1670 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1671 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1672 ? DWRITE_GRID_FIT_MODE_DISABLED
1673 : DWRITE_GRID_FIT_MODE_DEFAULT;
1674
1675 ComPtr<IDWriteFactory2> factory2;
1676 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1677 &factory2);
1678 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1679 if (!SUCCEEDED(hr)) {
1680 qErrnoWarning(hr, "%s: Failed to query IDWriteFactory2 interface.", __FUNCTION__);
1681 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1682 &glyphRun,
1683 1.0f,
1684 &transform,
1685 renderMode,
1686 measureMode,
1687 0.0, 0.0,
1688 &glyphAnalysis
1689 );
1690 } else {
1691 hr = factory2->CreateGlyphRunAnalysis(
1692 &glyphRun,
1693 &transform,
1694 renderMode,
1695 measureMode,
1696 gridFitMode,
1697 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1698 0.0, 0.0,
1699 &glyphAnalysis
1700 );
1701 }
1702
1703 if (SUCCEEDED(hr)) {
1704 QRect rect = paintGraphBounds(t, transform);
1705 if (rect.isEmpty())
1706 rect = alphaTextureBounds(t, transform);
1707 if (rect.isEmpty())
1708 rect = colorBitmapBounds(t, transform);
1709
1710 if (rect.isEmpty()) {
1711 qCDebug(lcQpaFonts) << __FUNCTION__ << "Cannot get alpha texture bounds. Falling back to slower rendering path.";
1712 return QImage();
1713 }
1714
1715 if (glyphBoundingBoxExceedsLimit(rect.width(),
1716 rect.height(),
1717 effectivePixelSizeSquared(xform))) {
1718 return QImage();
1719 }
1720
1721 QRect boundingRect = rect.adjusted(-margin, -margin, margin, margin);
1722
1723 QImage image;
1724 if (glyphFormat == QFontEngine::Format_ARGB) {
1725 image = renderColorGlyph(&glyphRun,
1726 transform,
1727 renderMode,
1728 measureMode,
1729 gridFitMode,
1730 color,
1731 boundingRect);
1732 }
1733
1734 // Not a color glyph, fall back to regular glyph rendering
1735 if (image.isNull()) {
1736 image = QImage(boundingRect.size(),
1737 glyphFormat == QFontEngine::Format_ARGB
1738 ? QImage::Format_ARGB32_Premultiplied
1739 : QImage::Format_RGB32);
1740 image.fill(image.format() == QImage::Format_ARGB32_Premultiplied ? 0x0 : 0xffffffff);
1741
1742 float r, g, b, a;
1743 if (glyphFormat == QFontEngine::Format_ARGB) {
1744 r = float(color.redF());
1745 g = float(color.greenF());
1746 b = float(color.blueF());
1747 a = float(color.alphaF());
1748 } else {
1749 r = g = b = a = 0.0;
1750 }
1751
1752 renderGlyphRun(&image,
1753 r,
1754 g,
1755 b,
1756 a,
1757 glyphAnalysis.Get(),
1758 boundingRect,
1759 renderMode);
1760 }
1761
1762 return image;
1763 } else {
1764 qErrnoWarning(hr, "%s: CreateGlyphRunAnalysis failed", __FUNCTION__);
1765 return QImage();
1766 }
1767}
1768
1769void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
1770 float r,
1771 float g,
1772 float b,
1773 float a,
1774 IDWriteGlyphRunAnalysis *glyphAnalysis,
1775 const QRect &boundingRect,
1776 DWRITE_RENDERING_MODE renderMode) const
1777{
1778 const int width = destination->width();
1779 const int height = destination->height();
1780
1781 r *= 255.0;
1782 g *= 255.0;
1783 b *= 255.0;
1784
1785 const int size = width * height * 3;
1786 if (size > 0) {
1787 RECT rect;
1788 rect.left = boundingRect.left();
1789 rect.top = boundingRect.top();
1790 rect.right = boundingRect.left() + boundingRect.width();
1791 rect.bottom = boundingRect.top() + boundingRect.height();
1792
1793 QVarLengthArray<BYTE, 1024> alphaValueArray(size);
1794 BYTE *alphaValues = alphaValueArray.data();
1795 memset(alphaValues, 0, size);
1796
1797 HRESULT hr = glyphAnalysis->CreateAlphaTexture(renderMode == DWRITE_RENDERING_MODE_ALIASED
1798 ? DWRITE_TEXTURE_ALIASED_1x1
1799 : DWRITE_TEXTURE_CLEARTYPE_3x1,
1800 &rect,
1801 alphaValues,
1802 size);
1803 if (SUCCEEDED(hr)) {
1804 if (destination->hasAlphaChannel()) { // Color glyphs
1805 for (int y = 0; y < height; ++y) {
1806 uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
1807 BYTE *src = alphaValues + width * 3 * y;
1808
1809 for (int x = 0; x < width; ++x) {
1810 float redAlpha = a * *src++ / 255.0;
1811 float greenAlpha = a * *src++ / 255.0;
1812 float blueAlpha = a * *src++ / 255.0;
1813 float averageAlpha = (redAlpha + greenAlpha + blueAlpha) / 3.0;
1814
1815 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1816 qSwap(redAlpha, blueAlpha);
1817
1818 QRgb currentRgb = dest[x];
1819 dest[x] = qRgba(qRound(qRed(currentRgb) * (1.0 - averageAlpha) + averageAlpha * r),
1820 qRound(qGreen(currentRgb) * (1.0 - averageAlpha) + averageAlpha * g),
1821 qRound(qBlue(currentRgb) * (1.0 - averageAlpha) + averageAlpha * b),
1822 qRound(qAlpha(currentRgb) * (1.0 - averageAlpha) + averageAlpha * 255));
1823 }
1824 }
1825 } else if (renderMode == DWRITE_RENDERING_MODE_ALIASED) {
1826 for (int y = 0; y < height; ++y) {
1827 uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
1828 BYTE *src = alphaValues + width * y;
1829
1830 for (int x = 0; x < width; ++x) {
1831 int alpha = *(src++);
1832 dest[x] = (alpha << 16) + (alpha << 8) + alpha;
1833 }
1834 }
1835 } else {
1836 for (int y = 0; y < height; ++y) {
1837 uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
1838 BYTE *src = alphaValues + width * 3 * y;
1839
1840 for (int x = 0; x < width; ++x) {
1841 BYTE redAlpha = *(src + 0);
1842 BYTE greenAlpha = *(src + 1);
1843 BYTE blueAlpha = *(src + 2);
1844
1845 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1846 qSwap(redAlpha, blueAlpha);
1847
1848 dest[x] = qRgb(redAlpha, greenAlpha, blueAlpha);
1849 src += 3;
1850 }
1851 }
1852 }
1853 } else {
1854 qErrnoWarning("%s: CreateAlphaTexture failed", __FUNCTION__);
1855 }
1856 }
1857}
1858
1859QImage QWindowsFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t,
1860 const QFixedPoint &subPixelPosition,
1861 const QTransform &xform)
1862{
1863 QImage mask = imageForGlyph(t,
1864 subPixelPosition,
1865 glyphMargin(QFontEngine::Format_A32),
1866 xform);
1867
1868 if (mask.isNull()) {
1869 if (xform.type() > QTransform::TxTranslate && glyphTransformExceedsLimit(t, xform))
1870 return QImage();
1871
1872 mask = QFontEngine::renderedPathForGlyph(t, Qt::white);
1873 if (!xform.isIdentity())
1874 mask = mask.transformed(xform);
1875 }
1876
1877 return mask.depth() == 32
1878 ? mask
1879 : mask.convertToFormat(QImage::Format_RGB32);
1880}
1881
1882QFontEngine *QWindowsFontEngineDirectWrite::cloneWithSize(qreal pixelSize) const
1883{
1884 QWindowsFontEngineDirectWrite *fontEngine = new QWindowsFontEngineDirectWrite(m_directWriteFontFace,
1885 pixelSize,
1886 m_fontEngineData);
1887
1888 fontEngine->fontDef = fontDef;
1889 fontEngine->fontDef.pixelSize = pixelSize;
1890 if (!m_uniqueFamilyName.isEmpty()) {
1891 fontEngine->setUniqueFamilyName(m_uniqueFamilyName);
1892 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
1893 static_cast<QWindowsFontDatabase *>(pfdb)->refUniqueFont(m_uniqueFamilyName);
1894 }
1895
1896 return fontEngine;
1897}
1898
1899Qt::HANDLE QWindowsFontEngineDirectWrite::handle() const
1900{
1901 return m_directWriteFontFace;
1902}
1903
1904void QWindowsFontEngineDirectWrite::initFontInfo(const QFontDef &request,
1905 int dpi)
1906{
1907 fontDef = request;
1908
1909 if (fontDef.pointSize < 0)
1910 fontDef.pointSize = fontDef.pixelSize * 72. / dpi;
1911 else if (fontDef.pixelSize == -1)
1912 fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.);
1913
1914 m_faceId.variableAxes = request.variableAxisValues;
1915
1916#if QT_CONFIG(directwrite3)
1917 IDWriteFontFace3 *face3 = nullptr;
1918 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace3),
1919 reinterpret_cast<void **>(&face3)))) {
1920
1921 fontDef.weight = QWindowsDirectWriteFontDatabase::fromDirectWriteWeight(face3->GetWeight());
1922 fontDef.style = QWindowsDirectWriteFontDatabase::fromDirectWriteStyle(face3->GetStyle());
1923
1924 IDWriteLocalizedStrings *names;
1925 if (SUCCEEDED(face3->GetFaceNames(&names))) {
1926 wchar_t englishLocale[] = L"en-us";
1927 fontDef.styleName = QWindowsDirectWriteFontDatabase::localeString(names, englishLocale);
1928 names->Release();
1929 }
1930
1931 // Color font
1932 if (face3->IsColorFont())
1933 glyphFormat = QFontEngine::Format_ARGB;
1934
1935 face3->Release();
1936 }
1937#endif
1938}
1939
1940QString QWindowsFontEngineDirectWrite::fontNameSubstitute(const QString &familyName)
1941{
1942 const QString substitute =
1943 QWinRegistryKey(HKEY_LOCAL_MACHINE,
1944 LR"(Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes)")
1945 .stringValue(familyName);
1946 return substitute.isEmpty() ? familyName : substitute;
1947}
1948
1949QRect QWindowsFontEngineDirectWrite::alphaTextureBounds(glyph_t glyph,
1950 const DWRITE_MATRIX &transform)
1951{
1952 UINT16 glyphIndex = glyph;
1953 FLOAT glyphAdvance = 0;
1954
1955 DWRITE_GLYPH_OFFSET glyphOffset;
1956 glyphOffset.advanceOffset = 0;
1957 glyphOffset.ascenderOffset = 0;
1958
1959 DWRITE_GLYPH_RUN glyphRun;
1960 glyphRun.fontFace = m_directWriteFontFace;
1961 glyphRun.fontEmSize = fontDef.pixelSize;
1962 glyphRun.glyphCount = 1;
1963 glyphRun.glyphIndices = &glyphIndex;
1964 glyphRun.glyphAdvances = &glyphAdvance;
1965 glyphRun.isSideways = false;
1966 glyphRun.bidiLevel = 0;
1967 glyphRun.glyphOffsets = &glyphOffset;
1968
1969 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1970 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1971 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1972 ? DWRITE_GRID_FIT_MODE_DISABLED
1973 : DWRITE_GRID_FIT_MODE_DEFAULT;
1974
1975 ComPtr<IDWriteFactory2> factory2 = nullptr;
1976 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1977 &factory2);
1978
1979 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1980 if (SUCCEEDED(hr)) {
1981 hr = factory2->CreateGlyphRunAnalysis(
1982 &glyphRun,
1983 &transform,
1984 renderMode,
1985 measureMode,
1986 gridFitMode,
1987 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1988 0.0, 0.0,
1989 &glyphAnalysis
1990 );
1991 } else {
1992 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1993 &glyphRun,
1994 1.0f,
1995 &transform,
1996 renderMode,
1997 measureMode,
1998 0.0, 0.0,
1999 &glyphAnalysis
2000 );
2001 }
2002
2003 if (SUCCEEDED(hr)) {
2004 RECT rect;
2005 hr = glyphAnalysis->GetAlphaTextureBounds(renderMode == DWRITE_RENDERING_MODE_ALIASED
2006 ? DWRITE_TEXTURE_ALIASED_1x1
2007 : DWRITE_TEXTURE_CLEARTYPE_3x1,
2008 &rect);
2009 if (FAILED(hr) || rect.left == rect.right || rect.top == rect.bottom)
2010 return QRect{};
2011
2012 return QRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
2013 } else {
2014 return QRect{};
2015 }
2016}
2017
2018QRect QWindowsFontEngineDirectWrite::colorBitmapBounds(glyph_t glyph, const DWRITE_MATRIX &transform)
2019{
2020#if QT_CONFIG(directwrite3)
2021 ComPtr<IDWriteFontFace4> face4;
2022 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
2023 &face4))) {
2024 DWRITE_GLYPH_IMAGE_FORMATS formats = face4->GetGlyphImageFormats();
2025
2026 const DWRITE_GLYPH_IMAGE_FORMATS supportedBitmapFormats =
2027 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_PNG
2028 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
2029 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF);
2030
2031 if (formats & supportedBitmapFormats) {
2032 DWRITE_GLYPH_IMAGE_DATA data;
2033 void *ctx;
2034 HRESULT hr = face4->GetGlyphImageData(glyph,
2035 fontDef.pixelSize,
2036 DWRITE_GLYPH_IMAGE_FORMATS(formats & supportedBitmapFormats),
2037 &data,
2038 &ctx);
2039 if (FAILED(hr)) {
2040 qErrnoWarning("%s: GetGlyphImageData failed", __FUNCTION__);
2041 return QRect{};
2042 }
2043
2044 auto fnc = qScopeGuard([&]() {
2045 if (data.imageData != nullptr)
2046 face4->ReleaseGlyphImageData(ctx);
2047 });
2048
2049 if (data.pixelsPerEm == 0 || data.imageData == nullptr || data.imageDataSize == 0)
2050 return QRect{};
2051
2052 QRect rect(-data.horizontalLeftOrigin.x,
2053 -data.horizontalLeftOrigin.y,
2054 data.pixelSize.width,
2055 data.pixelSize.height);
2056
2057 QTransform matrix(transform.m11, transform.m12,
2058 transform.m21, transform.m22,
2059 transform.dx, transform.dy);
2060
2061 // GetGlyphImageData returns the closest matching size, which we need to scale down
2062 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
2063 matrix.scale(scale, scale);
2064 rect = matrix.mapRect(rect);
2065
2066 return rect;
2067 }
2068 }
2069
2070 return QRect{};
2071#else
2072 Q_UNUSED(glyph);
2073 Q_UNUSED(transform);
2074 return QRect{};
2075#endif // QT_CONFIG(directwrite3)
2076}
2077
2078glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph,
2079 const QFixedPoint &subPixelPosition,
2080 const QTransform &originalTransform,
2081 GlyphFormat format)
2082{
2083 QTransform matrix = originalTransform;
2084 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
2085 matrix.scale(fontDef.stretch / 100.0, 1.0);
2086
2087 glyph_metrics_t bbox = QFontEngine::boundingBox(glyph, matrix); // To get transformed advance
2088
2089 DWRITE_MATRIX transform;
2090 transform.dx = subPixelPosition.x.toReal();
2091 transform.dy = 0;
2092 transform.m11 = matrix.m11();
2093 transform.m12 = matrix.m12();
2094 transform.m21 = matrix.m21();
2095 transform.m22 = matrix.m22();
2096
2097 // Try COLRv1 approach first
2098 QRect rect = paintGraphBounds(glyph, transform);
2099
2100 // Then try general approach (works with regular truetype glyphs as well as COLRv0)
2101 if (rect.isEmpty())
2102 rect = alphaTextureBounds(glyph, transform);
2103
2104 // If this fails, we check if it is an embedded color bitmap
2105 if (rect.isEmpty())
2106 rect = colorBitmapBounds(glyph, transform);
2107
2108 // If we are unable to find metrics, just return the design metrics
2109 if (rect.isEmpty())
2110 return bbox;
2111
2112 int margin = glyphMargin(format);
2113 return glyph_metrics_t(rect.left(),
2114 rect.top(),
2115 rect.width() + margin * 2,
2116 rect.height() + margin * 2,
2117 bbox.xoff, bbox.yoff);
2118}
2119
2120QImage QWindowsFontEngineDirectWrite::bitmapForGlyph(glyph_t glyph,
2121 const QFixedPoint &subPixelPosition,
2122 const QTransform &t,
2123 const QColor &color)
2124{
2125 return imageForGlyph(glyph, subPixelPosition, glyphMargin(QFontEngine::Format_ARGB), t, color);
2126}
2127
2128QList<QFontVariableAxis> QWindowsFontEngineDirectWrite::variableAxes() const
2129{
2130 return m_variableAxes;
2131}
2132
2133bool QWindowsFontEngineDirectWrite::expectsGammaCorrectedBlending(QFontEngine::GlyphFormat format) const
2134{
2135 return format == QFontEngine::Format_A32;
2136}
2137
2138QT_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)