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.size(), QImage::Format_ARGB32_Premultiplied);
1489 ret.fill(0);
1490 }
1491
1492 if (renderColr0GlyphRun(&ret,
1493 reinterpret_cast<const DWRITE_COLOR_GLYPH_RUN *>(colorGlyphRun), // Broken inheritance in MinGW
1494 transform,
1495 renderMode,
1496 measureMode,
1497 gridFitMode,
1498 color,
1499 boundingRect)) {
1500 break;
1501 }
1502 } else if (colorGlyphRun->glyphImageFormat & supportedBitmapFormats) {
1503 ComPtr<IDWriteFontFace4> face4;
1504 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
1505 &face4))) {
1506 DWRITE_GLYPH_IMAGE_DATA data;
1507 void *ctx;
1508 Q_ASSERT(glyphRun->glyphCount == 1);
1509 HRESULT hr = face4->GetGlyphImageData(glyphRun->glyphIndices[0],
1510 fontDef.pixelSize,
1511 DWRITE_GLYPH_IMAGE_FORMATS(colorGlyphRun->glyphImageFormat & supportedBitmapFormats),
1512 &data,
1513 &ctx);
1514 if (FAILED(hr)) {
1515 qErrnoWarning("%s: GetGlyphImageData failed", __FUNCTION__);
1516 return QImage{};
1517 }
1518
1519 auto fnc = qScopeGuard([&]() {
1520 if (data.imageData != nullptr)
1521 face4->ReleaseGlyphImageData(ctx);
1522 });
1523
1524 if (data.pixelsPerEm == 0 || data.imageData == nullptr || data.imageDataSize == 0) {
1525 qCWarning(lcQpaFonts) << __FUNCTION__
1526 << "Failed to retrieve image data for glyph"
1527 << glyphRun->glyphIndices[0]
1528 << "in font:"
1529 << fontDef.families
1530 << "with formats:"
1531 << colorGlyphRun->glyphImageFormat;
1532 } else {
1533 const char *format;
1534 switch (colorGlyphRun->glyphImageFormat) {
1535 case DWRITE_GLYPH_IMAGE_FORMATS_JPEG:
1536 format = "JPEG";
1537 break;
1538 case DWRITE_GLYPH_IMAGE_FORMATS_TIFF:
1539 format = "TIFF";
1540 break;
1541 default:
1542 format = "PNG";
1543 break;
1544 };
1545
1546 ret = QImage::fromData(reinterpret_cast<const uchar *>(data.imageData),
1547 data.imageDataSize,
1548 format);
1549
1550 QTransform matrix(transform.m11, transform.m12,
1551 transform.m21, transform.m22,
1552 transform.dx, transform.dy);
1553
1554 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
1555 matrix.scale(scale, scale);
1556
1557 if (!matrix.isIdentity())
1558 ret = ret.transformed(matrix, Qt::SmoothTransformation);
1559
1560 break;
1561 }
1562 }
1563
1564 } else {
1565 qCDebug(lcQpaFonts) << "Found glyph run with unsupported format"
1566 << colorGlyphRun->glyphImageFormat;
1567 }
1568 }
1569 }
1570 }
1571#endif // QT_CONFIG(directwrite3)
1572
1573 // If all else fails, we go through the pre-dwrite3 version, which just supports COLRv0.
1574 if (ret.isNull()) {
1575 ComPtr<IDWriteFactory2> factory2;
1576 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1577 &factory2);
1578 if (FAILED(hr))
1579 return ret;
1580
1581 ComPtr<IDWriteColorGlyphRunEnumerator> enumerator;
1582 hr = factory2->TranslateColorGlyphRun(0.0f,
1583 0.0f,
1584 glyphRun,
1585 NULL,
1586 measureMode,
1587 NULL,
1588 0,
1589 &enumerator);
1590 if (SUCCEEDED(hr)) {
1591 ret = QImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied);
1592 ret.fill(0);
1593
1594 BOOL ok = true;
1595 while (SUCCEEDED(hr) && ok) {
1596 hr = enumerator->MoveNext(&ok);
1597 if (FAILED(hr)) {
1598 qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::MoveNext failed", __FUNCTION__);
1599 return QImage{};
1600 }
1601
1602 if (ok) {
1603 const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun = nullptr;
1604 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1605 if (FAILED(hr)) { // No colored runs, only outline
1606 qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed", __FUNCTION__);
1607 return QImage{};
1608 }
1609
1610 if (!renderColr0GlyphRun(&ret,
1611 colorGlyphRun,
1612 transform,
1613 renderMode,
1614 measureMode,
1615 gridFitMode,
1616 color,
1617 boundingRect)) {
1618 return QImage{};
1619 }
1620 }
1621 }
1622 }
1623 }
1624
1625 return ret;
1626}
1627
1628QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
1629 const QFixedPoint &subPixelPosition,
1630 int margin,
1631 const QTransform &originalTransform,
1632 const QColor &color)
1633{
1634 UINT16 glyphIndex = t;
1635 FLOAT glyphAdvance = 0;
1636
1637 DWRITE_GLYPH_OFFSET glyphOffset;
1638 glyphOffset.advanceOffset = 0;
1639 glyphOffset.ascenderOffset = 0;
1640
1641 DWRITE_GLYPH_RUN glyphRun;
1642 glyphRun.fontFace = m_directWriteFontFace;
1643 glyphRun.fontEmSize = fontDef.pixelSize;
1644 glyphRun.glyphCount = 1;
1645 glyphRun.glyphIndices = &glyphIndex;
1646 glyphRun.glyphAdvances = &glyphAdvance;
1647 glyphRun.isSideways = false;
1648 glyphRun.bidiLevel = 0;
1649 glyphRun.glyphOffsets = &glyphOffset;
1650
1651 QTransform xform = originalTransform;
1652 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
1653 xform.scale(fontDef.stretch / 100.0, 1.0);
1654
1655 DWRITE_MATRIX transform;
1656 transform.dx = subPixelPosition.x.toReal();
1657 transform.dy = 0;
1658 transform.m11 = xform.m11();
1659 transform.m12 = xform.m12();
1660 transform.m21 = xform.m21();
1661 transform.m22 = xform.m22();
1662
1663 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1664 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1665 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1666 ? DWRITE_GRID_FIT_MODE_DISABLED
1667 : DWRITE_GRID_FIT_MODE_DEFAULT;
1668
1669 ComPtr<IDWriteFactory2> factory2;
1670 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1671 &factory2);
1672 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1673 if (!SUCCEEDED(hr)) {
1674 qErrnoWarning(hr, "%s: Failed to query IDWriteFactory2 interface.", __FUNCTION__);
1675 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1676 &glyphRun,
1677 1.0f,
1678 &transform,
1679 renderMode,
1680 measureMode,
1681 0.0, 0.0,
1682 &glyphAnalysis
1683 );
1684 } else {
1685 hr = factory2->CreateGlyphRunAnalysis(
1686 &glyphRun,
1687 &transform,
1688 renderMode,
1689 measureMode,
1690 gridFitMode,
1691 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1692 0.0, 0.0,
1693 &glyphAnalysis
1694 );
1695 }
1696
1697 if (SUCCEEDED(hr)) {
1698 QRect rect = paintGraphBounds(t, transform);
1699 if (rect.isEmpty())
1700 rect = alphaTextureBounds(t, transform);
1701 if (rect.isEmpty())
1702 rect = colorBitmapBounds(t, transform);
1703
1704 if (rect.isEmpty()) {
1705 qCDebug(lcQpaFonts) << __FUNCTION__ << "Cannot get alpha texture bounds. Falling back to slower rendering path.";
1706 return QImage();
1707 }
1708
1709 QRect boundingRect = rect.adjusted(-margin, -margin, margin, margin);
1710
1711 QImage image;
1712 if (glyphFormat == QFontEngine::Format_ARGB) {
1713 image = renderColorGlyph(&glyphRun,
1714 transform,
1715 renderMode,
1716 measureMode,
1717 gridFitMode,
1718 color,
1719 boundingRect);
1720 }
1721
1722 // Not a color glyph, fall back to regular glyph rendering
1723 if (image.isNull()) {
1724 image = QImage(boundingRect.size(),
1725 glyphFormat == QFontEngine::Format_ARGB
1726 ? QImage::Format_ARGB32_Premultiplied
1727 : QImage::Format_RGB32);
1728 image.fill(image.format() == QImage::Format_ARGB32_Premultiplied ? 0x0 : 0xffffffff);
1729
1730 float r, g, b, a;
1731 if (glyphFormat == QFontEngine::Format_ARGB) {
1732 r = float(color.redF());
1733 g = float(color.greenF());
1734 b = float(color.blueF());
1735 a = float(color.alphaF());
1736 } else {
1737 r = g = b = a = 0.0;
1738 }
1739
1740 renderGlyphRun(&image,
1741 r,
1742 g,
1743 b,
1744 a,
1745 glyphAnalysis.Get(),
1746 boundingRect,
1747 renderMode);
1748 }
1749
1750 return image;
1751 } else {
1752 qErrnoWarning(hr, "%s: CreateGlyphRunAnalysis failed", __FUNCTION__);
1753 return QImage();
1754 }
1755}
1756
1757void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
1758 float r,
1759 float g,
1760 float b,
1761 float a,
1762 IDWriteGlyphRunAnalysis *glyphAnalysis,
1763 const QRect &boundingRect,
1764 DWRITE_RENDERING_MODE renderMode) const
1765{
1766 const int width = destination->width();
1767 const int height = destination->height();
1768
1769 r *= 255.0;
1770 g *= 255.0;
1771 b *= 255.0;
1772
1773 const int size = width * height * 3;
1774 if (size > 0) {
1775 RECT rect;
1776 rect.left = boundingRect.left();
1777 rect.top = boundingRect.top();
1778 rect.right = boundingRect.left() + boundingRect.width();
1779 rect.bottom = boundingRect.top() + boundingRect.height();
1780
1781 QVarLengthArray<BYTE, 1024> alphaValueArray(size);
1782 BYTE *alphaValues = alphaValueArray.data();
1783 memset(alphaValues, 0, size);
1784
1785 HRESULT hr = glyphAnalysis->CreateAlphaTexture(renderMode == DWRITE_RENDERING_MODE_ALIASED
1786 ? DWRITE_TEXTURE_ALIASED_1x1
1787 : DWRITE_TEXTURE_CLEARTYPE_3x1,
1788 &rect,
1789 alphaValues,
1790 size);
1791 if (SUCCEEDED(hr)) {
1792 if (destination->hasAlphaChannel()) { // Color glyphs
1793 for (int y = 0; y < height; ++y) {
1794 uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
1795 BYTE *src = alphaValues + width * 3 * y;
1796
1797 for (int x = 0; x < width; ++x) {
1798 float redAlpha = a * *src++ / 255.0;
1799 float greenAlpha = a * *src++ / 255.0;
1800 float blueAlpha = a * *src++ / 255.0;
1801 float averageAlpha = (redAlpha + greenAlpha + blueAlpha) / 3.0;
1802
1803 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1804 qSwap(redAlpha, blueAlpha);
1805
1806 QRgb currentRgb = dest[x];
1807 dest[x] = qRgba(qRound(qRed(currentRgb) * (1.0 - averageAlpha) + averageAlpha * r),
1808 qRound(qGreen(currentRgb) * (1.0 - averageAlpha) + averageAlpha * g),
1809 qRound(qBlue(currentRgb) * (1.0 - averageAlpha) + averageAlpha * b),
1810 qRound(qAlpha(currentRgb) * (1.0 - averageAlpha) + averageAlpha * 255));
1811 }
1812 }
1813 } else if (renderMode == DWRITE_RENDERING_MODE_ALIASED) {
1814 for (int y = 0; y < height; ++y) {
1815 uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
1816 BYTE *src = alphaValues + width * y;
1817
1818 for (int x = 0; x < width; ++x) {
1819 int alpha = *(src++);
1820 dest[x] = (alpha << 16) + (alpha << 8) + alpha;
1821 }
1822 }
1823 } else {
1824 for (int y = 0; y < height; ++y) {
1825 uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
1826 BYTE *src = alphaValues + width * 3 * y;
1827
1828 for (int x = 0; x < width; ++x) {
1829 BYTE redAlpha = *(src + 0);
1830 BYTE greenAlpha = *(src + 1);
1831 BYTE blueAlpha = *(src + 2);
1832
1833 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1834 qSwap(redAlpha, blueAlpha);
1835
1836 dest[x] = qRgb(redAlpha, greenAlpha, blueAlpha);
1837 src += 3;
1838 }
1839 }
1840 }
1841 } else {
1842 qErrnoWarning("%s: CreateAlphaTexture failed", __FUNCTION__);
1843 }
1844 }
1845}
1846
1847QImage QWindowsFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t,
1848 const QFixedPoint &subPixelPosition,
1849 const QTransform &xform)
1850{
1851 QImage mask = imageForGlyph(t,
1852 subPixelPosition,
1853 glyphMargin(QFontEngine::Format_A32),
1854 xform);
1855
1856 if (mask.isNull()) {
1857 mask = QFontEngine::renderedPathForGlyph(t, Qt::white);
1858 if (!xform.isIdentity())
1859 mask = mask.transformed(xform);
1860 }
1861
1862 return mask.depth() == 32
1863 ? mask
1864 : mask.convertToFormat(QImage::Format_RGB32);
1865}
1866
1867QFontEngine *QWindowsFontEngineDirectWrite::cloneWithSize(qreal pixelSize) const
1868{
1869 QWindowsFontEngineDirectWrite *fontEngine = new QWindowsFontEngineDirectWrite(m_directWriteFontFace,
1870 pixelSize,
1871 m_fontEngineData);
1872
1873 fontEngine->fontDef = fontDef;
1874 fontEngine->fontDef.pixelSize = pixelSize;
1875 if (!m_uniqueFamilyName.isEmpty()) {
1876 fontEngine->setUniqueFamilyName(m_uniqueFamilyName);
1877 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
1878 static_cast<QWindowsFontDatabase *>(pfdb)->refUniqueFont(m_uniqueFamilyName);
1879 }
1880
1881 return fontEngine;
1882}
1883
1884Qt::HANDLE QWindowsFontEngineDirectWrite::handle() const
1885{
1886 return m_directWriteFontFace;
1887}
1888
1889void QWindowsFontEngineDirectWrite::initFontInfo(const QFontDef &request,
1890 int dpi)
1891{
1892 fontDef = request;
1893
1894 if (fontDef.pointSize < 0)
1895 fontDef.pointSize = fontDef.pixelSize * 72. / dpi;
1896 else if (fontDef.pixelSize == -1)
1897 fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.);
1898
1899 m_faceId.variableAxes = request.variableAxisValues;
1900
1901#if QT_CONFIG(directwrite3)
1902 IDWriteFontFace3 *face3 = nullptr;
1903 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace3),
1904 reinterpret_cast<void **>(&face3)))) {
1905
1906 fontDef.weight = QWindowsDirectWriteFontDatabase::fromDirectWriteWeight(face3->GetWeight());
1907 fontDef.style = QWindowsDirectWriteFontDatabase::fromDirectWriteStyle(face3->GetStyle());
1908
1909 IDWriteLocalizedStrings *names;
1910 if (SUCCEEDED(face3->GetFaceNames(&names))) {
1911 wchar_t englishLocale[] = L"en-us";
1912 fontDef.styleName = QWindowsDirectWriteFontDatabase::localeString(names, englishLocale);
1913 names->Release();
1914 }
1915
1916 // Color font
1917 if (face3->IsColorFont())
1918 glyphFormat = QFontEngine::Format_ARGB;
1919
1920 face3->Release();
1921 }
1922#endif
1923}
1924
1925QString QWindowsFontEngineDirectWrite::fontNameSubstitute(const QString &familyName)
1926{
1927 const QString substitute =
1928 QWinRegistryKey(HKEY_LOCAL_MACHINE,
1929 LR"(Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes)")
1930 .stringValue(familyName);
1931 return substitute.isEmpty() ? familyName : substitute;
1932}
1933
1934QRect QWindowsFontEngineDirectWrite::alphaTextureBounds(glyph_t glyph,
1935 const DWRITE_MATRIX &transform)
1936{
1937 UINT16 glyphIndex = glyph;
1938 FLOAT glyphAdvance = 0;
1939
1940 DWRITE_GLYPH_OFFSET glyphOffset;
1941 glyphOffset.advanceOffset = 0;
1942 glyphOffset.ascenderOffset = 0;
1943
1944 DWRITE_GLYPH_RUN glyphRun;
1945 glyphRun.fontFace = m_directWriteFontFace;
1946 glyphRun.fontEmSize = fontDef.pixelSize;
1947 glyphRun.glyphCount = 1;
1948 glyphRun.glyphIndices = &glyphIndex;
1949 glyphRun.glyphAdvances = &glyphAdvance;
1950 glyphRun.isSideways = false;
1951 glyphRun.bidiLevel = 0;
1952 glyphRun.glyphOffsets = &glyphOffset;
1953
1954 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1955 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1956 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1957 ? DWRITE_GRID_FIT_MODE_DISABLED
1958 : DWRITE_GRID_FIT_MODE_DEFAULT;
1959
1960 ComPtr<IDWriteFactory2> factory2 = nullptr;
1961 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1962 &factory2);
1963
1964 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1965 if (SUCCEEDED(hr)) {
1966 hr = factory2->CreateGlyphRunAnalysis(
1967 &glyphRun,
1968 &transform,
1969 renderMode,
1970 measureMode,
1971 gridFitMode,
1972 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1973 0.0, 0.0,
1974 &glyphAnalysis
1975 );
1976 } else {
1977 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1978 &glyphRun,
1979 1.0f,
1980 &transform,
1981 renderMode,
1982 measureMode,
1983 0.0, 0.0,
1984 &glyphAnalysis
1985 );
1986 }
1987
1988 if (SUCCEEDED(hr)) {
1989 RECT rect;
1990 hr = glyphAnalysis->GetAlphaTextureBounds(renderMode == DWRITE_RENDERING_MODE_ALIASED
1991 ? DWRITE_TEXTURE_ALIASED_1x1
1992 : DWRITE_TEXTURE_CLEARTYPE_3x1,
1993 &rect);
1994 if (FAILED(hr) || rect.left == rect.right || rect.top == rect.bottom)
1995 return QRect{};
1996
1997 return QRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
1998 } else {
1999 return QRect{};
2000 }
2001}
2002
2003QRect QWindowsFontEngineDirectWrite::colorBitmapBounds(glyph_t glyph, const DWRITE_MATRIX &transform)
2004{
2005#if QT_CONFIG(directwrite3)
2006 ComPtr<IDWriteFontFace4> face4;
2007 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
2008 &face4))) {
2009 DWRITE_GLYPH_IMAGE_FORMATS formats = face4->GetGlyphImageFormats();
2010
2011 const DWRITE_GLYPH_IMAGE_FORMATS supportedBitmapFormats =
2012 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_PNG
2013 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
2014 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF);
2015
2016 if (formats & supportedBitmapFormats) {
2017 DWRITE_GLYPH_IMAGE_DATA data;
2018 void *ctx;
2019 HRESULT hr = face4->GetGlyphImageData(glyph,
2020 fontDef.pixelSize,
2021 DWRITE_GLYPH_IMAGE_FORMATS(formats & supportedBitmapFormats),
2022 &data,
2023 &ctx);
2024 if (FAILED(hr)) {
2025 qErrnoWarning("%s: GetGlyphImageData failed", __FUNCTION__);
2026 return QRect{};
2027 }
2028
2029 auto fnc = qScopeGuard([&]() {
2030 if (data.imageData != nullptr)
2031 face4->ReleaseGlyphImageData(ctx);
2032 });
2033
2034 if (data.pixelsPerEm == 0 || data.imageData == nullptr || data.imageDataSize == 0)
2035 return QRect{};
2036
2037 QRect rect(-data.horizontalLeftOrigin.x,
2038 -data.horizontalLeftOrigin.y,
2039 data.pixelSize.width,
2040 data.pixelSize.height);
2041
2042 QTransform matrix(transform.m11, transform.m12,
2043 transform.m21, transform.m22,
2044 transform.dx, transform.dy);
2045
2046 // GetGlyphImageData returns the closest matching size, which we need to scale down
2047 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
2048 matrix.scale(scale, scale);
2049 rect = matrix.mapRect(rect);
2050
2051 return rect;
2052 }
2053 }
2054
2055 return QRect{};
2056#else
2057 Q_UNUSED(glyph);
2058 Q_UNUSED(transform);
2059 return QRect{};
2060#endif // QT_CONFIG(directwrite3)
2061}
2062
2063glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph,
2064 const QFixedPoint &subPixelPosition,
2065 const QTransform &originalTransform,
2066 GlyphFormat format)
2067{
2068 QTransform matrix = originalTransform;
2069 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
2070 matrix.scale(fontDef.stretch / 100.0, 1.0);
2071
2072 glyph_metrics_t bbox = QFontEngine::boundingBox(glyph, matrix); // To get transformed advance
2073
2074 DWRITE_MATRIX transform;
2075 transform.dx = subPixelPosition.x.toReal();
2076 transform.dy = 0;
2077 transform.m11 = matrix.m11();
2078 transform.m12 = matrix.m12();
2079 transform.m21 = matrix.m21();
2080 transform.m22 = matrix.m22();
2081
2082 // Try COLRv1 approach first
2083 QRect rect = paintGraphBounds(glyph, transform);
2084
2085 // Then try general approach (works with regular truetype glyphs as well as COLRv0)
2086 if (rect.isEmpty())
2087 rect = alphaTextureBounds(glyph, transform);
2088
2089 // If this fails, we check if it is an embedded color bitmap
2090 if (rect.isEmpty())
2091 rect = colorBitmapBounds(glyph, transform);
2092
2093 // If we are unable to find metrics, just return the design metrics
2094 if (rect.isEmpty())
2095 return bbox;
2096
2097 int margin = glyphMargin(format);
2098 return glyph_metrics_t(rect.left(),
2099 rect.top(),
2100 rect.width() + margin * 2,
2101 rect.height() + margin * 2,
2102 bbox.xoff, bbox.yoff);
2103}
2104
2105QImage QWindowsFontEngineDirectWrite::bitmapForGlyph(glyph_t glyph,
2106 const QFixedPoint &subPixelPosition,
2107 const QTransform &t,
2108 const QColor &color)
2109{
2110 return imageForGlyph(glyph, subPixelPosition, glyphMargin(QFontEngine::Format_ARGB), t, color);
2111}
2112
2113QList<QFontVariableAxis> QWindowsFontEngineDirectWrite::variableAxes() const
2114{
2115 return m_variableAxes;
2116}
2117
2118bool QWindowsFontEngineDirectWrite::expectsGammaCorrectedBlending(QFontEngine::GlyphFormat format) const
2119{
2120 return format == QFontEngine::Format_A32;
2121}
2122
2123QT_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)