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>
19#include <private/qcolrpaintgraphrenderer_p.h>
21#if QT_CONFIG(directwrite3)
22# include "qwindowsdirectwritefontdatabase_p.h"
31QT_WARNING_DISABLE_CLANG(
"-Wmicrosoft-exception-spec")
34#define DESIGN_TO_LOGICAL(DESIGN_UNIT_VALUE)
35 QFixed::fromReal((qreal(DESIGN_UNIT_VALUE) / qreal(m_unitsPerEm)) * fontDef.pixelSize)
39 class GeometrySink:
public IDWriteGeometrySink
41 Q_DISABLE_COPY_MOVE(GeometrySink)
43 GeometrySink(QPainterPath *path)
44 : m_refCount(0), m_path(path)
46 Q_ASSERT(m_path != 0);
48 virtual ~GeometrySink() =
default;
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;
58 IFACEMETHOD_(
unsigned long, AddRef)()
override;
59 IFACEMETHOD_(
unsigned long, Release)()
override;
60 IFACEMETHOD(QueryInterface)(IID
const &riid,
void **ppvObject)
override;
63 inline static QPointF fromD2D1_POINT_2F(
const D2D1_POINT_2F &inp)
65 return QPointF(inp.x, inp.y);
68 unsigned long m_refCount;
73 void GeometrySink::AddBeziers(
const D2D1_BEZIER_SEGMENT *beziers,
74 UINT bezierCount)
noexcept
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);
81 m_path->cubicTo(c1, c2, p2);
85 void GeometrySink::AddLines(
const D2D1_POINT_2F *points, UINT pointsCount)
noexcept
87 for (uint i=0; i<pointsCount; ++i)
88 m_path->lineTo(fromD2D1_POINT_2F(points[i]));
91 void GeometrySink::BeginFigure(D2D1_POINT_2F startPoint,
92 D2D1_FIGURE_BEGIN )
noexcept
94 m_startPoint = fromD2D1_POINT_2F(startPoint);
95 m_path->moveTo(m_startPoint);
98 IFACEMETHODIMP GeometrySink::Close()
noexcept
103 void GeometrySink::EndFigure(D2D1_FIGURE_END figureEnd)
noexcept
105 if (figureEnd == D2D1_FIGURE_END_CLOSED)
106 m_path->closeSubpath();
109 void GeometrySink::SetFillMode(D2D1_FILL_MODE fillMode)
noexcept
111 m_path->setFillRule(fillMode == D2D1_FILL_MODE_ALTERNATE
116 void GeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT )
noexcept
121 IFACEMETHODIMP_(
unsigned long) GeometrySink::AddRef()
noexcept
123 return InterlockedIncrement(&m_refCount);
126 IFACEMETHODIMP_(
unsigned long) GeometrySink::Release()
noexcept
128 unsigned long newCount = InterlockedDecrement(&m_refCount);
138 IFACEMETHODIMP GeometrySink::QueryInterface(IID
const &riid,
void **ppvObject)
noexcept
140 if (__uuidof(IDWriteGeometrySink) == riid) {
142 }
else if (__uuidof(IUnknown) == riid) {
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;
163 return DWRITE_MEASURING_MODE_NATURAL;
169 QFont::HintingPreference hintingPreference = QFont::HintingPreference(fontDef.hintingPreference);
170 if (hintingPreference == QFont::PreferDefaultHinting) {
171 if (!qFuzzyCompare(qApp->devicePixelRatio(), 1.0)) {
174 hintingPreference = fontDef.pixelSize > 16.0
175 ? QFont::PreferNoHinting
176 : QFont::PreferVerticalHinting;
178 hintingPreference = QFont::PreferFullHinting;
182 return hintingPreference;
185DWRITE_RENDERING_MODE QWindowsFontEngineDirectWrite::hintingPreferenceToRenderingMode(
const QFontDef &fontDef)
const
187 if ((fontDef.styleStrategy & QFont::NoAntialias) && glyphFormat != QFontEngine::Format_ARGB)
188 return DWRITE_RENDERING_MODE_ALIASED;
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;
197 return fontDef.pixelSize > 16.0
198 ? DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC
199 : DWRITE_RENDERING_MODE_GDI_CLASSIC;
204
205
206
207
208
209
210
211
212
213
215QWindowsFontEngineDirectWrite::QWindowsFontEngineDirectWrite(IDWriteFontFace *directWriteFontFace,
217 const QSharedPointer<QWindowsFontEngineData> &d)
218 : QFontEngine(DirectWrite)
219 , m_fontEngineData(d)
220 , m_directWriteFontFace(directWriteFontFace)
221 , m_directWriteBitmapRenderTarget(0)
222 , m_lineThickness(-1)
227 qCDebug(lcQpaFonts) <<
__FUNCTION__ << pixelSize;
229 Q_ASSERT(m_directWriteFontFace);
231 m_fontEngineData->directWriteFactory->AddRef();
232 m_directWriteFontFace->AddRef();
234 IDWriteRenderingParams *renderingParams =
nullptr;
235 if (SUCCEEDED(m_fontEngineData->directWriteFactory->CreateRenderingParams(&renderingParams))) {
236 m_pixelGeometry = renderingParams->GetPixelGeometry();
237 renderingParams->Release();
240 fontDef.pixelSize = pixelSize;
242 cache_cost = m_xHeight.toInt() * m_xHeight.toInt() * 2000;
245QWindowsFontEngineDirectWrite::~QWindowsFontEngineDirectWrite()
247 qCDebug(lcQpaFonts) <<
__FUNCTION__;
249 m_fontEngineData->directWriteFactory->Release();
250 m_directWriteFontFace->Release();
252 if (m_directWriteBitmapRenderTarget != 0)
253 m_directWriteBitmapRenderTarget->Release();
255 if (!m_uniqueFamilyName.isEmpty()) {
256 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
257 static_cast<QWindowsFontDatabase *>(pfdb)->derefUniqueFont(m_uniqueFamilyName);
266 return __uuidof(IDWriteLocalFontFileLoader);
269DECLARE_INTERFACE_(QIdWriteLocalFontFileLoader, IDWriteFontFileLoader)
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;
276static UUID uuidIdWriteLocalFontFileLoader()
278 static const UUID result = { 0xb2d9f3ec, 0xc9fe, 0x4a11, {0xa2, 0xec, 0xd8, 0x62, 0x8, 0xf7, 0xc0, 0xa2}};
283QString QWindowsFontEngineDirectWrite::filenameFromFontFile(IDWriteFontFile *fontFile)
285 IDWriteFontFileLoader *loader =
nullptr;
287 HRESULT hr = fontFile->GetLoader(&loader);
289 qErrnoWarning(
"%s: GetLoader failed",
__FUNCTION__);
293 QIdWriteLocalFontFileLoader *localLoader =
nullptr;
294 hr = loader->QueryInterface(uuidIdWriteLocalFontFileLoader(),
295 reinterpret_cast<
void **>(&localLoader));
297 const void *fontFileReferenceKey =
nullptr;
298 UINT32 fontFileReferenceKeySize = 0;
300 hr = fontFile->GetReferenceKey(&fontFileReferenceKey,
301 &fontFileReferenceKeySize);
303 qErrnoWarning(hr,
"%s: GetReferenceKey failed",
__FUNCTION__);
306 UINT32 filePathLength = 0;
308 hr = localLoader->GetFilePathLengthFromKey(fontFileReferenceKey,
309 fontFileReferenceKeySize,
312 qErrnoWarning(hr,
"GetFilePathLength failed",
__FUNCTION__);
316 if (SUCCEEDED(hr) && filePathLength > 0) {
317 QVarLengthArray<
wchar_t> filePath(filePathLength + 1);
319 hr = localLoader->GetFilePathFromKey(fontFileReferenceKey,
320 fontFileReferenceKeySize,
324 qErrnoWarning(hr,
"%s: GetFilePathFromKey failed",
__FUNCTION__);
326 ret = QString::fromWCharArray(filePath.data());
329 if (localLoader !=
nullptr)
330 localLoader->Release();
332 if (loader !=
nullptr)
337HFONT QWindowsFontEngineDirectWrite::createHFONT()
const
339 if (m_fontEngineData ==
nullptr || m_directWriteFontFace ==
nullptr)
343 HRESULT hr = m_fontEngineData->directWriteGdiInterop->ConvertFontFaceToLOGFONT(m_directWriteFontFace,
346 lf.lfHeight = -qRound(fontDef.pixelSize);
347 return CreateFontIndirect(&lf);
353void QWindowsFontEngineDirectWrite::initializeHeightMetrics()
const
355 DWRITE_FONT_METRICS metrics;
356 m_directWriteFontFace->GetMetrics(&metrics);
362 QFontEngine::initializeHeightMetrics();
365void QWindowsFontEngineDirectWrite::collectMetrics()
367 DWRITE_FONT_METRICS metrics;
369 m_directWriteFontFace->GetMetrics(&metrics);
370 m_unitsPerEm = metrics.designUnitsPerEm;
374 if (m_unitsPerEm == 0) {
375 qCWarning(lcQpaFonts) <<
"Font" << fontDef.families <<
"reports an em square size of 0."
376 <<
"Clamping to minimum value.";
385 IDWriteFontFile *fontFile =
nullptr;
386 UINT32 numberOfFiles = 1;
387 if (SUCCEEDED(m_directWriteFontFace->GetFiles(&numberOfFiles, &fontFile))) {
388 m_faceId.filename = QFile::encodeName(filenameFromFontFile(fontFile));
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);
399 loadKerningPairs(emSquareSize() / QFixed::fromReal(fontDef.pixelSize));
401#if QT_CONFIG(directwrite3)
402 IDWriteFontFace5 *face5;
403 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace5),
404 reinterpret_cast<
void **>(&face5)))) {
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);
413 QVarLengthArray<DWRITE_FONT_AXIS_RANGE, 8> axisRanges(fontAxisCount);
415 hres = fontResource->GetFontAxisRanges(axisRanges.data(), fontAxisCount);
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);
422 if (range.minValue < range.maxValue) {
423 QFontVariableAxis axis;
424 if (
auto maybeTag = QFont::Tag::fromValue(qToBigEndian<UINT32>(value.axisTag))) {
425 axis.setTag(*maybeTag);
427 qWarning() <<
"QWindowsFontEngineDirectWrite::collectMetrics: Invalid tag" << value.axisTag;
430 axis.setDefaultValue(value.value);
431 axis.setMaximumValue(range.maxValue);
432 axis.setMinimumValue(range.minValue);
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;
439 QString name = hasDefaultLocale
440 ? QWindowsDirectWriteFontDatabase::localeString(names, defaultLocale)
442 if (name.isEmpty()) {
443 wchar_t englishLocale[] = L"en-us";
444 name = QWindowsDirectWriteFontDatabase::localeString(names, englishLocale);
451 m_variableAxes.append(axis);
456 fontResource->Release();
463QFixed QWindowsFontEngineDirectWrite::underlinePosition()
const
465 if (m_underlinePosition > 0)
466 return m_underlinePosition;
468 return QFontEngine::underlinePosition();
471QFixed QWindowsFontEngineDirectWrite::lineThickness()
const
473 if (m_lineThickness > 0)
474 return m_lineThickness;
476 return QFontEngine::lineThickness();
479bool QWindowsFontEngineDirectWrite::getSfntTableData(uint tag, uchar *buffer, uint *length)
const
483 const void *tableData = 0;
485 void *tableContext = 0;
487 HRESULT hr = m_directWriteFontFace->TryGetFontTable(qbswap<quint32>(tag),
488 &tableData, &tableSize,
489 &tableContext, &exists);
493 if (buffer && *length >= tableSize)
494 memcpy(buffer, tableData, tableSize);
496 Q_ASSERT(
int(*length) > 0);
498 m_directWriteFontFace->ReleaseFontTable(tableContext);
500 qErrnoWarning(
"%s: TryGetFontTable failed",
__FUNCTION__);
506QFixed QWindowsFontEngineDirectWrite::emSquareSize()
const
511glyph_t QWindowsFontEngineDirectWrite::glyphIndex(uint ucs4)
const
515 HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(&ucs4, 1, &glyphIndex);
517 qErrnoWarning(
"%s: glyphIndex failed",
__FUNCTION__);
524int QWindowsFontEngineDirectWrite::stringToCMap(
const QChar *str,
int len, QGlyphLayout *glyphs,
525 int *nglyphs, QFontEngine::ShaperFlags flags)
const
527 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
528 if (*nglyphs < len) {
533 QVarLengthArray<UINT32> codePoints(len);
534 int actualLength = 0;
535 QStringIterator it(str, str + len);
537 codePoints[actualLength++] = it.next();
539 QVarLengthArray<UINT16> glyphIndices(actualLength);
540 HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(codePoints.data(), actualLength,
541 glyphIndices.data());
543 qErrnoWarning(
"%s: GetGlyphIndicesW failed",
__FUNCTION__);
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)))
554 *nglyphs = actualLength;
555 glyphs->numGlyphs = actualLength;
557 if (!(flags & GlyphIndicesOnly))
558 recalcAdvances(glyphs, {});
563QFontEngine::FaceId QWindowsFontEngineDirectWrite::faceId()
const
568void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags shaperFlags)
const
570 QVarLengthArray<UINT16> glyphIndices(glyphs->numGlyphs);
573 for(
int i=0; i<glyphs->numGlyphs; i++)
574 glyphIndices[i] = UINT16(glyphs->glyphs[i]);
576 QVarLengthArray<DWRITE_GLYPH_METRICS> glyphMetrics(glyphIndices.size());
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;
586 hr = m_directWriteFontFace->GetGdiCompatibleGlyphMetrics(
float(fontDef.pixelSize),
592 glyphMetrics.data());
594 hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(),
596 glyphMetrics.data());
599 qreal stretch = fontDef.stretch != QFont::AnyStretch ? fontDef.stretch / 100.0 : 1.0;
600 for (
int i = 0; i < glyphs->numGlyphs; ++i)
603 qErrnoWarning(
"%s: GetDesignGlyphMetrics failed",
__FUNCTION__);
607QPainterPath QWindowsFontEngineDirectWrite::unscaledGlyph(glyph_t glyph)
const
609 float advance = 0.0f;
611 DWRITE_GLYPH_OFFSET offset;
612 offset.advanceOffset = 0;
613 offset.ascenderOffset = 0;
616 GeometrySink geometrySink(&ret);
617 HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(m_unitsPerEm,
626 qErrnoWarning(
"%s: GetGlyphRunOutline failed",
__FUNCTION__);
631void QWindowsFontEngineDirectWrite::getUnscaledGlyph(glyph_t glyph,
633 glyph_metrics_t *metric)
635 *path = unscaledGlyph(glyph);
638 DWRITE_GLYPH_METRICS glyphMetrics;
639 HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&g, 1, &glyphMetrics);
641 qErrnoWarning(
"%s: GetDesignGlyphMetrics failed",
__FUNCTION__);
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,
662void QWindowsFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions,
int nglyphs,
663 QPainterPath *path, QTextItem::RenderFlags flags)
666 QVarLengthArray<UINT16> glyphIndices(nglyphs);
667 QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(nglyphs);
668 QVarLengthArray<FLOAT> glyphAdvances(nglyphs);
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;
677 GeometrySink geometrySink(path);
678 HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(
681 glyphAdvances.data(),
690 qErrnoWarning(
"%s: GetGlyphRunOutline failed",
__FUNCTION__);
693glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(
const QGlyphLayout &glyphs)
695 if (glyphs.numGlyphs == 0)
696 return glyph_metrics_t();
698 for (
int i = 0; i < glyphs.numGlyphs; ++i)
699 w += glyphs.effectiveAdvance(i);
701 const QFixed leftBearing = firstLeftBearing(glyphs);
702 return glyph_metrics_t(leftBearing, -ascent(), w - leftBearing - lastRightBearing(glyphs),
703 ascent() + descent(), w, 0);
706glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(glyph_t g)
708 UINT16 glyphIndex = g;
710 DWRITE_GLYPH_METRICS glyphMetrics;
711 HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics);
720 QFixed width = advanceWidth - leftSideBearing - rightSideBearing;
721 QFixed height = advanceHeight - topSideBearing - bottomSideBearing;
722 return glyph_metrics_t(leftSideBearing,
723 -verticalOriginY + topSideBearing,
729 qErrnoWarning(
"%s: GetDesignGlyphMetrics failed",
__FUNCTION__);
732 return glyph_metrics_t();
735QFixed QWindowsFontEngineDirectWrite::capHeight()
const
737 if (m_capHeight <= 0)
738 return calculatedCapHeight();
743QFixed QWindowsFontEngineDirectWrite::xHeight()
const
748qreal QWindowsFontEngineDirectWrite::maxCharWidth()
const
750 return m_maxAdvanceWidth.toReal();
753QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
754 const QFixedPoint &subPixelPosition,
757 QImage im = imageForGlyph(glyph, subPixelPosition, glyphMargin(Format_A8), t);
760 QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
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.);
774 return QFontEngine::alphaMapForGlyph(glyph, t);
778QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
779 const QFixedPoint &subPixelPosition)
781 return alphaMapForGlyph(glyph, subPixelPosition, QTransform());
784bool QWindowsFontEngineDirectWrite::supportsHorizontalSubPixelPositions()
const
786 QFont::HintingPreference hinting = determineHinting(fontDef);
787 return (hinting != QFont::PreferFullHinting && !(fontDef.styleStrategy & QFont::NoAntialias));
790QFontEngine::Properties QWindowsFontEngineDirectWrite::properties()
const
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);
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;
810 directWriteFontFace2->Release();
813 return QFontEngine::properties();
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,
824 QRect boundingRect)
const
826 ComPtr<IDWriteFactory2> factory2;
827 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
832 ComPtr<IDWriteGlyphRunAnalysis> colorGlyphsAnalysis;
833 hr = factory2->CreateGlyphRunAnalysis(
834 &colorGlyphRun->glyphRun,
839 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
845 qErrnoWarning(hr,
"%s: CreateGlyphRunAnalysis failed for color run",
__FUNCTION__);
850 if (colorGlyphRun->paletteIndex == 0xFFFF) {
851 r =
float(color.redF());
852 g =
float(color.greenF());
853 b =
float(color.blueF());
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);
862 if (!qFuzzyIsNull(a))
863 renderGlyphRun(image, r, g, b, a, colorGlyphsAnalysis.Get(), boundingRect, renderMode);
868#if QT_CONFIG(directwritecolrv1)
869static inline QTransform matrixToTransform(
const DWRITE_MATRIX &matrix,
872 return QTransform(matrix.m11, matrix.m12,
873 matrix.m21, matrix.m22,
874 matrix.dx * unitsPerEm, matrix.dy * unitsPerEm);
877static inline QColor colorToColor(
const DWRITE_COLOR_F &color,
float alphaMultiplier = 1.0)
879 return QColor::fromRgbF(color.r, color.g, color.b, color.a * alphaMultiplier);
882static inline QGradient::Spread extendToSpread(UINT32 extendMode)
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;
891static inline QPainter::CompositionMode compositeToCompositionMode(DWRITE_COLOR_COMPOSITE_MODE mode)
894 case DWRITE_COLOR_COMPOSITE_CLEAR:
895 return QPainter::CompositionMode_Clear;
897 case DWRITE_COLOR_COMPOSITE_SRC:
898 return QPainter::CompositionMode_Source;
900 case DWRITE_COLOR_COMPOSITE_DEST:
901 return QPainter::CompositionMode_Destination;
903 case DWRITE_COLOR_COMPOSITE_SRC_OVER:
904 return QPainter::CompositionMode_SourceOver;
906 case DWRITE_COLOR_COMPOSITE_DEST_OVER:
907 return QPainter::CompositionMode_DestinationOver;
909 case DWRITE_COLOR_COMPOSITE_SRC_IN:
910 return QPainter::CompositionMode_SourceIn;
912 case DWRITE_COLOR_COMPOSITE_DEST_IN:
913 return QPainter::CompositionMode_DestinationIn;
915 case DWRITE_COLOR_COMPOSITE_SRC_OUT:
916 return QPainter::CompositionMode_SourceOut;
918 case DWRITE_COLOR_COMPOSITE_DEST_OUT:
919 return QPainter::CompositionMode_DestinationOut;
921 case DWRITE_COLOR_COMPOSITE_SRC_ATOP:
922 return QPainter::CompositionMode_SourceAtop;
924 case DWRITE_COLOR_COMPOSITE_DEST_ATOP:
925 return QPainter::CompositionMode_DestinationAtop;
927 case DWRITE_COLOR_COMPOSITE_XOR:
928 return QPainter::CompositionMode_Xor;
930 case DWRITE_COLOR_COMPOSITE_PLUS:
931 return QPainter::CompositionMode_Plus;
933 case DWRITE_COLOR_COMPOSITE_SCREEN:
934 return QPainter::CompositionMode_Screen;
936 case DWRITE_COLOR_COMPOSITE_OVERLAY:
937 return QPainter::CompositionMode_Overlay;
939 case DWRITE_COLOR_COMPOSITE_DARKEN:
940 return QPainter::CompositionMode_Darken;
942 case DWRITE_COLOR_COMPOSITE_LIGHTEN:
943 return QPainter::CompositionMode_Lighten;
945 case DWRITE_COLOR_COMPOSITE_COLOR_DODGE:
946 return QPainter::CompositionMode_ColorDodge;
948 case DWRITE_COLOR_COMPOSITE_COLOR_BURN:
949 return QPainter::CompositionMode_ColorBurn;
951 case DWRITE_COLOR_COMPOSITE_HARD_LIGHT:
952 return QPainter::CompositionMode_HardLight;
954 case DWRITE_COLOR_COMPOSITE_SOFT_LIGHT:
955 return QPainter::CompositionMode_SoftLight;
957 case DWRITE_COLOR_COMPOSITE_DIFFERENCE:
958 return QPainter::CompositionMode_Difference;
960 case DWRITE_COLOR_COMPOSITE_EXCLUSION:
961 return QPainter::CompositionMode_Exclusion;
963 case DWRITE_COLOR_COMPOSITE_MULTIPLY:
964 return QPainter::CompositionMode_Multiply;
967 qCWarning(lcColrv1) <<
"Unhandled color composite mode:" << mode;
968 return QPainter::CompositionMode_SourceOver;
973QRect QWindowsFontEngineDirectWrite::paintGraphBounds(glyph_t glyph,
974 const DWRITE_MATRIX &matrix)
const
976#if QT_CONFIG(directwritecolrv1)
977 ComPtr<IDWriteFontFace7> face7;
978 HRESULT hr = m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace7),
981 DWRITE_PAINT_FEATURE_LEVEL featureLevel = face7->GetPaintFeatureLevel(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE);
982 if (featureLevel != DWRITE_PAINT_FEATURE_LEVEL_COLR_V1)
985 ComPtr<IDWritePaintReader> paintReader;
986 hr = face7->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
990 qErrnoWarning(hr,
"%s: CreatePaintReader failed",
__FUNCTION__);
994 DWRITE_PAINT_ELEMENT paintElement;
996 hr = paintReader->SetCurrentGlyph(glyph,
998 sizeof(paintElement),
1003 qErrnoWarning(hr,
"%s: SetCurrentGlyph failed",
__FUNCTION__);
1007 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE)
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();
1019 QTransform initialTransform;
1020 initialTransform.scale(fontDef.pixelSize, fontDef.pixelSize);
1021 boundingRect = initialTransform.mapRect(boundingRect);
1023 QTransform originalXform = matrixToTransform(matrix);
1024 boundingRect = originalXform.mapRect(boundingRect);
1026 return boundingRect.toAlignedRect();
1028 qCDebug(lcColrv1) <<
"Font face does not support IDWriteFontFace7 interface";
1038#if QT_CONFIG(directwritecolrv1)
1039bool QWindowsFontEngineDirectWrite::traverseColr1(IDWritePaintReader *paintReader,
1040 IDWriteFontFace7 *face7,
1041 const DWRITE_PAINT_ELEMENT *paintElement,
1042 QColrPaintGraphRenderer *paintGraphRenderer)
const
1044 paintGraphRenderer->save();
1045 auto cleanup = qScopeGuard([&paintGraphRenderer]() {
1046 paintGraphRenderer->restore();
1049 auto traverseChildren = [&](quint32 childCount) {
1050 DWRITE_PAINT_ELEMENT childPaintElement;
1051 if (FAILED(paintReader->MoveToFirstChild(&childPaintElement,
sizeof(DWRITE_PAINT_ELEMENT))))
1054 while (childCount-- > 0) {
1055 traverseColr1(paintReader, face7, &childPaintElement, paintGraphRenderer);
1056 if (childCount > 0) {
1057 if (FAILED(paintReader->MoveToNextSibling(&childPaintElement,
sizeof(DWRITE_PAINT_ELEMENT)))) {
1063 return SUCCEEDED(paintReader->MoveToParent());
1066 auto collectStops = [&paintReader](
int stopCount) {
1069 QVarLengthArray<D2D1_GRADIENT_STOP> stops(stopCount);
1070 HRESULT hr = paintReader->GetGradientStops(0, stopCount, stops.data());
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));
1083 switch (paintElement->paintType) {
1084 case DWRITE_PAINT_TYPE_LAYERS:
1086 if (!traverseChildren(paintElement->paint.layers.childCount))
1092 case DWRITE_PAINT_TYPE_SOLID_GLYPH:
1094 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.solidGlyph.glyphIndex);
1096 QColor color = colorToColor(paintElement->paint.solidGlyph.color.value);
1097 paintGraphRenderer->setPath(glyphPath);
1098 paintGraphRenderer->setSolidColor(color);
1099 paintGraphRenderer->drawCurrentPath();
1104 case DWRITE_PAINT_TYPE_SOLID:
1106 QColor color = colorToColor(paintElement->paint.solid.value,
1107 paintElement->paint.solid.alphaMultiplier);
1108 paintGraphRenderer->setSolidColor(color);
1109 paintGraphRenderer->drawCurrentPath();
1113 case DWRITE_PAINT_TYPE_COMPOSITE:
1115 if (!paintGraphRenderer->isRendering()) {
1116 traverseChildren(2);
1118 DWRITE_PAINT_ELEMENT childElement;
1120 HRESULT hr = paintReader->MoveToFirstChild(&childElement,
sizeof(DWRITE_PAINT_ELEMENT));
1122 qErrnoWarning(hr,
"%s: Cannot move to first child of composite node",
1128 hr = paintReader->MoveToNextSibling(&childElement,
sizeof(DWRITE_PAINT_ELEMENT));
1130 qErrnoWarning(hr,
"%s: Cannot move to second child of composite node",
1135 DWRITE_COLOR_COMPOSITE_MODE compositeMode = paintElement->paint.composite.mode;
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))
1145 hr = paintReader->MoveToParent();
1147 qErrnoWarning(hr,
"%s: Cannot move back to parent composite node",
1152 hr = paintReader->MoveToFirstChild(&childElement,
sizeof(DWRITE_PAINT_ELEMENT));
1154 qErrnoWarning(hr,
"%s: Cannot move to first child of composite node",
1159 compositeRenderer.setCompositionMode(compositeToCompositionMode(compositeMode));
1160 if (!traverseColr1(paintReader, face7, &childElement, &compositeRenderer))
1163 hr = paintReader->MoveToParent();
1165 qErrnoWarning(hr,
"%s: Cannot move back to parent composite node",
1170 paintGraphRenderer->drawImage(compositeRenderer.endRender());
1176 case DWRITE_PAINT_TYPE_RADIAL_GRADIENT:
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;
1185 const QGradient::Spread spread
1186 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1188 const QGradientStops gradientStops
1189 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1191 paintGraphRenderer->setRadialGradient(c0, r0, c1, r1, spread, gradientStops);
1192 paintGraphRenderer->drawCurrentPath();
1196 case DWRITE_PAINT_TYPE_SWEEP_GRADIENT:
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;
1203 const QGradient::Spread spread
1204 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1205 const QGradientStops gradientStops
1206 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1208 paintGraphRenderer->setConicalGradient(center,
1213 paintGraphRenderer->drawCurrentPath();
1217 case DWRITE_PAINT_TYPE_LINEAR_GRADIENT:
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);
1226 const QGradient::Spread spread
1227 = extendToSpread(paintElement->paint.linearGradient.extendMode);
1228 const QGradientStops gradientStops
1229 = collectStops(paintElement->paint.linearGradient.gradientStopCount);
1231 paintGraphRenderer->setLinearGradient(p0, p1, p2, spread, gradientStops);
1232 paintGraphRenderer->drawCurrentPath();
1236 case DWRITE_PAINT_TYPE_GLYPH:
1238 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.glyph.glyphIndex);
1239 paintGraphRenderer->appendPath(glyphPath);
1240 if (!traverseChildren(1))
1245 case DWRITE_PAINT_TYPE_COLOR_GLYPH:
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);
1255 paintGraphRenderer->setClip(clipBox);
1258 DWRITE_PAINT_ELEMENT childElement;
1259 if (FAILED(paintReader->MoveToFirstChild(&childElement,
sizeof(DWRITE_PAINT_ELEMENT))))
1262 if (!traverseColr1(paintReader, face7, &childElement, paintGraphRenderer))
1265 if (FAILED(paintReader->MoveToParent()))
1271 case DWRITE_PAINT_TYPE_TRANSFORM:
1273 QTransform transform = matrixToTransform(paintElement->paint.transform, m_unitsPerEm);
1274 paintGraphRenderer->prependTransform(transform);
1275 if (!traverseChildren(1))
1282 qCDebug(lcColrv1) <<
"Unhandled paint graph node type" << paintElement->paintType;
1289bool QWindowsFontEngineDirectWrite::renderColr1GlyphRun(QImage *image,
1290 const DWRITE_GLYPH_RUN *glyphRun,
1291 const DWRITE_MATRIX &matrix,
1294 qCDebug(lcColrv1) <<
"renderColr1GlyphRun,"
1295 <<
"families:" << fontDef.families;
1296 ComPtr<IDWriteFontFace7> face7;
1297 HRESULT hr = m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace7),
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;
1307 ComPtr<IDWritePaintReader> paintReader;
1308 hr = face7->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
1312 qErrnoWarning(hr,
"%s: CreatePaintReader failed",
__FUNCTION__);
1316 Q_ASSERT(glyphRun->glyphCount == 1);
1317 DWRITE_PAINT_ELEMENT paintElement;
1319 hr = paintReader->SetCurrentGlyph(glyphRun->glyphIndices[0],
1321 sizeof(DWRITE_PAINT_ELEMENT),
1325 qErrnoWarning(hr,
"%s: SetCurrentGlyph failed",
__FUNCTION__);
1329 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE) {
1330 qCDebug(lcColrv1) <<
"Glyph" << glyphRun->glyphIndices[0]
1331 <<
"does not have a paint graph";
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);
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();
1351 QTransform initialTransform;
1352 initialTransform.scale(fontDef.pixelSize, fontDef.pixelSize);
1353 boundingRect = initialTransform.mapRect(boundingRect);
1355 QTransform originalXform = matrixToTransform(matrix);
1356 boundingRect = originalXform.mapRect(boundingRect);
1358 qCDebug(lcColrv1).noquote() <<
"Bounds of"
1359 << glyphRun->glyphIndices[0]
1360 <<
" in device coordinates:"
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))
1369 *image = graphRenderer.endRender();
1371 qCDebug(lcColrv1) <<
"Font face does not support IDWriteFontFace7 interface";
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,
1384 QRect boundingRect)
const
1388#if QT_CONFIG(directwritecolrv1)
1391 renderColr1GlyphRun(&ret, glyphRun, transform, color);
1394#if QT_CONFIG(directwrite3)
1398 ComPtr<IDWriteFactory4> factory4;
1399 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory4),
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);
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);
1413 ComPtr<IDWriteColorGlyphRunEnumerator1> enumerator;
1414 hr = factory4->TranslateColorGlyphRun(D2D1::Point2F(0.0f, 0.0f),
1423 while (SUCCEEDED(hr) && ok) {
1424 hr = enumerator->MoveNext(&ok);
1428 const DWRITE_COLOR_GLYPH_RUN1 *colorGlyphRun =
nullptr;
1429 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1431 qErrnoWarning(hr,
"%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed",
__FUNCTION__);
1435 if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_NONE) {
1437 }
else if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_COLR) {
1439 ret = QImage(boundingRect.width() - 1,
1440 boundingRect.height() - 1,
1441 QImage::Format_ARGB32_Premultiplied);
1445 if (!renderColr0GlyphRun(&ret,
1446 reinterpret_cast<
const DWRITE_COLOR_GLYPH_RUN *>(colorGlyphRun),
1455 }
else if (colorGlyphRun->glyphImageFormat & supportedBitmapFormats) {
1456 ComPtr<IDWriteFontFace4> face4;
1457 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
1459 DWRITE_GLYPH_IMAGE_DATA data;
1461 Q_ASSERT(glyphRun->glyphCount == 1);
1462 HRESULT hr = face4->GetGlyphImageData(glyphRun->glyphIndices[0],
1464 DWRITE_GLYPH_IMAGE_FORMATS(colorGlyphRun->glyphImageFormat & supportedBitmapFormats),
1468 qErrnoWarning(
"%s: GetGlyphImageData failed",
__FUNCTION__);
1473 switch (colorGlyphRun->glyphImageFormat) {
1474 case DWRITE_GLYPH_IMAGE_FORMATS_JPEG:
1477 case DWRITE_GLYPH_IMAGE_FORMATS_TIFF:
1485 ret = QImage::fromData(
reinterpret_cast<
const uchar *>(data.imageData),
1489 QTransform matrix(transform.m11, transform.m12,
1490 transform.m21, transform.m22,
1491 transform.dx, transform.dy);
1493 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
1494 matrix.scale(scale, scale);
1496 if (!matrix.isIdentity())
1497 ret = ret.transformed(matrix, Qt::SmoothTransformation);
1499 face4->ReleaseGlyphImageData(ctx);
1503 qCDebug(lcQpaFonts) <<
"Found glyph run with unsupported format"
1504 << colorGlyphRun->glyphImageFormat;
1513 ComPtr<IDWriteFactory2> factory2;
1514 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1519 ComPtr<IDWriteColorGlyphRunEnumerator> enumerator;
1520 hr = factory2->TranslateColorGlyphRun(0.0f,
1528 if (SUCCEEDED(hr)) {
1529 ret = QImage(boundingRect.width() - 1,
1530 boundingRect.height() - 1,
1531 QImage::Format_ARGB32_Premultiplied);
1535 while (SUCCEEDED(hr) && ok) {
1536 hr = enumerator->MoveNext(&ok);
1538 qErrnoWarning(hr,
"%s: IDWriteColorGlyphRunEnumerator::MoveNext failed",
__FUNCTION__);
1543 const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun =
nullptr;
1544 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1546 qErrnoWarning(hr,
"%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed",
__FUNCTION__);
1550 if (!renderColr0GlyphRun(&ret,
1568QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
1569 const QFixedPoint &subPixelPosition,
1571 const QTransform &originalTransform,
1572 const QColor &color)
1574 UINT16 glyphIndex = t;
1575 FLOAT glyphAdvance = 0;
1577 DWRITE_GLYPH_OFFSET glyphOffset;
1578 glyphOffset.advanceOffset = 0;
1579 glyphOffset.ascenderOffset = 0;
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;
1591 QTransform xform = originalTransform;
1592 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
1593 xform.scale(fontDef.stretch / 100.0, 1.0);
1595 DWRITE_MATRIX transform;
1596 transform.dx = subPixelPosition.x.toReal();
1598 transform.m11 = xform.m11();
1599 transform.m12 = xform.m12();
1600 transform.m21 = xform.m21();
1601 transform.m22 = xform.m22();
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;
1609 ComPtr<IDWriteFactory2> factory2;
1610 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1612 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1613 if (!SUCCEEDED(hr)) {
1614 qErrnoWarning(hr,
"%s: Failed to query IDWriteFactory2 interface.",
__FUNCTION__);
1615 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1625 hr = factory2->CreateGlyphRunAnalysis(
1631 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1637 if (SUCCEEDED(hr)) {
1638 QRect rect = paintGraphBounds(t, transform);
1640 rect = alphaTextureBounds(t, transform);
1642 rect = colorBitmapBounds(t, transform);
1644 if (rect.isEmpty()) {
1645 qCDebug(lcQpaFonts) <<
__FUNCTION__ <<
"Cannot get alpha texture bounds. Falling back to slower rendering path.";
1649 QRect boundingRect = QRect(QPoint(rect.left() - margin,
1650 rect.top() - margin),
1651 QPoint(rect.right() + margin,
1652 rect.bottom() + margin));
1655 if (glyphFormat == QFontEngine::Format_ARGB) {
1656 image = renderColorGlyph(&glyphRun,
1666 if (image.isNull()) {
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);
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());
1682 r = g = b = a = 0.0;
1685 renderGlyphRun(&image,
1690 glyphAnalysis.Get(),
1697 qErrnoWarning(hr,
"%s: CreateGlyphRunAnalysis failed",
__FUNCTION__);
1702void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
1707 IDWriteGlyphRunAnalysis *glyphAnalysis,
1708 const QRect &boundingRect,
1709 DWRITE_RENDERING_MODE renderMode)
const
1711 const int width = destination->width();
1712 const int height = destination->height();
1718 const int size = width * height * 3;
1721 rect.left = boundingRect.left();
1722 rect.top = boundingRect.top();
1723 rect.right = boundingRect.right();
1724 rect.bottom = boundingRect.bottom();
1726 QVarLengthArray<BYTE, 1024> alphaValueArray(size);
1727 BYTE *alphaValues = alphaValueArray.data();
1728 memset(alphaValues, 0, size);
1730 HRESULT hr = glyphAnalysis->CreateAlphaTexture(renderMode == DWRITE_RENDERING_MODE_ALIASED
1731 ? DWRITE_TEXTURE_ALIASED_1x1
1732 : DWRITE_TEXTURE_CLEARTYPE_3x1,
1736 if (SUCCEEDED(hr)) {
1737 if (destination->hasAlphaChannel()) {
1738 for (
int y = 0; y < height; ++y) {
1739 uint *dest =
reinterpret_cast<uint *>(destination->scanLine(y));
1740 BYTE *src = alphaValues + width * 3 * y;
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;
1748 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1749 qSwap(redAlpha, blueAlpha);
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));
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;
1763 for (
int x = 0; x < width; ++x) {
1764 int alpha = *(src++);
1765 dest[x] = (alpha << 16) + (alpha << 8) + alpha;
1769 for (
int y = 0; y < height; ++y) {
1770 uint *dest =
reinterpret_cast<uint *>(destination->scanLine(y));
1771 BYTE *src = alphaValues + width * 3 * y;
1773 for (
int x = 0; x < width; ++x) {
1774 BYTE redAlpha = *(src + 0);
1775 BYTE greenAlpha = *(src + 1);
1776 BYTE blueAlpha = *(src + 2);
1778 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1779 qSwap(redAlpha, blueAlpha);
1781 dest[x] = qRgb(redAlpha, greenAlpha, blueAlpha);
1787 qErrnoWarning(
"%s: CreateAlphaTexture failed",
__FUNCTION__);
1792QImage QWindowsFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t,
1793 const QFixedPoint &subPixelPosition,
1794 const QTransform &xform)
1796 QImage mask = imageForGlyph(t,
1798 glyphMargin(QFontEngine::Format_A32),
1801 if (mask.isNull()) {
1802 mask = QFontEngine::renderedPathForGlyph(t, Qt::white);
1803 if (!xform.isIdentity())
1804 mask = mask.transformed(xform);
1807 return mask.depth() == 32
1809 : mask.convertToFormat(QImage::Format_RGB32);
1812QFontEngine *QWindowsFontEngineDirectWrite::cloneWithSize(qreal pixelSize)
const
1814 QWindowsFontEngineDirectWrite *fontEngine =
new QWindowsFontEngineDirectWrite(m_directWriteFontFace,
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);
1829Qt::HANDLE QWindowsFontEngineDirectWrite::handle()
const
1831 return m_directWriteFontFace;
1834void QWindowsFontEngineDirectWrite::initFontInfo(
const QFontDef &request,
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.);
1844 m_faceId.variableAxes = request.variableAxisValues;
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);
1858 if (face3->IsColorFont())
1859 glyphFormat = QFontEngine::Format_ARGB;
1866QString QWindowsFontEngineDirectWrite::fontNameSubstitute(
const QString &familyName)
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;
1875QRect QWindowsFontEngineDirectWrite::alphaTextureBounds(glyph_t glyph,
1876 const DWRITE_MATRIX &transform)
1878 UINT16 glyphIndex = glyph;
1879 FLOAT glyphAdvance = 0;
1881 DWRITE_GLYPH_OFFSET glyphOffset;
1882 glyphOffset.advanceOffset = 0;
1883 glyphOffset.ascenderOffset = 0;
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;
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;
1901 ComPtr<IDWriteFactory2> factory2 =
nullptr;
1902 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1905 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1906 if (SUCCEEDED(hr)) {
1907 hr = factory2->CreateGlyphRunAnalysis(
1913 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1918 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1929 if (SUCCEEDED(hr)) {
1931 hr = glyphAnalysis->GetAlphaTextureBounds(renderMode == DWRITE_RENDERING_MODE_ALIASED
1932 ? DWRITE_TEXTURE_ALIASED_1x1
1933 : DWRITE_TEXTURE_CLEARTYPE_3x1,
1935 if (FAILED(hr) || rect.left == rect.right || rect.top == rect.bottom)
1938 return QRect(QPoint(rect.left, rect.top), QPoint(rect.right, rect.bottom));
1944QRect QWindowsFontEngineDirectWrite::colorBitmapBounds(glyph_t glyph,
const DWRITE_MATRIX &transform)
1946#if QT_CONFIG(directwrite3)
1947 ComPtr<IDWriteFontFace4> face4;
1948 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
1950 DWRITE_GLYPH_IMAGE_FORMATS formats = face4->GetGlyphImageFormats();
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);
1957 if (formats & supportedBitmapFormats) {
1958 DWRITE_GLYPH_IMAGE_DATA data;
1960 HRESULT hr = face4->GetGlyphImageData(glyph,
1962 DWRITE_GLYPH_IMAGE_FORMATS(formats & supportedBitmapFormats),
1966 qErrnoWarning(
"%s: GetGlyphImageData failed",
__FUNCTION__);
1970 QRect rect(-data.horizontalLeftOrigin.x,
1971 -data.horizontalLeftOrigin.y,
1972 data.pixelSize.width,
1973 data.pixelSize.height);
1975 QTransform matrix(transform.m11, transform.m12,
1976 transform.m21, transform.m22,
1977 transform.dx, transform.dy);
1980 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
1981 matrix.scale(scale, scale);
1983 rect = matrix.mapRect(rect);
1984 face4->ReleaseGlyphImageData(ctx);
1993 Q_UNUSED(transform);
1998glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph,
1999 const QFixedPoint &subPixelPosition,
2000 const QTransform &originalTransform,
2003 QTransform matrix = originalTransform;
2004 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
2005 matrix.scale(fontDef.stretch / 100.0, 1.0);
2007 glyph_metrics_t bbox = QFontEngine::boundingBox(glyph, matrix);
2009 DWRITE_MATRIX transform;
2010 transform.dx = subPixelPosition.x.toReal();
2012 transform.m11 = matrix.m11();
2013 transform.m12 = matrix.m12();
2014 transform.m21 = matrix.m21();
2015 transform.m22 = matrix.m22();
2018 QRect rect = paintGraphBounds(glyph, transform);
2022 rect = alphaTextureBounds(glyph, transform);
2026 rect = colorBitmapBounds(glyph, transform);
2032 int margin = glyphMargin(format);
2033 return glyph_metrics_t(rect.left(),
2035 rect.right() - rect.left() + margin * 2,
2036 rect.bottom() - rect.top() + margin * 2,
2037 bbox.xoff, bbox.yoff);
2040QImage QWindowsFontEngineDirectWrite::bitmapForGlyph(glyph_t glyph,
2041 const QFixedPoint &subPixelPosition,
2042 const QTransform &t,
2043 const QColor &color)
2045 return imageForGlyph(glyph, subPixelPosition, glyphMargin(QFontEngine::Format_ARGB), t, color);
2048QList<QFontVariableAxis> QWindowsFontEngineDirectWrite::variableAxes()
const
2050 return m_variableAxes;
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)