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