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.constData());
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.constData() + 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 ret = true;
540 if (buffer && *length >= tableSize)
541 memcpy(buffer, tableData, tableSize);
542 *length = tableSize;
543 Q_ASSERT(int(*length) > 0);
544 }
545 m_directWriteFontFace->ReleaseFontTable(tableContext);
546 } else {
547 qErrnoWarning("%s: TryGetFontTable failed", __FUNCTION__);
548 }
549
550 return ret;
551}
552
553QFixed QWindowsFontEngineDirectWrite::emSquareSize() const
554{
555 return m_unitsPerEm;
556}
557
558glyph_t QWindowsFontEngineDirectWrite::glyphIndex(uint ucs4) const
559{
560 UINT16 glyphIndex;
561
562 HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(&ucs4, 1, &glyphIndex);
563 if (FAILED(hr)) {
564 qErrnoWarning("%s: glyphIndex failed", __FUNCTION__);
565 glyphIndex = 0;
566 }
567
568 return glyphIndex;
569}
570
571int QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
572 int *nglyphs, QFontEngine::ShaperFlags flags) const
573{
574 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
575 if (*nglyphs < len) {
576 *nglyphs = len;
577 return -1;
578 }
579
580 QVarLengthArray<UINT32> codePoints(len);
581 int actualLength = 0;
582 QStringIterator it(str, str + len);
583 while (it.hasNext())
584 codePoints[actualLength++] = it.next();
585
586 QVarLengthArray<UINT16> glyphIndices(actualLength);
587 HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(codePoints.data(), actualLength,
588 glyphIndices.data());
589 if (FAILED(hr)) {
590 qErrnoWarning("%s: GetGlyphIndicesW failed", __FUNCTION__);
591 return -1;
592 }
593
594 int mappedGlyphs = 0;
595 for (int i = 0; i < actualLength; ++i) {
596 glyphs->glyphs[i] = glyphIndices.at(i);
597 if (glyphs->glyphs[i] != 0 || isIgnorableChar(codePoints.at(i)))
598 mappedGlyphs++;
599 }
600
601 *nglyphs = actualLength;
602 glyphs->numGlyphs = actualLength;
603
604 if (!(flags & GlyphIndicesOnly))
605 recalcAdvances(glyphs, {});
606
607 return mappedGlyphs;
608}
609
610QFontEngine::FaceId QWindowsFontEngineDirectWrite::faceId() const
611{
612 return m_faceId;
613}
614
615void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags shaperFlags) const
616{
617 QVarLengthArray<UINT16> glyphIndices(glyphs->numGlyphs);
618
619 // ### Caching?
620 for(int i=0; i<glyphs->numGlyphs; i++)
621 glyphIndices[i] = UINT16(glyphs->glyphs[i]);
622
623 QVarLengthArray<DWRITE_GLYPH_METRICS> glyphMetrics(glyphIndices.size());
624
625 HRESULT hr;
626 QFont::HintingPreference hint = determineHinting(fontDef);
627 bool needsDesignMetrics = shaperFlags & QFontEngine::DesignMetrics;
628 if (!needsDesignMetrics && hint == QFont::PreferFullHinting) {
629 const DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
630 const bool needsNaturalMetrics = renderMode == DWRITE_RENDERING_MODE_NATURAL
631 || renderMode == DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
632
633 hr = m_directWriteFontFace->GetGdiCompatibleGlyphMetrics(float(fontDef.pixelSize),
634 1.0f,
635 NULL,
636 needsNaturalMetrics,
637 glyphIndices.data(),
638 glyphIndices.size(),
639 glyphMetrics.data());
640 } else {
641 hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(),
642 glyphIndices.size(),
643 glyphMetrics.data());
644 }
645 if (SUCCEEDED(hr)) {
646 qreal stretch = fontDef.stretch != QFont::AnyStretch ? fontDef.stretch / 100.0 : 1.0;
647 for (int i = 0; i < glyphs->numGlyphs; ++i)
648 glyphs->advances[i] = DESIGN_TO_LOGICAL(glyphMetrics[i].advanceWidth * stretch);
649 } else {
650 qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__);
651 }
652}
653
654QPainterPath QWindowsFontEngineDirectWrite::unscaledGlyph(glyph_t glyph) const
655{
656 float advance = 0.0f;
657 UINT16 g = glyph;
658 DWRITE_GLYPH_OFFSET offset;
659 offset.advanceOffset = 0;
660 offset.ascenderOffset = 0;
661
662 QPainterPath ret;
663 GeometrySink geometrySink(&ret);
664 HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(m_unitsPerEm,
665 &g,
666 &advance,
667 &offset,
668 1,
669 false,
670 false,
671 &geometrySink);
672 if (FAILED(hr))
673 qErrnoWarning("%s: GetGlyphRunOutline failed", __FUNCTION__);
674
675 return ret;
676}
677
678void QWindowsFontEngineDirectWrite::getUnscaledGlyph(glyph_t glyph,
679 QPainterPath *path,
680 glyph_metrics_t *metric)
681{
682 *path = unscaledGlyph(glyph);
683
684 UINT16 g = glyph;
685 DWRITE_GLYPH_METRICS glyphMetrics;
686 HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&g, 1, &glyphMetrics);
687 if (FAILED(hr)) {
688 qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__);
689 return;
690 }
691
692 QFixed advanceWidth = QFixed(int(glyphMetrics.advanceWidth));
693 QFixed leftSideBearing = QFixed(glyphMetrics.leftSideBearing);
694 QFixed rightSideBearing = QFixed(glyphMetrics.rightSideBearing);
695 QFixed advanceHeight = QFixed(int(glyphMetrics.advanceHeight));
696 QFixed verticalOriginY = QFixed(glyphMetrics.verticalOriginY);
697 QFixed topSideBearing = QFixed(glyphMetrics.topSideBearing);
698 QFixed bottomSideBearing = QFixed(glyphMetrics.bottomSideBearing);
699 QFixed width = advanceWidth - leftSideBearing - rightSideBearing;
700 QFixed height = advanceHeight - topSideBearing - bottomSideBearing;
701 *metric = glyph_metrics_t(leftSideBearing,
702 -verticalOriginY + topSideBearing,
703 width,
704 height,
705 advanceWidth,
706 0);
707}
708
709void QWindowsFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
710 QPainterPath *path, QTextItem::RenderFlags flags)
711{
712 Q_UNUSED(flags);
713 QVarLengthArray<UINT16> glyphIndices(nglyphs);
714 QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(nglyphs);
715 QVarLengthArray<FLOAT> glyphAdvances(nglyphs);
716
717 for (int i=0; i<nglyphs; ++i) {
718 glyphIndices[i] = glyphs[i];
719 glyphOffsets[i].advanceOffset = positions[i].x.toReal();
720 glyphOffsets[i].ascenderOffset = -positions[i].y.toReal();
721 glyphAdvances[i] = 0.0;
722 }
723
724 GeometrySink geometrySink(path);
725 HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(
726 fontDef.pixelSize,
727 glyphIndices.data(),
728 glyphAdvances.data(),
729 glyphOffsets.data(),
730 nglyphs,
731 false,
732 false,
733 &geometrySink
734 );
735
736 if (FAILED(hr))
737 qErrnoWarning("%s: GetGlyphRunOutline failed", __FUNCTION__);
738}
739
740glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(const QGlyphLayout &glyphs)
741{
742 if (glyphs.numGlyphs == 0)
743 return glyph_metrics_t();
744 QFixed w = 0;
745 for (int i = 0; i < glyphs.numGlyphs; ++i)
746 w += glyphs.effectiveAdvance(i);
747
748 const QFixed leftBearing = firstLeftBearing(glyphs);
749 return glyph_metrics_t(leftBearing, -ascent(), w - leftBearing - lastRightBearing(glyphs),
750 ascent() + descent(), w, 0);
751}
752
753glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(glyph_t g)
754{
755 UINT16 glyphIndex = g;
756
757 DWRITE_GLYPH_METRICS glyphMetrics;
758 HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics);
759 if (SUCCEEDED(hr)) {
760 QFixed advanceWidth = DESIGN_TO_LOGICAL(glyphMetrics.advanceWidth);
761 QFixed leftSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.leftSideBearing);
762 QFixed rightSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.rightSideBearing);
763 QFixed advanceHeight = DESIGN_TO_LOGICAL(glyphMetrics.advanceHeight);
764 QFixed verticalOriginY = DESIGN_TO_LOGICAL(glyphMetrics.verticalOriginY);
765 QFixed topSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.topSideBearing);
766 QFixed bottomSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.bottomSideBearing);
767 QFixed width = advanceWidth - leftSideBearing - rightSideBearing;
768 QFixed height = advanceHeight - topSideBearing - bottomSideBearing;
769 return glyph_metrics_t(leftSideBearing,
770 -verticalOriginY + topSideBearing,
771 width,
772 height,
773 advanceWidth,
774 0);
775 } else {
776 qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__);
777 }
778
779 return glyph_metrics_t();
780}
781
782QFixed QWindowsFontEngineDirectWrite::capHeight() const
783{
784 if (m_capHeight <= 0)
785 return calculatedCapHeight();
786
787 return m_capHeight;
788}
789
790QFixed QWindowsFontEngineDirectWrite::xHeight() const
791{
792 return m_xHeight;
793}
794
795qreal QWindowsFontEngineDirectWrite::maxCharWidth() const
796{
797 return m_maxAdvanceWidth.toReal();
798}
799
800QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
801 const QFixedPoint &subPixelPosition,
802 const QTransform &t)
803{
804 QImage im = imageForGlyph(glyph, subPixelPosition, glyphMargin(Format_A8), t);
805
806 if (!im.isNull()) {
807 QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
808
809 for (int y=0; y<im.height(); ++y) {
810 const uint *src = reinterpret_cast<const uint *>(im.constScanLine(y));
811 uchar *dst = alphaMap.scanLine(y);
812 for (int x=0; x<im.width(); ++x) {
813 *dst = qGray(*src);
814 ++dst;
815 ++src;
816 }
817 }
818
819 return alphaMap;
820 } else {
821 return QFontEngine::alphaMapForGlyph(glyph, t);
822 }
823}
824
825QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
826 const QFixedPoint &subPixelPosition)
827{
828 return alphaMapForGlyph(glyph, subPixelPosition, QTransform());
829}
830
831bool QWindowsFontEngineDirectWrite::supportsHorizontalSubPixelPositions() const
832{
833 QFont::HintingPreference hinting = determineHinting(fontDef);
834 return (hinting != QFont::PreferFullHinting && !(fontDef.styleStrategy & QFont::NoAntialias));
835}
836
837QFontEngine::Properties QWindowsFontEngineDirectWrite::properties() const
838{
839 IDWriteFontFace2 *directWriteFontFace2;
840 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace2),
841 reinterpret_cast<void **>(&directWriteFontFace2)))) {
842 DWRITE_FONT_METRICS1 metrics;
843 directWriteFontFace2->GetMetrics(&metrics);
844
845 Properties p = QFontEngine::properties();
846 p.emSquare = metrics.designUnitsPerEm;
847 p.boundingBox = QRectF(metrics.glyphBoxLeft,
848 -metrics.glyphBoxTop,
849 metrics.glyphBoxRight - metrics.glyphBoxLeft,
850 metrics.glyphBoxTop - metrics.glyphBoxBottom);
851 p.ascent = metrics.ascent;
852 p.descent = metrics.descent;
853 p.leading = metrics.lineGap;
854 p.capHeight = metrics.capHeight;
855 p.lineWidth = metrics.underlineThickness;
856
857 directWriteFontFace2->Release();
858 return p;
859 } else {
860 return QFontEngine::properties();
861 }
862}
863
864bool QWindowsFontEngineDirectWrite::renderColr0GlyphRun(QImage *image,
865 const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun,
866 const DWRITE_MATRIX &transform,
867 DWRITE_RENDERING_MODE renderMode,
868 DWRITE_MEASURING_MODE measureMode,
869 DWRITE_GRID_FIT_MODE gridFitMode,
870 QColor color,
871 QRect boundingRect) const
872{
873 ComPtr<IDWriteFactory2> factory2;
874 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
875 &factory2);
876 if (FAILED(hr))
877 return false;
878
879 ComPtr<IDWriteGlyphRunAnalysis> colorGlyphsAnalysis;
880 hr = factory2->CreateGlyphRunAnalysis(
881 &colorGlyphRun->glyphRun,
882 &transform,
883 renderMode,
884 measureMode,
885 gridFitMode,
886 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
887 0.0, 0.0,
888 &colorGlyphsAnalysis
889 );
890
891 if (FAILED(hr)) {
892 qErrnoWarning(hr, "%s: CreateGlyphRunAnalysis failed for color run", __FUNCTION__);
893 return false;
894 }
895
896 float r, g, b, a;
897 if (colorGlyphRun->paletteIndex == 0xFFFF) {
898 r = float(color.redF());
899 g = float(color.greenF());
900 b = float(color.blueF());
901 a = 1.0;
902 } else {
903 r = qBound(0.0f, colorGlyphRun->runColor.r, 1.0f);
904 g = qBound(0.0f, colorGlyphRun->runColor.g, 1.0f);
905 b = qBound(0.0f, colorGlyphRun->runColor.b, 1.0f);
906 a = qBound(0.0f, colorGlyphRun->runColor.a, 1.0f);
907 }
908
909 if (!qFuzzyIsNull(a))
910 renderGlyphRun(image, r, g, b, a, colorGlyphsAnalysis.Get(), boundingRect, renderMode);
911
912 return true;
913}
914
915#if QT_CONFIG(directwritecolrv1)
916static inline QTransform matrixToTransform(const DWRITE_MATRIX &matrix,
917 int unitsPerEm = 1)
918{
919 return QTransform(matrix.m11, matrix.m12,
920 matrix.m21, matrix.m22,
921 matrix.dx * unitsPerEm, matrix.dy * unitsPerEm);
922}
923
924static inline QColor colorToColor(const DWRITE_COLOR_F &color, float alphaMultiplier = 1.0)
925{
926 return QColor::fromRgbF(color.r, color.g, color.b, color.a * alphaMultiplier);
927}
928
929static inline QGradient::Spread extendToSpread(UINT32 extendMode)
930{
931 switch (extendMode) {
932 case D2D1_EXTEND_MODE_WRAP: return QGradient::RepeatSpread;
933 case D2D1_EXTEND_MODE_MIRROR: return QGradient::ReflectSpread;
934 default: return QGradient::PadSpread;
935 };
936}
937
938static inline QPainter::CompositionMode compositeToCompositionMode(DWRITE_COLOR_COMPOSITE_MODE mode)
939{
940 switch (mode) {
941 case DWRITE_COLOR_COMPOSITE_CLEAR:
942 return QPainter::CompositionMode_Clear;
943
944 case DWRITE_COLOR_COMPOSITE_SRC:
945 return QPainter::CompositionMode_Source;
946
947 case DWRITE_COLOR_COMPOSITE_DEST:
948 return QPainter::CompositionMode_Destination;
949
950 case DWRITE_COLOR_COMPOSITE_SRC_OVER:
951 return QPainter::CompositionMode_SourceOver;
952
953 case DWRITE_COLOR_COMPOSITE_DEST_OVER:
954 return QPainter::CompositionMode_DestinationOver;
955
956 case DWRITE_COLOR_COMPOSITE_SRC_IN:
957 return QPainter::CompositionMode_SourceIn;
958
959 case DWRITE_COLOR_COMPOSITE_DEST_IN:
960 return QPainter::CompositionMode_DestinationIn;
961
962 case DWRITE_COLOR_COMPOSITE_SRC_OUT:
963 return QPainter::CompositionMode_SourceOut;
964
965 case DWRITE_COLOR_COMPOSITE_DEST_OUT:
966 return QPainter::CompositionMode_DestinationOut;
967
968 case DWRITE_COLOR_COMPOSITE_SRC_ATOP:
969 return QPainter::CompositionMode_SourceAtop;
970
971 case DWRITE_COLOR_COMPOSITE_DEST_ATOP:
972 return QPainter::CompositionMode_DestinationAtop;
973
974 case DWRITE_COLOR_COMPOSITE_XOR:
975 return QPainter::CompositionMode_Xor;
976
977 case DWRITE_COLOR_COMPOSITE_PLUS:
978 return QPainter::CompositionMode_Plus;
979
980 case DWRITE_COLOR_COMPOSITE_SCREEN:
981 return QPainter::CompositionMode_Screen;
982
983 case DWRITE_COLOR_COMPOSITE_OVERLAY:
984 return QPainter::CompositionMode_Overlay;
985
986 case DWRITE_COLOR_COMPOSITE_DARKEN:
987 return QPainter::CompositionMode_Darken;
988
989 case DWRITE_COLOR_COMPOSITE_LIGHTEN:
990 return QPainter::CompositionMode_Lighten;
991
992 case DWRITE_COLOR_COMPOSITE_COLOR_DODGE:
993 return QPainter::CompositionMode_ColorDodge;
994
995 case DWRITE_COLOR_COMPOSITE_COLOR_BURN:
996 return QPainter::CompositionMode_ColorBurn;
997
998 case DWRITE_COLOR_COMPOSITE_HARD_LIGHT:
999 return QPainter::CompositionMode_HardLight;
1000
1001 case DWRITE_COLOR_COMPOSITE_SOFT_LIGHT:
1002 return QPainter::CompositionMode_SoftLight;
1003
1004 case DWRITE_COLOR_COMPOSITE_DIFFERENCE:
1005 return QPainter::CompositionMode_Difference;
1006
1007 case DWRITE_COLOR_COMPOSITE_EXCLUSION:
1008 return QPainter::CompositionMode_Exclusion;
1009
1010 case DWRITE_COLOR_COMPOSITE_MULTIPLY:
1011 return QPainter::CompositionMode_Multiply;
1012
1013 default:
1014 qCWarning(lcColrv1) << "Unhandled color composite mode:" << mode;
1015 return QPainter::CompositionMode_SourceOver;
1016 };
1017}
1018#endif // QT_CONFIG(directwritecolrv1)
1019
1020QRect QWindowsFontEngineDirectWrite::paintGraphBounds(glyph_t glyph,
1021 const DWRITE_MATRIX &matrix) const
1022{
1023#if QT_CONFIG(directwritecolrv1)
1024 ComPtr<IDWriteFontFace7> face7;
1025 HRESULT hr = m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace7),
1026 &face7);
1027 if (SUCCEEDED(hr)) {
1028 DWRITE_PAINT_FEATURE_LEVEL featureLevel = face7->GetPaintFeatureLevel(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE);
1029 if (featureLevel != DWRITE_PAINT_FEATURE_LEVEL_COLR_V1)
1030 return QRect{};
1031
1032 ComPtr<IDWritePaintReader> paintReader;
1033 hr = face7->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
1034 featureLevel,
1035 &paintReader);
1036 if (FAILED(hr)) {
1037 qErrnoWarning(hr, "%s: CreatePaintReader failed", __FUNCTION__);
1038 return QRect{};
1039 }
1040
1041 DWRITE_PAINT_ELEMENT paintElement;
1042 D2D_RECT_F clipBox;
1043 hr = paintReader->SetCurrentGlyph(glyph,
1044 &paintElement,
1045 sizeof(paintElement),
1046 &clipBox,
1047 nullptr);
1048
1049 if (FAILED(hr)) {
1050 qErrnoWarning(hr, "%s: SetCurrentGlyph failed", __FUNCTION__);
1051 return QRect{};
1052 }
1053
1054 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE)
1055 return QRect{};
1056
1057 QRectF boundingRect = QRectF(QPointF(clipBox.left, clipBox.top),
1058 QPointF(clipBox.right, clipBox.bottom));
1059 if (boundingRect.isNull()) {
1060 QColrPaintGraphRenderer boundingRectCalculator;
1061 boundingRectCalculator.beginCalculateBoundingBox();
1062 if (traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &boundingRectCalculator))
1063 boundingRect = boundingRectCalculator.boundingRect();
1064 }
1065
1066 QTransform initialTransform;
1067 initialTransform.scale(fontDef.pixelSize, fontDef.pixelSize);
1068 boundingRect = initialTransform.mapRect(boundingRect);
1069
1070 QTransform originalXform = matrixToTransform(matrix);
1071 boundingRect = originalXform.mapRect(boundingRect);
1072
1073 return boundingRect.toAlignedRect();
1074 } else {
1075 qCDebug(lcColrv1) << "Font face does not support IDWriteFontFace7 interface";
1076 }
1077#else
1078 Q_UNUSED(glyph);
1079 Q_UNUSED(matrix);
1080#endif
1081
1082 return QRect{};
1083}
1084
1085#if QT_CONFIG(directwritecolrv1)
1086bool QWindowsFontEngineDirectWrite::traverseColr1(IDWritePaintReader *paintReader,
1087 IDWriteFontFace7 *face7,
1088 const DWRITE_PAINT_ELEMENT *paintElement,
1089 QColrPaintGraphRenderer *paintGraphRenderer) const
1090{
1091 paintGraphRenderer->save();
1092 auto cleanup = qScopeGuard([&paintGraphRenderer]() {
1093 paintGraphRenderer->restore();
1094 });
1095
1096 auto traverseChildren = [&](quint32 childCount) {
1097 DWRITE_PAINT_ELEMENT childPaintElement;
1098 if (FAILED(paintReader->MoveToFirstChild(&childPaintElement, sizeof(DWRITE_PAINT_ELEMENT))))
1099 return false;
1100
1101 while (childCount-- > 0) {
1102 traverseColr1(paintReader, face7, &childPaintElement, paintGraphRenderer);
1103 if (childCount > 0) {
1104 if (FAILED(paintReader->MoveToNextSibling(&childPaintElement, sizeof(DWRITE_PAINT_ELEMENT)))) {
1105 return false;
1106 }
1107 }
1108 }
1109
1110 return SUCCEEDED(paintReader->MoveToParent());
1111 };
1112
1113 auto collectStops = [&paintReader](int stopCount) {
1114 QGradientStops ret;
1115
1116 QVarLengthArray<D2D1_GRADIENT_STOP> stops(stopCount);
1117 HRESULT hr = paintReader->GetGradientStops(0, stopCount, stops.data());
1118 if (FAILED(hr))
1119 return ret;
1120
1121 for (int i = 0; i < stopCount; ++i) {
1122 const D2D1_GRADIENT_STOP &stop = stops[i];
1123 QColor color = QColor::fromRgbF(stop.color.r, stop.color.g, stop.color.b, stop.color.a);
1124 ret.append({stop.position, color});
1125 }
1126
1127 return ret;
1128 };
1129
1130 switch (paintElement->paintType) {
1131 case DWRITE_PAINT_TYPE_LAYERS:
1132 {
1133 if (!traverseChildren(paintElement->paint.layers.childCount))
1134 return false;
1135
1136 break;
1137 }
1138
1139 case DWRITE_PAINT_TYPE_SOLID_GLYPH:
1140 {
1141 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.solidGlyph.glyphIndex);
1142
1143 QColor color = colorToColor(paintElement->paint.solidGlyph.color.value);
1144 paintGraphRenderer->setPath(glyphPath);
1145 paintGraphRenderer->setSolidColor(color);
1146 paintGraphRenderer->drawCurrentPath();
1147
1148 break;
1149 }
1150
1151 case DWRITE_PAINT_TYPE_SOLID:
1152 {
1153 QColor color = colorToColor(paintElement->paint.solid.value,
1154 paintElement->paint.solid.alphaMultiplier);
1155 paintGraphRenderer->setSolidColor(color);
1156 paintGraphRenderer->drawCurrentPath();
1157 break;
1158 }
1159
1160 case DWRITE_PAINT_TYPE_COMPOSITE:
1161 {
1162 if (!paintGraphRenderer->isRendering()) {
1163 traverseChildren(2);
1164 } else {
1165 DWRITE_PAINT_ELEMENT childElement;
1166
1167 HRESULT hr = paintReader->MoveToFirstChild(&childElement, sizeof(DWRITE_PAINT_ELEMENT));
1168 if (FAILED(hr)) {
1169 qErrnoWarning(hr, "%s: Cannot move to first child of composite node",
1170 __FUNCTION__);
1171 return false;
1172 }
1173
1174 // First draw back drop which is the second child
1175 hr = paintReader->MoveToNextSibling(&childElement, sizeof(DWRITE_PAINT_ELEMENT));
1176 if (FAILED(hr)) {
1177 qErrnoWarning(hr, "%s: Cannot move to second child of composite node",
1178 __FUNCTION__);
1179 return false;
1180 }
1181
1182 DWRITE_COLOR_COMPOSITE_MODE compositeMode = paintElement->paint.composite.mode;
1183
1184 QColrPaintGraphRenderer compositeRenderer;
1185 compositeRenderer.setBoundingRect(paintGraphRenderer->boundingRect());
1186 compositeRenderer.beginRender(fontDef.pixelSize / m_unitsPerEm,
1187 paintGraphRenderer->currentTransform());
1188 if (!traverseColr1(paintReader, face7, &childElement, &compositeRenderer))
1189 return false;
1190
1191 // Then draw source which is the first child
1192 hr = paintReader->MoveToParent();
1193 if (FAILED(hr)) {
1194 qErrnoWarning(hr, "%s: Cannot move back to parent composite node",
1195 __FUNCTION__);
1196 return false;
1197 }
1198
1199 hr = paintReader->MoveToFirstChild(&childElement, sizeof(DWRITE_PAINT_ELEMENT));
1200 if (FAILED(hr)) {
1201 qErrnoWarning(hr, "%s: Cannot move to first child of composite node",
1202 __FUNCTION__);
1203 return false;
1204 }
1205
1206 compositeRenderer.setCompositionMode(compositeToCompositionMode(compositeMode));
1207 if (!traverseColr1(paintReader, face7, &childElement, &compositeRenderer))
1208 return false;
1209
1210 hr = paintReader->MoveToParent();
1211 if (FAILED(hr)) {
1212 qErrnoWarning(hr, "%s: Cannot move back to parent composite node",
1213 __FUNCTION__);
1214 return false;
1215 }
1216
1217 paintGraphRenderer->drawImage(compositeRenderer.endRender());
1218 }
1219
1220 break;
1221 }
1222
1223 case DWRITE_PAINT_TYPE_RADIAL_GRADIENT:
1224 {
1225 const QPointF c0(paintElement->paint.radialGradient.x0 * m_unitsPerEm,
1226 paintElement->paint.radialGradient.y0 * m_unitsPerEm);
1227 const QPointF c1(paintElement->paint.radialGradient.x1 * m_unitsPerEm,
1228 paintElement->paint.radialGradient.y1 * m_unitsPerEm);
1229 const qreal r0 = paintElement->paint.radialGradient.radius0 * m_unitsPerEm;
1230 const qreal r1 = paintElement->paint.radialGradient.radius1 * m_unitsPerEm;
1231
1232 const QGradient::Spread spread
1233 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1234
1235 const QGradientStops gradientStops
1236 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1237
1238 paintGraphRenderer->setRadialGradient(c0, r0, c1, r1, spread, gradientStops);
1239 paintGraphRenderer->drawCurrentPath();
1240 break;
1241 }
1242
1243 case DWRITE_PAINT_TYPE_SWEEP_GRADIENT:
1244 {
1245 const QPointF center(paintElement->paint.sweepGradient.centerX * m_unitsPerEm,
1246 paintElement->paint.sweepGradient.centerY * m_unitsPerEm);
1247 const qreal startAngle = paintElement->paint.sweepGradient.startAngle;
1248 const qreal endAngle = paintElement->paint.sweepGradient.endAngle;
1249
1250 const QGradient::Spread spread
1251 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1252 const QGradientStops gradientStops
1253 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1254
1255 paintGraphRenderer->setConicalGradient(center,
1256 startAngle,
1257 endAngle,
1258 spread,
1259 gradientStops);
1260 paintGraphRenderer->drawCurrentPath();
1261 break;
1262 }
1263
1264 case DWRITE_PAINT_TYPE_LINEAR_GRADIENT:
1265 {
1266 const QPointF p0(paintElement->paint.linearGradient.x0 * m_unitsPerEm,
1267 paintElement->paint.linearGradient.y0 * m_unitsPerEm);
1268 const QPointF p1(paintElement->paint.linearGradient.x1 * m_unitsPerEm,
1269 paintElement->paint.linearGradient.y1 * m_unitsPerEm);
1270 const QPointF p2(paintElement->paint.linearGradient.x2 * m_unitsPerEm,
1271 paintElement->paint.linearGradient.y2 * m_unitsPerEm);
1272
1273 const QGradient::Spread spread
1274 = extendToSpread(paintElement->paint.linearGradient.extendMode);
1275 const QGradientStops gradientStops
1276 = collectStops(paintElement->paint.linearGradient.gradientStopCount);
1277
1278 paintGraphRenderer->setLinearGradient(p0, p1, p2, spread, gradientStops);
1279 paintGraphRenderer->drawCurrentPath();
1280 break;
1281 }
1282
1283 case DWRITE_PAINT_TYPE_GLYPH:
1284 {
1285 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.glyph.glyphIndex);
1286 paintGraphRenderer->appendPath(glyphPath);
1287 if (!traverseChildren(1))
1288 return false;
1289 break;
1290 }
1291
1292 case DWRITE_PAINT_TYPE_COLOR_GLYPH:
1293 {
1294 D2D_RECT_F rect = paintElement->paint.colorGlyph.clipBox;
1295 QRect clipBox = QRectF(QPointF(rect.left, rect.top),
1296 QPointF(rect.right, rect.bottom)).toAlignedRect();
1297 if (!clipBox.isEmpty()) {
1298 QTransform coordinatesTransform;
1299 coordinatesTransform.scale(m_unitsPerEm, m_unitsPerEm);
1300 clipBox = coordinatesTransform.mapRect(clipBox);
1301
1302 paintGraphRenderer->setClip(clipBox);
1303 }
1304
1305 DWRITE_PAINT_ELEMENT childElement;
1306 if (FAILED(paintReader->MoveToFirstChild(&childElement, sizeof(DWRITE_PAINT_ELEMENT))))
1307 return false;
1308
1309 if (!traverseColr1(paintReader, face7, &childElement, paintGraphRenderer))
1310 return false;
1311
1312 if (FAILED(paintReader->MoveToParent()))
1313 return false;
1314
1315 break;
1316 }
1317
1318 case DWRITE_PAINT_TYPE_TRANSFORM:
1319 {
1320 QTransform transform = matrixToTransform(paintElement->paint.transform, m_unitsPerEm);
1321 paintGraphRenderer->prependTransform(transform);
1322 if (!traverseChildren(1))
1323 return false;
1324
1325 break;
1326 }
1327
1328 default:
1329 qCDebug(lcColrv1) << "Unhandled paint graph node type" << paintElement->paintType;
1330 break;
1331 };
1332
1333 return true;
1334}
1335
1336bool QWindowsFontEngineDirectWrite::renderColr1GlyphRun(QImage *image,
1337 const DWRITE_GLYPH_RUN *glyphRun,
1338 const DWRITE_MATRIX &matrix,
1339 QColor color) const
1340{
1341 qCDebug(lcColrv1) << "renderColr1GlyphRun,"
1342 << "families:" << fontDef.families;
1343 ComPtr<IDWriteFontFace7> face7;
1344 HRESULT hr = m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace7),
1345 &face7);
1346 if (SUCCEEDED(hr)) {
1347 DWRITE_PAINT_FEATURE_LEVEL featureLevel =
1348 face7->GetPaintFeatureLevel(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE);
1349 if (featureLevel != DWRITE_PAINT_FEATURE_LEVEL_COLR_V1) {
1350 qCDebug(lcColrv1) << "Unsupported feature level:" << featureLevel;
1351 return true;
1352 }
1353
1354 ComPtr<IDWritePaintReader> paintReader;
1355 hr = face7->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
1356 featureLevel,
1357 &paintReader);
1358 if (FAILED(hr)) {
1359 qErrnoWarning(hr, "%s: CreatePaintReader failed", __FUNCTION__);
1360 return false;
1361 }
1362
1363 Q_ASSERT(glyphRun->glyphCount == 1);
1364 DWRITE_PAINT_ELEMENT paintElement;
1365 D2D_RECT_F clipBox;
1366 hr = paintReader->SetCurrentGlyph(glyphRun->glyphIndices[0],
1367 &paintElement,
1368 sizeof(DWRITE_PAINT_ELEMENT),
1369 &clipBox,
1370 nullptr);
1371 if (FAILED(hr)) {
1372 qErrnoWarning(hr, "%s: SetCurrentGlyph failed", __FUNCTION__);
1373 return false;
1374 }
1375
1376 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE) {
1377 qCDebug(lcColrv1) << "Glyph" << glyphRun->glyphIndices[0]
1378 << "does not have a paint graph";
1379 return false;
1380 }
1381
1382 DWRITE_COLOR_F dwColor;
1383 dwColor.r = color.redF();
1384 dwColor.g = color.greenF();
1385 dwColor.b = color.blueF();
1386 dwColor.a = color.alphaF();
1387 paintReader->SetTextColor(dwColor);
1388
1389 QRectF boundingRect = QRectF(QPointF(clipBox.left, clipBox.top),
1390 QPointF(clipBox.right, clipBox.bottom));
1391 if (boundingRect.isNull()) {
1392 QColrPaintGraphRenderer boundingRectCalculator;
1393 boundingRectCalculator.beginCalculateBoundingBox();
1394 if (traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &boundingRectCalculator))
1395 boundingRect = boundingRectCalculator.boundingRect();
1396 }
1397
1398 QTransform initialTransform;
1399 initialTransform.scale(fontDef.pixelSize, fontDef.pixelSize);
1400 boundingRect = initialTransform.mapRect(boundingRect);
1401
1402 QTransform originalXform = matrixToTransform(matrix);
1403 boundingRect = originalXform.mapRect(boundingRect);
1404
1405 qCDebug(lcColrv1).noquote() << "Bounds of"
1406 << glyphRun->glyphIndices[0]
1407 << " in device coordinates:"
1408 << boundingRect;
1409
1410 QColrPaintGraphRenderer graphRenderer;
1411 graphRenderer.setBoundingRect(boundingRect);
1412 graphRenderer.beginRender(fontDef.pixelSize / m_unitsPerEm, matrixToTransform(matrix));
1413 if (!traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &graphRenderer))
1414 return false;
1415
1416 *image = graphRenderer.endRender();
1417 } else {
1418 qCDebug(lcColrv1) << "Font face does not support IDWriteFontFace7 interface";
1419 }
1420
1421 return true;
1422}
1423#endif // QT_CONFIG(directwritecolrv1)
1424
1425QImage QWindowsFontEngineDirectWrite::renderColorGlyph(DWRITE_GLYPH_RUN *glyphRun,
1426 const DWRITE_MATRIX &transform,
1427 DWRITE_RENDERING_MODE renderMode,
1428 DWRITE_MEASURING_MODE measureMode,
1429 DWRITE_GRID_FIT_MODE gridFitMode,
1430 QColor color,
1431 QRect boundingRect) const
1432{
1433 QImage ret;
1434
1435#if QT_CONFIG(directwritecolrv1)
1436 // Start by trying COLRv1 glyph, where we need to get the paint nodes ourselves and render
1437 // them.
1438 renderColr1GlyphRun(&ret, glyphRun, transform, color);
1439#endif // QT_CONFIG(directwritecolrv1)
1440
1441#if QT_CONFIG(directwrite3)
1442 // If not successful, we try the modern API that supports both embedded pixmaps or COLRv0
1443 // glyphs, or a combination.
1444 if (ret.isNull()) {
1445 ComPtr<IDWriteFactory4> factory4;
1446 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory4),
1447 &factory4);
1448 if (SUCCEEDED(hr)) {
1449 const DWRITE_GLYPH_IMAGE_FORMATS supportedBitmapFormats =
1450 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_PNG
1451 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
1452 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF);
1453
1454 const DWRITE_GLYPH_IMAGE_FORMATS glyphFormats =
1455 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_COLR
1456 | supportedBitmapFormats
1457 | DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE
1458 | DWRITE_GLYPH_IMAGE_FORMATS_CFF);
1459
1460 ComPtr<IDWriteColorGlyphRunEnumerator1> enumerator;
1461 hr = factory4->TranslateColorGlyphRun(D2D1::Point2F(0.0f, 0.0f),
1462 glyphRun,
1463 NULL,
1464 glyphFormats,
1465 measureMode,
1466 NULL,
1467 0,
1468 &enumerator);
1469 BOOL ok = true;
1470 while (SUCCEEDED(hr) && ok) {
1471 hr = enumerator->MoveNext(&ok);
1472 if (!ok)
1473 break;
1474
1475 const DWRITE_COLOR_GLYPH_RUN1 *colorGlyphRun = nullptr;
1476 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1477 if (FAILED(hr)) {
1478 qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed", __FUNCTION__);
1479 return QImage{};
1480 }
1481
1482 if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_NONE) {
1483 // On some older platforms, we get a glyph run with format NONE before
1484 // the actual ones
1485 continue;
1486 } else if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_COLR) {
1487 if (ret.isNull()) {
1488 ret = QImage(boundingRect.width() - 1,
1489 boundingRect.height() - 1,
1490 QImage::Format_ARGB32_Premultiplied);
1491 ret.fill(0);
1492 }
1493
1494 if (renderColr0GlyphRun(&ret,
1495 reinterpret_cast<const DWRITE_COLOR_GLYPH_RUN *>(colorGlyphRun), // Broken inheritance in MinGW
1496 transform,
1497 renderMode,
1498 measureMode,
1499 gridFitMode,
1500 color,
1501 boundingRect)) {
1502 break;
1503 }
1504 } else if (colorGlyphRun->glyphImageFormat & supportedBitmapFormats) {
1505 ComPtr<IDWriteFontFace4> face4;
1506 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
1507 &face4))) {
1508 DWRITE_GLYPH_IMAGE_DATA data;
1509 void *ctx;
1510 Q_ASSERT(glyphRun->glyphCount == 1);
1511 HRESULT hr = face4->GetGlyphImageData(glyphRun->glyphIndices[0],
1512 fontDef.pixelSize,
1513 DWRITE_GLYPH_IMAGE_FORMATS(colorGlyphRun->glyphImageFormat & supportedBitmapFormats),
1514 &data,
1515 &ctx);
1516 if (FAILED(hr)) {
1517 qErrnoWarning("%s: GetGlyphImageData failed", __FUNCTION__);
1518 return QImage{};
1519 }
1520
1521 auto fnc = qScopeGuard([&]() {
1522 if (data.imageData != nullptr)
1523 face4->ReleaseGlyphImageData(ctx);
1524 });
1525
1526 if (data.pixelsPerEm == 0 || data.imageData == nullptr || data.imageDataSize == 0) {
1527 qCWarning(lcQpaFonts) << __FUNCTION__
1528 << "Failed to retrieve image data for glyph"
1529 << glyphRun->glyphIndices[0]
1530 << "in font:"
1531 << fontDef.families
1532 << "with formats:"
1533 << colorGlyphRun->glyphImageFormat;
1534 } else {
1535 const char *format;
1536 switch (colorGlyphRun->glyphImageFormat) {
1537 case DWRITE_GLYPH_IMAGE_FORMATS_JPEG:
1538 format = "JPEG";
1539 break;
1540 case DWRITE_GLYPH_IMAGE_FORMATS_TIFF:
1541 format = "TIFF";
1542 break;
1543 default:
1544 format = "PNG";
1545 break;
1546 };
1547
1548 ret = QImage::fromData(reinterpret_cast<const uchar *>(data.imageData),
1549 data.imageDataSize,
1550 format);
1551
1552 QTransform matrix(transform.m11, transform.m12,
1553 transform.m21, transform.m22,
1554 transform.dx, transform.dy);
1555
1556 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
1557 matrix.scale(scale, scale);
1558
1559 if (!matrix.isIdentity())
1560 ret = ret.transformed(matrix, Qt::SmoothTransformation);
1561
1562 break;
1563 }
1564 }
1565
1566 } else {
1567 qCDebug(lcQpaFonts) << "Found glyph run with unsupported format"
1568 << colorGlyphRun->glyphImageFormat;
1569 }
1570 }
1571 }
1572 }
1573#endif // QT_CONFIG(directwrite3)
1574
1575 // If all else fails, we go through the pre-dwrite3 version, which just supports COLRv0.
1576 if (ret.isNull()) {
1577 ComPtr<IDWriteFactory2> factory2;
1578 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1579 &factory2);
1580 if (FAILED(hr))
1581 return ret;
1582
1583 ComPtr<IDWriteColorGlyphRunEnumerator> enumerator;
1584 hr = factory2->TranslateColorGlyphRun(0.0f,
1585 0.0f,
1586 glyphRun,
1587 NULL,
1588 measureMode,
1589 NULL,
1590 0,
1591 &enumerator);
1592 if (SUCCEEDED(hr)) {
1593 ret = QImage(boundingRect.width() - 1,
1594 boundingRect.height() - 1,
1595 QImage::Format_ARGB32_Premultiplied);
1596 ret.fill(0);
1597
1598 BOOL ok = true;
1599 while (SUCCEEDED(hr) && ok) {
1600 hr = enumerator->MoveNext(&ok);
1601 if (FAILED(hr)) {
1602 qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::MoveNext failed", __FUNCTION__);
1603 return QImage{};
1604 }
1605
1606 if (ok) {
1607 const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun = nullptr;
1608 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1609 if (FAILED(hr)) { // No colored runs, only outline
1610 qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed", __FUNCTION__);
1611 return QImage{};
1612 }
1613
1614 if (!renderColr0GlyphRun(&ret,
1615 colorGlyphRun,
1616 transform,
1617 renderMode,
1618 measureMode,
1619 gridFitMode,
1620 color,
1621 boundingRect)) {
1622 return QImage{};
1623 }
1624 }
1625 }
1626 }
1627 }
1628
1629 return ret;
1630}
1631
1632QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
1633 const QFixedPoint &subPixelPosition,
1634 int margin,
1635 const QTransform &originalTransform,
1636 const QColor &color)
1637{
1638 UINT16 glyphIndex = t;
1639 FLOAT glyphAdvance = 0;
1640
1641 DWRITE_GLYPH_OFFSET glyphOffset;
1642 glyphOffset.advanceOffset = 0;
1643 glyphOffset.ascenderOffset = 0;
1644
1645 DWRITE_GLYPH_RUN glyphRun;
1646 glyphRun.fontFace = m_directWriteFontFace;
1647 glyphRun.fontEmSize = fontDef.pixelSize;
1648 glyphRun.glyphCount = 1;
1649 glyphRun.glyphIndices = &glyphIndex;
1650 glyphRun.glyphAdvances = &glyphAdvance;
1651 glyphRun.isSideways = false;
1652 glyphRun.bidiLevel = 0;
1653 glyphRun.glyphOffsets = &glyphOffset;
1654
1655 QTransform xform = originalTransform;
1656 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
1657 xform.scale(fontDef.stretch / 100.0, 1.0);
1658
1659 DWRITE_MATRIX transform;
1660 transform.dx = subPixelPosition.x.toReal();
1661 transform.dy = 0;
1662 transform.m11 = xform.m11();
1663 transform.m12 = xform.m12();
1664 transform.m21 = xform.m21();
1665 transform.m22 = xform.m22();
1666
1667 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1668 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1669 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1670 ? DWRITE_GRID_FIT_MODE_DISABLED
1671 : DWRITE_GRID_FIT_MODE_DEFAULT;
1672
1673 ComPtr<IDWriteFactory2> factory2;
1674 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1675 &factory2);
1676 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1677 if (!SUCCEEDED(hr)) {
1678 qErrnoWarning(hr, "%s: Failed to query IDWriteFactory2 interface.", __FUNCTION__);
1679 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1680 &glyphRun,
1681 1.0f,
1682 &transform,
1683 renderMode,
1684 measureMode,
1685 0.0, 0.0,
1686 &glyphAnalysis
1687 );
1688 } else {
1689 hr = factory2->CreateGlyphRunAnalysis(
1690 &glyphRun,
1691 &transform,
1692 renderMode,
1693 measureMode,
1694 gridFitMode,
1695 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1696 0.0, 0.0,
1697 &glyphAnalysis
1698 );
1699 }
1700
1701 if (SUCCEEDED(hr)) {
1702 QRect rect = paintGraphBounds(t, transform);
1703 if (rect.isEmpty())
1704 rect = alphaTextureBounds(t, transform);
1705 if (rect.isEmpty())
1706 rect = colorBitmapBounds(t, transform);
1707
1708 if (rect.isEmpty()) {
1709 qCDebug(lcQpaFonts) << __FUNCTION__ << "Cannot get alpha texture bounds. Falling back to slower rendering path.";
1710 return QImage();
1711 }
1712
1713 QRect boundingRect = QRect(QPoint(rect.left() - margin,
1714 rect.top() - margin),
1715 QPoint(rect.right() + margin,
1716 rect.bottom() + margin));
1717
1718 QImage image;
1719 if (glyphFormat == QFontEngine::Format_ARGB) {
1720 image = renderColorGlyph(&glyphRun,
1721 transform,
1722 renderMode,
1723 measureMode,
1724 gridFitMode,
1725 color,
1726 boundingRect);
1727 }
1728
1729 // Not a color glyph, fall back to regular glyph rendering
1730 if (image.isNull()) {
1731 // -1 due to Qt's off-by-one definition of a QRect
1732 image = QImage(boundingRect.width() - 1,
1733 boundingRect.height() - 1,
1734 glyphFormat == QFontEngine::Format_ARGB
1735 ? QImage::Format_ARGB32_Premultiplied
1736 : QImage::Format_RGB32);
1737 image.fill(image.format() == QImage::Format_ARGB32_Premultiplied ? 0x0 : 0xffffffff);
1738
1739 float r, g, b, a;
1740 if (glyphFormat == QFontEngine::Format_ARGB) {
1741 r = float(color.redF());
1742 g = float(color.greenF());
1743 b = float(color.blueF());
1744 a = float(color.alphaF());
1745 } else {
1746 r = g = b = a = 0.0;
1747 }
1748
1749 renderGlyphRun(&image,
1750 r,
1751 g,
1752 b,
1753 a,
1754 glyphAnalysis.Get(),
1755 boundingRect,
1756 renderMode);
1757 }
1758
1759 return image;
1760 } else {
1761 qErrnoWarning(hr, "%s: CreateGlyphRunAnalysis failed", __FUNCTION__);
1762 return QImage();
1763 }
1764}
1765
1766void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
1767 float r,
1768 float g,
1769 float b,
1770 float a,
1771 IDWriteGlyphRunAnalysis *glyphAnalysis,
1772 const QRect &boundingRect,
1773 DWRITE_RENDERING_MODE renderMode) const
1774{
1775 const int width = destination->width();
1776 const int height = destination->height();
1777
1778 r *= 255.0;
1779 g *= 255.0;
1780 b *= 255.0;
1781
1782 const int size = width * height * 3;
1783 if (size > 0) {
1784 RECT rect;
1785 rect.left = boundingRect.left();
1786 rect.top = boundingRect.top();
1787 rect.right = boundingRect.right();
1788 rect.bottom = boundingRect.bottom();
1789
1790 QVarLengthArray<BYTE, 1024> alphaValueArray(size);
1791 BYTE *alphaValues = alphaValueArray.data();
1792 memset(alphaValues, 0, size);
1793
1794 HRESULT hr = glyphAnalysis->CreateAlphaTexture(renderMode == DWRITE_RENDERING_MODE_ALIASED
1795 ? DWRITE_TEXTURE_ALIASED_1x1
1796 : DWRITE_TEXTURE_CLEARTYPE_3x1,
1797 &rect,
1798 alphaValues,
1799 size);
1800 if (SUCCEEDED(hr)) {
1801 if (destination->hasAlphaChannel()) { // Color glyphs
1802 for (int y = 0; y < height; ++y) {
1803 uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
1804 BYTE *src = alphaValues + width * 3 * y;
1805
1806 for (int x = 0; x < width; ++x) {
1807 float redAlpha = a * *src++ / 255.0;
1808 float greenAlpha = a * *src++ / 255.0;
1809 float blueAlpha = a * *src++ / 255.0;
1810 float averageAlpha = (redAlpha + greenAlpha + blueAlpha) / 3.0;
1811
1812 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1813 qSwap(redAlpha, blueAlpha);
1814
1815 QRgb currentRgb = dest[x];
1816 dest[x] = qRgba(qRound(qRed(currentRgb) * (1.0 - averageAlpha) + averageAlpha * r),
1817 qRound(qGreen(currentRgb) * (1.0 - averageAlpha) + averageAlpha * g),
1818 qRound(qBlue(currentRgb) * (1.0 - averageAlpha) + averageAlpha * b),
1819 qRound(qAlpha(currentRgb) * (1.0 - averageAlpha) + averageAlpha * 255));
1820 }
1821 }
1822 } else if (renderMode == DWRITE_RENDERING_MODE_ALIASED) {
1823 for (int y = 0; y < height; ++y) {
1824 uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
1825 BYTE *src = alphaValues + width * y;
1826
1827 for (int x = 0; x < width; ++x) {
1828 int alpha = *(src++);
1829 dest[x] = (alpha << 16) + (alpha << 8) + alpha;
1830 }
1831 }
1832 } else {
1833 for (int y = 0; y < height; ++y) {
1834 uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
1835 BYTE *src = alphaValues + width * 3 * y;
1836
1837 for (int x = 0; x < width; ++x) {
1838 BYTE redAlpha = *(src + 0);
1839 BYTE greenAlpha = *(src + 1);
1840 BYTE blueAlpha = *(src + 2);
1841
1842 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1843 qSwap(redAlpha, blueAlpha);
1844
1845 dest[x] = qRgb(redAlpha, greenAlpha, blueAlpha);
1846 src += 3;
1847 }
1848 }
1849 }
1850 } else {
1851 qErrnoWarning("%s: CreateAlphaTexture failed", __FUNCTION__);
1852 }
1853 }
1854}
1855
1856QImage QWindowsFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t,
1857 const QFixedPoint &subPixelPosition,
1858 const QTransform &xform)
1859{
1860 QImage mask = imageForGlyph(t,
1861 subPixelPosition,
1862 glyphMargin(QFontEngine::Format_A32),
1863 xform);
1864
1865 if (mask.isNull()) {
1866 mask = QFontEngine::renderedPathForGlyph(t, Qt::white);
1867 if (!xform.isIdentity())
1868 mask = mask.transformed(xform);
1869 }
1870
1871 return mask.depth() == 32
1872 ? mask
1873 : mask.convertToFormat(QImage::Format_RGB32);
1874}
1875
1876QFontEngine *QWindowsFontEngineDirectWrite::cloneWithSize(qreal pixelSize) const
1877{
1878 QWindowsFontEngineDirectWrite *fontEngine = new QWindowsFontEngineDirectWrite(m_directWriteFontFace,
1879 pixelSize,
1880 m_fontEngineData);
1881
1882 fontEngine->fontDef = fontDef;
1883 fontEngine->fontDef.pixelSize = pixelSize;
1884 if (!m_uniqueFamilyName.isEmpty()) {
1885 fontEngine->setUniqueFamilyName(m_uniqueFamilyName);
1886 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
1887 static_cast<QWindowsFontDatabase *>(pfdb)->refUniqueFont(m_uniqueFamilyName);
1888 }
1889
1890 return fontEngine;
1891}
1892
1893Qt::HANDLE QWindowsFontEngineDirectWrite::handle() const
1894{
1895 return m_directWriteFontFace;
1896}
1897
1898void QWindowsFontEngineDirectWrite::initFontInfo(const QFontDef &request,
1899 int dpi)
1900{
1901 fontDef = request;
1902
1903 if (fontDef.pointSize < 0)
1904 fontDef.pointSize = fontDef.pixelSize * 72. / dpi;
1905 else if (fontDef.pixelSize == -1)
1906 fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.);
1907
1908 m_faceId.variableAxes = request.variableAxisValues;
1909
1910#if QT_CONFIG(directwrite3)
1911 IDWriteFontFace3 *face3 = nullptr;
1912 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace3),
1913 reinterpret_cast<void **>(&face3)))) {
1914
1915 fontDef.weight = QWindowsDirectWriteFontDatabase::fromDirectWriteWeight(face3->GetWeight());
1916 fontDef.style = QWindowsDirectWriteFontDatabase::fromDirectWriteStyle(face3->GetStyle());
1917
1918 IDWriteLocalizedStrings *names;
1919 if (SUCCEEDED(face3->GetFaceNames(&names))) {
1920 wchar_t englishLocale[] = L"en-us";
1921 fontDef.styleName = QWindowsDirectWriteFontDatabase::localeString(names, englishLocale);
1922 names->Release();
1923 }
1924
1925 // Color font
1926 if (face3->IsColorFont())
1927 glyphFormat = QFontEngine::Format_ARGB;
1928
1929 face3->Release();
1930 }
1931#endif
1932}
1933
1934QString QWindowsFontEngineDirectWrite::fontNameSubstitute(const QString &familyName)
1935{
1936 const QString substitute =
1937 QWinRegistryKey(HKEY_LOCAL_MACHINE,
1938 LR"(Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes)")
1939 .stringValue(familyName);
1940 return substitute.isEmpty() ? familyName : substitute;
1941}
1942
1943QRect QWindowsFontEngineDirectWrite::alphaTextureBounds(glyph_t glyph,
1944 const DWRITE_MATRIX &transform)
1945{
1946 UINT16 glyphIndex = glyph;
1947 FLOAT glyphAdvance = 0;
1948
1949 DWRITE_GLYPH_OFFSET glyphOffset;
1950 glyphOffset.advanceOffset = 0;
1951 glyphOffset.ascenderOffset = 0;
1952
1953 DWRITE_GLYPH_RUN glyphRun;
1954 glyphRun.fontFace = m_directWriteFontFace;
1955 glyphRun.fontEmSize = fontDef.pixelSize;
1956 glyphRun.glyphCount = 1;
1957 glyphRun.glyphIndices = &glyphIndex;
1958 glyphRun.glyphAdvances = &glyphAdvance;
1959 glyphRun.isSideways = false;
1960 glyphRun.bidiLevel = 0;
1961 glyphRun.glyphOffsets = &glyphOffset;
1962
1963 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1964 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1965 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1966 ? DWRITE_GRID_FIT_MODE_DISABLED
1967 : DWRITE_GRID_FIT_MODE_DEFAULT;
1968
1969 ComPtr<IDWriteFactory2> factory2 = nullptr;
1970 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1971 &factory2);
1972
1973 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1974 if (SUCCEEDED(hr)) {
1975 hr = factory2->CreateGlyphRunAnalysis(
1976 &glyphRun,
1977 &transform,
1978 renderMode,
1979 measureMode,
1980 gridFitMode,
1981 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1982 0.0, 0.0,
1983 &glyphAnalysis
1984 );
1985 } else {
1986 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1987 &glyphRun,
1988 1.0f,
1989 &transform,
1990 renderMode,
1991 measureMode,
1992 0.0, 0.0,
1993 &glyphAnalysis
1994 );
1995 }
1996
1997 if (SUCCEEDED(hr)) {
1998 RECT rect;
1999 hr = glyphAnalysis->GetAlphaTextureBounds(renderMode == DWRITE_RENDERING_MODE_ALIASED
2000 ? DWRITE_TEXTURE_ALIASED_1x1
2001 : DWRITE_TEXTURE_CLEARTYPE_3x1,
2002 &rect);
2003 if (FAILED(hr) || rect.left == rect.right || rect.top == rect.bottom)
2004 return QRect{};
2005
2006 return QRect(QPoint(rect.left, rect.top), QPoint(rect.right, rect.bottom));
2007 } else {
2008 return QRect{};
2009 }
2010}
2011
2012QRect QWindowsFontEngineDirectWrite::colorBitmapBounds(glyph_t glyph, const DWRITE_MATRIX &transform)
2013{
2014#if QT_CONFIG(directwrite3)
2015 ComPtr<IDWriteFontFace4> face4;
2016 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
2017 &face4))) {
2018 DWRITE_GLYPH_IMAGE_FORMATS formats = face4->GetGlyphImageFormats();
2019
2020 const DWRITE_GLYPH_IMAGE_FORMATS supportedBitmapFormats =
2021 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_PNG
2022 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
2023 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF);
2024
2025 if (formats & supportedBitmapFormats) {
2026 DWRITE_GLYPH_IMAGE_DATA data;
2027 void *ctx;
2028 HRESULT hr = face4->GetGlyphImageData(glyph,
2029 fontDef.pixelSize,
2030 DWRITE_GLYPH_IMAGE_FORMATS(formats & supportedBitmapFormats),
2031 &data,
2032 &ctx);
2033 if (FAILED(hr)) {
2034 qErrnoWarning("%s: GetGlyphImageData failed", __FUNCTION__);
2035 return QRect{};
2036 }
2037
2038 auto fnc = qScopeGuard([&]() {
2039 if (data.imageData != nullptr)
2040 face4->ReleaseGlyphImageData(ctx);
2041 });
2042
2043 if (data.pixelsPerEm == 0 || data.imageData == nullptr || data.imageDataSize == 0)
2044 return QRect{};
2045
2046 QRect rect(-data.horizontalLeftOrigin.x,
2047 -data.horizontalLeftOrigin.y,
2048 data.pixelSize.width,
2049 data.pixelSize.height);
2050
2051 QTransform matrix(transform.m11, transform.m12,
2052 transform.m21, transform.m22,
2053 transform.dx, transform.dy);
2054
2055 // GetGlyphImageData returns the closest matching size, which we need to scale down
2056 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
2057 matrix.scale(scale, scale);
2058 rect = matrix.mapRect(rect);
2059
2060 return rect;
2061 }
2062 }
2063
2064 return QRect{};
2065#else
2066 Q_UNUSED(glyph);
2067 Q_UNUSED(transform);
2068 return QRect{};
2069#endif // QT_CONFIG(directwrite3)
2070}
2071
2072glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph,
2073 const QFixedPoint &subPixelPosition,
2074 const QTransform &originalTransform,
2075 GlyphFormat format)
2076{
2077 QTransform matrix = originalTransform;
2078 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
2079 matrix.scale(fontDef.stretch / 100.0, 1.0);
2080
2081 glyph_metrics_t bbox = QFontEngine::boundingBox(glyph, matrix); // To get transformed advance
2082
2083 DWRITE_MATRIX transform;
2084 transform.dx = subPixelPosition.x.toReal();
2085 transform.dy = 0;
2086 transform.m11 = matrix.m11();
2087 transform.m12 = matrix.m12();
2088 transform.m21 = matrix.m21();
2089 transform.m22 = matrix.m22();
2090
2091 // Try COLRv1 approach first
2092 QRect rect = paintGraphBounds(glyph, transform);
2093
2094 // Then try general approach (works with regular truetype glyphs as well as COLRv0)
2095 if (rect.isEmpty())
2096 rect = alphaTextureBounds(glyph, transform);
2097
2098 // If this fails, we check if it is an embedded color bitmap
2099 if (rect.isEmpty())
2100 rect = colorBitmapBounds(glyph, transform);
2101
2102 // If we are unable to find metrics, just return the design metrics
2103 if (rect.isEmpty())
2104 return bbox;
2105
2106 int margin = glyphMargin(format);
2107 return glyph_metrics_t(rect.left(),
2108 rect.top(),
2109 rect.right() - rect.left() + margin * 2,
2110 rect.bottom() - rect.top() + margin * 2,
2111 bbox.xoff, bbox.yoff);
2112}
2113
2114QImage QWindowsFontEngineDirectWrite::bitmapForGlyph(glyph_t glyph,
2115 const QFixedPoint &subPixelPosition,
2116 const QTransform &t,
2117 const QColor &color)
2118{
2119 return imageForGlyph(glyph, subPixelPosition, glyphMargin(QFontEngine::Format_ARGB), t, color);
2120}
2121
2122QList<QFontVariableAxis> QWindowsFontEngineDirectWrite::variableAxes() const
2123{
2124 return m_variableAxes;
2125}
2126
2127bool QWindowsFontEngineDirectWrite::expectsGammaCorrectedBlending(QFontEngine::GlyphFormat format) const
2128{
2129 return format == QFontEngine::Format_A32;
2130}
2131
2132QT_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)