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