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;
185bool QWindowsFontEngineDirectWrite::useSymmetricAntialiasing()
const
187 if (m_useSymmetricAntialiasing < 0) {
188 qCDebug(lcQpaFonts) <<
"Checking antialiasing strategy for:"
193 m_useSymmetricAntialiasing = fontDef.pixelSize > 16 || !hasHinting();
195 qCDebug(lcQpaFonts) <<
" After checking pixel size and hinting, defaulting to:"
196 <<
bool(m_useSymmetricAntialiasing);
199 const QByteArray gasp = getSfntTable(QFont::Tag(
"gasp").value());
200 if (gasp.size() > 4) {
201 qCDebug(lcQpaFonts) <<
" Checking GASP table, size=" << gasp.size();
203 const uchar *start =
reinterpret_cast<
const uchar *>(gasp.constBegin());
205 const quint16 numRanges = qFromBigEndian<quint16>(start + 2);
206 if (gasp.size() >= 4 + numRanges * 4) {
207 qCDebug(lcQpaFonts) <<
" GASP ranges:" << numRanges;
208 for (
int i = 0; i < numRanges; ++i) {
209 const quint16 rangeMaxPPEM = qFromBigEndian<quint16>(start + 4 + i * 4);
210 qCDebug(lcQpaFonts) <<
" GASP range " << i <<
"upper limit == " << rangeMaxPPEM;
212 if (rangeMaxPPEM >= fontDef.pixelSize) {
213 const quint16 rangeGaspBehavior = qFromBigEndian<quint16>(start + 4 + i * 4 + 2);
214 qCDebug(lcQpaFonts) <<
" GASP behavior " << i <<
"==" << rangeGaspBehavior;
217 m_useSymmetricAntialiasing = !!(rangeGaspBehavior & 0x8);
218 qCDebug(lcQpaFonts) <<
" Symmetric antialiasing override: "
219 <<
bool(m_useSymmetricAntialiasing);
228 return m_useSymmetricAntialiasing;
231DWRITE_RENDERING_MODE QWindowsFontEngineDirectWrite::hintingPreferenceToRenderingMode(
const QFontDef &fontDef)
const
233 if ((fontDef.styleStrategy & QFont::NoAntialias) && glyphFormat != QFontEngine::Format_ARGB)
234 return DWRITE_RENDERING_MODE_ALIASED;
236 QFont::HintingPreference hintingPreference = determineHinting(fontDef);
237 switch (hintingPreference) {
238 case QFont::PreferNoHinting:
239 return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
240 case QFont::PreferVerticalHinting:
241 return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
243 if (useSymmetricAntialiasing())
244 return DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
246 return DWRITE_RENDERING_MODE_GDI_CLASSIC;
251
252
253
254
255
256
257
258
259
260
262QWindowsFontEngineDirectWrite::QWindowsFontEngineDirectWrite(IDWriteFontFace *directWriteFontFace,
264 const QSharedPointer<QWindowsFontEngineData> &d)
265 : QFontEngine(DirectWrite)
266 , m_fontEngineData(d)
267 , m_directWriteFontFace(directWriteFontFace)
268 , m_directWriteBitmapRenderTarget(0)
269 , m_lineThickness(-1)
274 qCDebug(lcQpaFonts) <<
__FUNCTION__ << pixelSize;
276 Q_ASSERT(m_directWriteFontFace);
278 m_fontEngineData->directWriteFactory->AddRef();
279 m_directWriteFontFace->AddRef();
281 IDWriteRenderingParams *renderingParams =
nullptr;
282 if (SUCCEEDED(m_fontEngineData->directWriteFactory->CreateRenderingParams(&renderingParams))) {
283 m_pixelGeometry = renderingParams->GetPixelGeometry();
284 renderingParams->Release();
287 fontDef.pixelSize = pixelSize;
289 cache_cost = m_xHeight.toInt() * m_xHeight.toInt() * 2000;
292QWindowsFontEngineDirectWrite::~QWindowsFontEngineDirectWrite()
294 qCDebug(lcQpaFonts) <<
__FUNCTION__;
296 m_fontEngineData->directWriteFactory->Release();
297 m_directWriteFontFace->Release();
299 if (m_directWriteBitmapRenderTarget != 0)
300 m_directWriteBitmapRenderTarget->Release();
302 if (!m_uniqueFamilyName.isEmpty()) {
303 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
304 static_cast<QWindowsFontDatabase *>(pfdb)->derefUniqueFont(m_uniqueFamilyName);
313 return __uuidof(IDWriteLocalFontFileLoader);
316DECLARE_INTERFACE_(QIdWriteLocalFontFileLoader, IDWriteFontFileLoader)
318 STDMETHOD(GetFilePathLengthFromKey)(THIS_
void const *, UINT32, UINT32*) PURE;
319 STDMETHOD(GetFilePathFromKey)(THIS_
void const *, UINT32, WCHAR *, UINT32) PURE;
320 STDMETHOD(GetLastWriteTimeFromKey)(THIS_
void const *, UINT32, FILETIME *) PURE;
323static UUID uuidIdWriteLocalFontFileLoader()
325 static const UUID result = { 0xb2d9f3ec, 0xc9fe, 0x4a11, {0xa2, 0xec, 0xd8, 0x62, 0x8, 0xf7, 0xc0, 0xa2}};
330QString QWindowsFontEngineDirectWrite::filenameFromFontFile(IDWriteFontFile *fontFile)
332 IDWriteFontFileLoader *loader =
nullptr;
334 HRESULT hr = fontFile->GetLoader(&loader);
336 qErrnoWarning(
"%s: GetLoader failed",
__FUNCTION__);
340 QIdWriteLocalFontFileLoader *localLoader =
nullptr;
341 hr = loader->QueryInterface(uuidIdWriteLocalFontFileLoader(),
342 reinterpret_cast<
void **>(&localLoader));
344 const void *fontFileReferenceKey =
nullptr;
345 UINT32 fontFileReferenceKeySize = 0;
347 hr = fontFile->GetReferenceKey(&fontFileReferenceKey,
348 &fontFileReferenceKeySize);
350 qErrnoWarning(hr,
"%s: GetReferenceKey failed",
__FUNCTION__);
353 UINT32 filePathLength = 0;
355 hr = localLoader->GetFilePathLengthFromKey(fontFileReferenceKey,
356 fontFileReferenceKeySize,
359 qErrnoWarning(hr,
"GetFilePathLength failed",
__FUNCTION__);
363 if (SUCCEEDED(hr) && filePathLength > 0) {
364 QVarLengthArray<
wchar_t> filePath(filePathLength + 1);
366 hr = localLoader->GetFilePathFromKey(fontFileReferenceKey,
367 fontFileReferenceKeySize,
371 qErrnoWarning(hr,
"%s: GetFilePathFromKey failed",
__FUNCTION__);
373 ret = QString::fromWCharArray(filePath.data());
376 if (localLoader !=
nullptr)
377 localLoader->Release();
379 if (loader !=
nullptr)
384HFONT QWindowsFontEngineDirectWrite::createHFONT()
const
386 if (m_fontEngineData ==
nullptr || m_directWriteFontFace ==
nullptr)
390 HRESULT hr = m_fontEngineData->directWriteGdiInterop->ConvertFontFaceToLOGFONT(m_directWriteFontFace,
393 lf.lfHeight = -qRound(fontDef.pixelSize);
394 return CreateFontIndirect(&lf);
400void QWindowsFontEngineDirectWrite::initializeHeightMetrics()
const
402 DWRITE_FONT_METRICS metrics;
403 m_directWriteFontFace->GetMetrics(&metrics);
409 QFontEngine::initializeHeightMetrics();
412void QWindowsFontEngineDirectWrite::collectMetrics()
414 DWRITE_FONT_METRICS metrics;
416 m_directWriteFontFace->GetMetrics(&metrics);
417 m_unitsPerEm = metrics.designUnitsPerEm;
421 if (m_unitsPerEm == 0) {
422 qCWarning(lcQpaFonts) <<
"Font" << fontDef.families <<
"reports an em square size of 0."
423 <<
"Clamping to minimum value.";
432 IDWriteFontFile *fontFile =
nullptr;
433 UINT32 numberOfFiles = 1;
434 if (SUCCEEDED(m_directWriteFontFace->GetFiles(&numberOfFiles, &fontFile))) {
435 m_faceId.filename = QFile::encodeName(filenameFromFontFile(fontFile));
439 QByteArray table = getSfntTable(QFont::Tag(
"hhea").value());
440 const int advanceWidthMaxLocation = 10;
441 if (table.size() >= advanceWidthMaxLocation +
int(
sizeof(quint16))) {
442 quint16 advanceWidthMax = qFromBigEndian<quint16>(table.constBegin() + advanceWidthMaxLocation);
446 loadKerningPairs(emSquareSize() / QFixed::fromReal(fontDef.pixelSize));
448#if QT_CONFIG(directwrite3)
449 IDWriteFontFace5 *face5;
450 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace5),
451 reinterpret_cast<
void **>(&face5)))) {
453 IDWriteFontResource *fontResource;
454 if (SUCCEEDED(face5->GetFontResource(&fontResource))) {
455 const UINT32 fontAxisCount = fontResource->GetFontAxisCount();
456 if (fontAxisCount > 0) {
457 QVarLengthArray<DWRITE_FONT_AXIS_VALUE, 8> axisValues(fontAxisCount);
458 HRESULT hres = fontResource->GetDefaultFontAxisValues(axisValues.data(), fontAxisCount);
460 QVarLengthArray<DWRITE_FONT_AXIS_RANGE, 8> axisRanges(fontAxisCount);
462 hres = fontResource->GetFontAxisRanges(axisRanges.data(), fontAxisCount);
464 if (SUCCEEDED(hres)) {
465 for (UINT32 i = 0; i < fontAxisCount; ++i) {
466 const DWRITE_FONT_AXIS_VALUE &value = axisValues.at(i);
467 const DWRITE_FONT_AXIS_RANGE &range = axisRanges.at(i);
469 if (range.minValue < range.maxValue) {
470 QFontVariableAxis axis;
471 if (
auto maybeTag = QFont::Tag::fromValue(qToBigEndian<UINT32>(value.axisTag))) {
472 axis.setTag(*maybeTag);
474 qWarning() <<
"QWindowsFontEngineDirectWrite::collectMetrics: Invalid tag" << value.axisTag;
477 axis.setDefaultValue(value.value);
478 axis.setMaximumValue(range.maxValue);
479 axis.setMinimumValue(range.minValue);
481 IDWriteLocalizedStrings *names;
482 if (SUCCEEDED(fontResource->GetAxisNames(i, &names))) {
483 wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
484 bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
486 QString name = hasDefaultLocale
487 ? QWindowsDirectWriteFontDatabase::localeString(names, defaultLocale)
489 if (name.isEmpty()) {
490 wchar_t englishLocale[] = L"en-us";
491 name = QWindowsDirectWriteFontDatabase::localeString(names, englishLocale);
498 m_variableAxes.append(axis);
503 fontResource->Release();
510QFixed QWindowsFontEngineDirectWrite::underlinePosition()
const
512 if (m_underlinePosition > 0)
513 return m_underlinePosition;
515 return QFontEngine::underlinePosition();
518QFixed QWindowsFontEngineDirectWrite::lineThickness()
const
520 if (m_lineThickness > 0)
521 return m_lineThickness;
523 return QFontEngine::lineThickness();
526bool QWindowsFontEngineDirectWrite::getSfntTableData(uint tag, uchar *buffer, uint *length)
const
530 const void *tableData = 0;
532 void *tableContext = 0;
534 HRESULT hr = m_directWriteFontFace->TryGetFontTable(qbswap<quint32>(tag),
535 &tableData, &tableSize,
536 &tableContext, &exists);
539 if (buffer && *length >= tableSize)
540 memcpy(buffer, tableData, tableSize);
544 m_directWriteFontFace->ReleaseFontTable(tableContext);
546 qErrnoWarning(
"%s: TryGetFontTable failed",
__FUNCTION__);
552QFixed QWindowsFontEngineDirectWrite::emSquareSize()
const
557glyph_t QWindowsFontEngineDirectWrite::glyphIndex(uint ucs4)
const
561 HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(&ucs4, 1, &glyphIndex);
563 qErrnoWarning(
"%s: glyphIndex failed",
__FUNCTION__);
570int QWindowsFontEngineDirectWrite::stringToCMap(
const QChar *str,
int len, QGlyphLayout *glyphs,
571 int *nglyphs, QFontEngine::ShaperFlags flags)
const
573 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
574 if (*nglyphs < len) {
579 QVarLengthArray<UINT32> codePoints(len);
580 int actualLength = 0;
581 QStringIterator it(str, str + len);
583 codePoints[actualLength++] = it.next();
585 QVarLengthArray<UINT16> glyphIndices(actualLength);
586 HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(codePoints.data(), actualLength,
587 glyphIndices.data());
589 qErrnoWarning(
"%s: GetGlyphIndicesW failed",
__FUNCTION__);
593 int mappedGlyphs = 0;
594 for (
int i = 0; i < actualLength; ++i) {
595 glyphs->glyphs[i] = glyphIndices.at(i);
596 if (glyphs->glyphs[i] != 0 || isIgnorableChar(codePoints.at(i)))
600 *nglyphs = actualLength;
601 glyphs->numGlyphs = actualLength;
603 if (!(flags & GlyphIndicesOnly))
604 recalcAdvances(glyphs, {});
609QFontEngine::FaceId QWindowsFontEngineDirectWrite::faceId()
const
614void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags shaperFlags)
const
616 QVarLengthArray<UINT16> glyphIndices(glyphs->numGlyphs);
619 for(
int i=0; i<glyphs->numGlyphs; i++)
620 glyphIndices[i] = UINT16(glyphs->glyphs[i]);
622 QVarLengthArray<DWRITE_GLYPH_METRICS> glyphMetrics(glyphIndices.size());
625 QFont::HintingPreference hint = determineHinting(fontDef);
626 bool needsDesignMetrics = shaperFlags & QFontEngine::DesignMetrics;
627 if (!needsDesignMetrics && hint == QFont::PreferFullHinting) {
628 const DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
629 const bool needsNaturalMetrics = renderMode == DWRITE_RENDERING_MODE_NATURAL
630 || renderMode == DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
632 hr = m_directWriteFontFace->GetGdiCompatibleGlyphMetrics(
float(fontDef.pixelSize),
638 glyphMetrics.data());
640 hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(),
642 glyphMetrics.data());
645 qreal stretch = fontDef.stretch != QFont::AnyStretch ? fontDef.stretch / 100.0 : 1.0;
646 for (
int i = 0; i < glyphs->numGlyphs; ++i)
649 qErrnoWarning(
"%s: GetDesignGlyphMetrics failed",
__FUNCTION__);
653QPainterPath QWindowsFontEngineDirectWrite::unscaledGlyph(glyph_t glyph)
const
655 float advance = 0.0f;
657 DWRITE_GLYPH_OFFSET offset;
658 offset.advanceOffset = 0;
659 offset.ascenderOffset = 0;
662 GeometrySink geometrySink(&ret);
663 HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(m_unitsPerEm,
672 qErrnoWarning(
"%s: GetGlyphRunOutline failed",
__FUNCTION__);
677void QWindowsFontEngineDirectWrite::getUnscaledGlyph(glyph_t glyph,
679 glyph_metrics_t *metric)
681 *path = unscaledGlyph(glyph);
684 DWRITE_GLYPH_METRICS glyphMetrics;
685 HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&g, 1, &glyphMetrics);
687 qErrnoWarning(
"%s: GetDesignGlyphMetrics failed",
__FUNCTION__);
691 QFixed advanceWidth = QFixed(
int(glyphMetrics.advanceWidth));
692 QFixed leftSideBearing = QFixed(glyphMetrics.leftSideBearing);
693 QFixed rightSideBearing = QFixed(glyphMetrics.rightSideBearing);
694 QFixed advanceHeight = QFixed(
int(glyphMetrics.advanceHeight));
695 QFixed verticalOriginY = QFixed(glyphMetrics.verticalOriginY);
696 QFixed topSideBearing = QFixed(glyphMetrics.topSideBearing);
697 QFixed bottomSideBearing = QFixed(glyphMetrics.bottomSideBearing);
698 QFixed width = advanceWidth - leftSideBearing - rightSideBearing;
699 QFixed height = advanceHeight - topSideBearing - bottomSideBearing;
700 *metric = glyph_metrics_t(leftSideBearing,
701 -verticalOriginY + topSideBearing,
708void QWindowsFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions,
int nglyphs,
709 QPainterPath *path, QTextItem::RenderFlags flags)
712 QVarLengthArray<UINT16> glyphIndices(nglyphs);
713 QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(nglyphs);
714 QVarLengthArray<FLOAT> glyphAdvances(nglyphs);
716 for (
int i=0; i<nglyphs; ++i) {
717 glyphIndices[i] = glyphs[i];
718 glyphOffsets[i].advanceOffset = positions[i].x.toReal();
719 glyphOffsets[i].ascenderOffset = -positions[i].y.toReal();
720 glyphAdvances[i] = 0.0;
723 GeometrySink geometrySink(path);
724 HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(
727 glyphAdvances.data(),
736 qErrnoWarning(
"%s: GetGlyphRunOutline failed",
__FUNCTION__);
739glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(
const QGlyphLayout &glyphs)
741 if (glyphs.numGlyphs == 0)
742 return glyph_metrics_t();
744 for (
int i = 0; i < glyphs.numGlyphs; ++i)
745 w += glyphs.effectiveAdvance(i);
747 const QFixed leftBearing = firstLeftBearing(glyphs);
748 return glyph_metrics_t(leftBearing, -ascent(), w - leftBearing - lastRightBearing(glyphs),
749 ascent() + descent(), w, 0);
752glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(glyph_t g)
754 UINT16 glyphIndex = g;
756 DWRITE_GLYPH_METRICS glyphMetrics;
757 HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics);
766 QFixed width = advanceWidth - leftSideBearing - rightSideBearing;
767 QFixed height = advanceHeight - topSideBearing - bottomSideBearing;
768 return glyph_metrics_t(leftSideBearing,
769 -verticalOriginY + topSideBearing,
775 qErrnoWarning(
"%s: GetDesignGlyphMetrics failed",
__FUNCTION__);
778 return glyph_metrics_t();
781QFixed QWindowsFontEngineDirectWrite::capHeight()
const
783 if (m_capHeight <= 0)
784 return calculatedCapHeight();
789QFixed QWindowsFontEngineDirectWrite::xHeight()
const
794qreal QWindowsFontEngineDirectWrite::maxCharWidth()
const
796 return m_maxAdvanceWidth.toReal();
799QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
800 const QFixedPoint &subPixelPosition,
803 QImage im = imageForGlyph(glyph, subPixelPosition, glyphMargin(Format_A8), t);
806 QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
808 for (
int y=0; y<im.height(); ++y) {
809 const uint *src =
reinterpret_cast<
const uint *>(im.constScanLine(y));
810 uchar *dst = alphaMap.scanLine(y);
811 for (
int x=0; x<im.width(); ++x) {
820 return QFontEngine::alphaMapForGlyph(glyph, t);
824QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
825 const QFixedPoint &subPixelPosition)
827 return alphaMapForGlyph(glyph, subPixelPosition, QTransform());
830bool QWindowsFontEngineDirectWrite::supportsHorizontalSubPixelPositions()
const
832 QFont::HintingPreference hinting = determineHinting(fontDef);
833 return (hinting != QFont::PreferFullHinting && !(fontDef.styleStrategy & QFont::NoAntialias));
836QFontEngine::Properties QWindowsFontEngineDirectWrite::properties()
const
838 IDWriteFontFace2 *directWriteFontFace2;
839 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace2),
840 reinterpret_cast<
void **>(&directWriteFontFace2)))) {
841 DWRITE_FONT_METRICS1 metrics;
842 directWriteFontFace2->GetMetrics(&metrics);
844 Properties p = QFontEngine::properties();
845 p.emSquare = metrics.designUnitsPerEm;
846 p.boundingBox = QRectF(metrics.glyphBoxLeft,
847 -metrics.glyphBoxTop,
848 metrics.glyphBoxRight - metrics.glyphBoxLeft,
849 metrics.glyphBoxTop - metrics.glyphBoxBottom);
850 p.ascent = metrics.ascent;
851 p.descent = metrics.descent;
852 p.leading = metrics.lineGap;
853 p.capHeight = metrics.capHeight;
854 p.lineWidth = metrics.underlineThickness;
856 directWriteFontFace2->Release();
859 return QFontEngine::properties();
863bool QWindowsFontEngineDirectWrite::renderColr0GlyphRun(QImage *image,
864 const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun,
865 const DWRITE_MATRIX &transform,
866 DWRITE_RENDERING_MODE renderMode,
867 DWRITE_MEASURING_MODE measureMode,
868 DWRITE_GRID_FIT_MODE gridFitMode,
870 QRect boundingRect)
const
872 ComPtr<IDWriteFactory2> factory2;
873 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
878 ComPtr<IDWriteGlyphRunAnalysis> colorGlyphsAnalysis;
879 hr = factory2->CreateGlyphRunAnalysis(
880 &colorGlyphRun->glyphRun,
885 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
891 qErrnoWarning(hr,
"%s: CreateGlyphRunAnalysis failed for color run",
__FUNCTION__);
896 if (colorGlyphRun->paletteIndex == 0xFFFF) {
897 r =
float(color.redF());
898 g =
float(color.greenF());
899 b =
float(color.blueF());
902 r = qBound(0.0f, colorGlyphRun->runColor.r, 1.0f);
903 g = qBound(0.0f, colorGlyphRun->runColor.g, 1.0f);
904 b = qBound(0.0f, colorGlyphRun->runColor.b, 1.0f);
905 a = qBound(0.0f, colorGlyphRun->runColor.a, 1.0f);
908 if (!qFuzzyIsNull(a))
909 renderGlyphRun(image, r, g, b, a, colorGlyphsAnalysis.Get(), boundingRect, renderMode);
914#if QT_CONFIG(directwritecolrv1)
915static inline QTransform matrixToTransform(
const DWRITE_MATRIX &matrix,
918 return QTransform(matrix.m11, matrix.m12,
919 matrix.m21, matrix.m22,
920 matrix.dx * unitsPerEm, matrix.dy * unitsPerEm);
923static inline QColor colorToColor(
const DWRITE_COLOR_F &color,
float alphaMultiplier = 1.0)
925 return QColor::fromRgbF(color.r, color.g, color.b, color.a * alphaMultiplier);
928static inline QGradient::Spread extendToSpread(UINT32 extendMode)
930 switch (extendMode) {
931 case D2D1_EXTEND_MODE_WRAP:
return QGradient::RepeatSpread;
932 case D2D1_EXTEND_MODE_MIRROR:
return QGradient::ReflectSpread;
933 default:
return QGradient::PadSpread;
937static inline QPainter::CompositionMode compositeToCompositionMode(DWRITE_COLOR_COMPOSITE_MODE mode)
940 case DWRITE_COLOR_COMPOSITE_CLEAR:
941 return QPainter::CompositionMode_Clear;
943 case DWRITE_COLOR_COMPOSITE_SRC:
944 return QPainter::CompositionMode_Source;
946 case DWRITE_COLOR_COMPOSITE_DEST:
947 return QPainter::CompositionMode_Destination;
949 case DWRITE_COLOR_COMPOSITE_SRC_OVER:
950 return QPainter::CompositionMode_SourceOver;
952 case DWRITE_COLOR_COMPOSITE_DEST_OVER:
953 return QPainter::CompositionMode_DestinationOver;
955 case DWRITE_COLOR_COMPOSITE_SRC_IN:
956 return QPainter::CompositionMode_SourceIn;
958 case DWRITE_COLOR_COMPOSITE_DEST_IN:
959 return QPainter::CompositionMode_DestinationIn;
961 case DWRITE_COLOR_COMPOSITE_SRC_OUT:
962 return QPainter::CompositionMode_SourceOut;
964 case DWRITE_COLOR_COMPOSITE_DEST_OUT:
965 return QPainter::CompositionMode_DestinationOut;
967 case DWRITE_COLOR_COMPOSITE_SRC_ATOP:
968 return QPainter::CompositionMode_SourceAtop;
970 case DWRITE_COLOR_COMPOSITE_DEST_ATOP:
971 return QPainter::CompositionMode_DestinationAtop;
973 case DWRITE_COLOR_COMPOSITE_XOR:
974 return QPainter::CompositionMode_Xor;
976 case DWRITE_COLOR_COMPOSITE_PLUS:
977 return QPainter::CompositionMode_Plus;
979 case DWRITE_COLOR_COMPOSITE_SCREEN:
980 return QPainter::CompositionMode_Screen;
982 case DWRITE_COLOR_COMPOSITE_OVERLAY:
983 return QPainter::CompositionMode_Overlay;
985 case DWRITE_COLOR_COMPOSITE_DARKEN:
986 return QPainter::CompositionMode_Darken;
988 case DWRITE_COLOR_COMPOSITE_LIGHTEN:
989 return QPainter::CompositionMode_Lighten;
991 case DWRITE_COLOR_COMPOSITE_COLOR_DODGE:
992 return QPainter::CompositionMode_ColorDodge;
994 case DWRITE_COLOR_COMPOSITE_COLOR_BURN:
995 return QPainter::CompositionMode_ColorBurn;
997 case DWRITE_COLOR_COMPOSITE_HARD_LIGHT:
998 return QPainter::CompositionMode_HardLight;
1000 case DWRITE_COLOR_COMPOSITE_SOFT_LIGHT:
1001 return QPainter::CompositionMode_SoftLight;
1003 case DWRITE_COLOR_COMPOSITE_DIFFERENCE:
1004 return QPainter::CompositionMode_Difference;
1006 case DWRITE_COLOR_COMPOSITE_EXCLUSION:
1007 return QPainter::CompositionMode_Exclusion;
1009 case DWRITE_COLOR_COMPOSITE_MULTIPLY:
1010 return QPainter::CompositionMode_Multiply;
1013 qCWarning(lcColrv1) <<
"Unhandled color composite mode:" << mode;
1014 return QPainter::CompositionMode_SourceOver;
1019QRect QWindowsFontEngineDirectWrite::paintGraphBounds(glyph_t glyph,
1020 const DWRITE_MATRIX &matrix)
const
1022#if QT_CONFIG(directwritecolrv1)
1023 ComPtr<IDWriteFontFace7> face7;
1024 HRESULT hr = m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace7),
1026 if (SUCCEEDED(hr)) {
1027 DWRITE_PAINT_FEATURE_LEVEL featureLevel = face7->GetPaintFeatureLevel(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE);
1028 if (featureLevel != DWRITE_PAINT_FEATURE_LEVEL_COLR_V1)
1031 ComPtr<IDWritePaintReader> paintReader;
1032 hr = face7->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
1036 qErrnoWarning(hr,
"%s: CreatePaintReader failed",
__FUNCTION__);
1040 DWRITE_PAINT_ELEMENT paintElement;
1042 hr = paintReader->SetCurrentGlyph(glyph,
1044 sizeof(paintElement),
1049 qErrnoWarning(hr,
"%s: SetCurrentGlyph failed",
__FUNCTION__);
1053 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE)
1056 QRectF boundingRect = QRectF(QPointF(clipBox.left, clipBox.top),
1057 QPointF(clipBox.right, clipBox.bottom));
1058 if (boundingRect.isNull()) {
1059 QColrPaintGraphRenderer boundingRectCalculator;
1060 boundingRectCalculator.beginCalculateBoundingBox();
1061 if (traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &boundingRectCalculator, 0))
1062 boundingRect = boundingRectCalculator.boundingRect();
1065 QTransform initialTransform;
1066 initialTransform.scale(fontDef.pixelSize, fontDef.pixelSize);
1067 boundingRect = initialTransform.mapRect(boundingRect);
1069 QTransform originalXform = matrixToTransform(matrix);
1070 boundingRect = originalXform.mapRect(boundingRect);
1072 return boundingRect.toAlignedRect();
1074 qCDebug(lcColrv1) <<
"Font face does not support IDWriteFontFace7 interface";
1084#if QT_CONFIG(directwritecolrv1)
1085bool QWindowsFontEngineDirectWrite::traverseColr1(IDWritePaintReader *paintReader,
1086 IDWriteFontFace7 *face7,
1087 const DWRITE_PAINT_ELEMENT *paintElement,
1088 QColrPaintGraphRenderer *paintGraphRenderer,
1091 if (maxColrv1NestingDepth() > 0 && depth > maxColrv1NestingDepth()) {
1092 qCWarning(lcColrv1) <<
"COLRv1 graph nested deeper than" << maxColrv1NestingDepth()
1093 <<
"levels; aborting traversal";
1097 paintGraphRenderer->save();
1098 auto cleanup = qScopeGuard([&paintGraphRenderer]() {
1099 paintGraphRenderer->restore();
1102 auto traverseChildren = [&](quint32 childCount) {
1103 DWRITE_PAINT_ELEMENT childPaintElement;
1104 if (FAILED(paintReader->MoveToFirstChild(&childPaintElement,
sizeof(DWRITE_PAINT_ELEMENT))))
1107 while (childCount-- > 0) {
1108 traverseColr1(paintReader, face7, &childPaintElement, paintGraphRenderer, depth + 1);
1109 if (childCount > 0) {
1110 if (FAILED(paintReader->MoveToNextSibling(&childPaintElement,
sizeof(DWRITE_PAINT_ELEMENT)))) {
1116 return SUCCEEDED(paintReader->MoveToParent());
1119 auto collectStops = [&paintReader](
int stopCount) {
1122 QVarLengthArray<D2D1_GRADIENT_STOP> stops(stopCount);
1123 HRESULT hr = paintReader->GetGradientStops(0, stopCount, stops.data());
1127 for (
int i = 0; i < stopCount; ++i) {
1128 const D2D1_GRADIENT_STOP &stop = stops[i];
1129 QColor color = QColor::fromRgbF(stop.color.r, stop.color.g, stop.color.b, stop.color.a);
1130 ret.append({stop.position, color});
1136 switch (paintElement->paintType) {
1137 case DWRITE_PAINT_TYPE_LAYERS:
1139 if (!traverseChildren(paintElement->paint.layers.childCount))
1145 case DWRITE_PAINT_TYPE_SOLID_GLYPH:
1147 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.solidGlyph.glyphIndex);
1149 QColor color = colorToColor(paintElement->paint.solidGlyph.color.value);
1150 paintGraphRenderer->setPath(glyphPath);
1151 paintGraphRenderer->setSolidColor(color);
1152 paintGraphRenderer->drawCurrentPath();
1157 case DWRITE_PAINT_TYPE_SOLID:
1159 QColor color = colorToColor(paintElement->paint.solid.value,
1160 paintElement->paint.solid.alphaMultiplier);
1161 paintGraphRenderer->setSolidColor(color);
1162 paintGraphRenderer->drawCurrentPath();
1166 case DWRITE_PAINT_TYPE_COMPOSITE:
1168 if (!paintGraphRenderer->isRendering()) {
1169 traverseChildren(2);
1171 DWRITE_PAINT_ELEMENT childElement;
1173 HRESULT hr = paintReader->MoveToFirstChild(&childElement,
sizeof(DWRITE_PAINT_ELEMENT));
1175 qErrnoWarning(hr,
"%s: Cannot move to first child of composite node",
1181 hr = paintReader->MoveToNextSibling(&childElement,
sizeof(DWRITE_PAINT_ELEMENT));
1183 qErrnoWarning(hr,
"%s: Cannot move to second child of composite node",
1188 DWRITE_COLOR_COMPOSITE_MODE compositeMode = paintElement->paint.composite.mode;
1190 QColrPaintGraphRenderer compositeRenderer;
1191 compositeRenderer.setBoundingRect(paintGraphRenderer->boundingRect());
1192 compositeRenderer.beginRender(fontDef.pixelSize / m_unitsPerEm,
1193 paintGraphRenderer->currentTransform());
1194 if (!traverseColr1(paintReader, face7, &childElement, &compositeRenderer, depth + 1))
1198 hr = paintReader->MoveToParent();
1200 qErrnoWarning(hr,
"%s: Cannot move back to parent composite node",
1205 hr = paintReader->MoveToFirstChild(&childElement,
sizeof(DWRITE_PAINT_ELEMENT));
1207 qErrnoWarning(hr,
"%s: Cannot move to first child of composite node",
1212 compositeRenderer.setCompositionMode(compositeToCompositionMode(compositeMode));
1213 if (!traverseColr1(paintReader, face7, &childElement, &compositeRenderer, depth + 1))
1216 hr = paintReader->MoveToParent();
1218 qErrnoWarning(hr,
"%s: Cannot move back to parent composite node",
1223 paintGraphRenderer->drawImage(compositeRenderer.endRender());
1229 case DWRITE_PAINT_TYPE_RADIAL_GRADIENT:
1231 const QPointF c0(paintElement->paint.radialGradient.x0 * m_unitsPerEm,
1232 paintElement->paint.radialGradient.y0 * m_unitsPerEm);
1233 const QPointF c1(paintElement->paint.radialGradient.x1 * m_unitsPerEm,
1234 paintElement->paint.radialGradient.y1 * m_unitsPerEm);
1235 const qreal r0 = paintElement->paint.radialGradient.radius0 * m_unitsPerEm;
1236 const qreal r1 = paintElement->paint.radialGradient.radius1 * m_unitsPerEm;
1238 const QGradient::Spread spread
1239 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1241 const QGradientStops gradientStops
1242 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1244 paintGraphRenderer->setRadialGradient(c0, r0, c1, r1, spread, gradientStops);
1245 paintGraphRenderer->drawCurrentPath();
1249 case DWRITE_PAINT_TYPE_SWEEP_GRADIENT:
1251 const QPointF center(paintElement->paint.sweepGradient.centerX * m_unitsPerEm,
1252 paintElement->paint.sweepGradient.centerY * m_unitsPerEm);
1253 const qreal startAngle = paintElement->paint.sweepGradient.startAngle;
1254 const qreal endAngle = paintElement->paint.sweepGradient.endAngle;
1256 const QGradient::Spread spread
1257 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1258 const QGradientStops gradientStops
1259 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1261 paintGraphRenderer->setConicalGradient(center,
1266 paintGraphRenderer->drawCurrentPath();
1270 case DWRITE_PAINT_TYPE_LINEAR_GRADIENT:
1272 const QPointF p0(paintElement->paint.linearGradient.x0 * m_unitsPerEm,
1273 paintElement->paint.linearGradient.y0 * m_unitsPerEm);
1274 const QPointF p1(paintElement->paint.linearGradient.x1 * m_unitsPerEm,
1275 paintElement->paint.linearGradient.y1 * m_unitsPerEm);
1276 const QPointF p2(paintElement->paint.linearGradient.x2 * m_unitsPerEm,
1277 paintElement->paint.linearGradient.y2 * m_unitsPerEm);
1279 const QGradient::Spread spread
1280 = extendToSpread(paintElement->paint.linearGradient.extendMode);
1281 const QGradientStops gradientStops
1282 = collectStops(paintElement->paint.linearGradient.gradientStopCount);
1284 paintGraphRenderer->setLinearGradient(p0, p1, p2, spread, gradientStops);
1285 paintGraphRenderer->drawCurrentPath();
1289 case DWRITE_PAINT_TYPE_GLYPH:
1291 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.glyph.glyphIndex);
1292 paintGraphRenderer->appendPath(glyphPath);
1293 if (!traverseChildren(1))
1298 case DWRITE_PAINT_TYPE_COLOR_GLYPH:
1300 D2D_RECT_F rect = paintElement->paint.colorGlyph.clipBox;
1301 QRect clipBox = QRectF(QPointF(rect.left, rect.top),
1302 QPointF(rect.right, rect.bottom)).toAlignedRect();
1303 if (!clipBox.isEmpty()) {
1304 QTransform coordinatesTransform;
1305 coordinatesTransform.scale(m_unitsPerEm, m_unitsPerEm);
1306 clipBox = coordinatesTransform.mapRect(clipBox);
1308 paintGraphRenderer->setClip(clipBox);
1311 DWRITE_PAINT_ELEMENT childElement;
1312 if (FAILED(paintReader->MoveToFirstChild(&childElement,
sizeof(DWRITE_PAINT_ELEMENT))))
1315 if (!traverseColr1(paintReader, face7, &childElement, paintGraphRenderer, depth + 1))
1318 if (FAILED(paintReader->MoveToParent()))
1324 case DWRITE_PAINT_TYPE_TRANSFORM:
1326 QTransform transform = matrixToTransform(paintElement->paint.transform, m_unitsPerEm);
1327 paintGraphRenderer->prependTransform(transform);
1328 if (!traverseChildren(1))
1335 qCDebug(lcColrv1) <<
"Unhandled paint graph node type" << paintElement->paintType;
1342bool QWindowsFontEngineDirectWrite::renderColr1GlyphRun(QImage *image,
1343 const DWRITE_GLYPH_RUN *glyphRun,
1344 const DWRITE_MATRIX &matrix,
1347 qCDebug(lcColrv1) <<
"renderColr1GlyphRun,"
1348 <<
"families:" << fontDef.families;
1349 ComPtr<IDWriteFontFace7> face7;
1350 HRESULT hr = m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace7),
1352 if (SUCCEEDED(hr)) {
1353 DWRITE_PAINT_FEATURE_LEVEL featureLevel =
1354 face7->GetPaintFeatureLevel(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE);
1355 if (featureLevel != DWRITE_PAINT_FEATURE_LEVEL_COLR_V1) {
1356 qCDebug(lcColrv1) <<
"Unsupported feature level:" << featureLevel;
1360 ComPtr<IDWritePaintReader> paintReader;
1361 hr = face7->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
1365 qErrnoWarning(hr,
"%s: CreatePaintReader failed",
__FUNCTION__);
1369 Q_ASSERT(glyphRun->glyphCount == 1);
1370 DWRITE_PAINT_ELEMENT paintElement;
1372 hr = paintReader->SetCurrentGlyph(glyphRun->glyphIndices[0],
1374 sizeof(DWRITE_PAINT_ELEMENT),
1378 qErrnoWarning(hr,
"%s: SetCurrentGlyph failed",
__FUNCTION__);
1382 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE) {
1383 qCDebug(lcColrv1) <<
"Glyph" << glyphRun->glyphIndices[0]
1384 <<
"does not have a paint graph";
1388 DWRITE_COLOR_F dwColor;
1389 dwColor.r = color.redF();
1390 dwColor.g = color.greenF();
1391 dwColor.b = color.blueF();
1392 dwColor.a = color.alphaF();
1393 paintReader->SetTextColor(dwColor);
1395 QRectF boundingRect = QRectF(QPointF(clipBox.left, clipBox.top),
1396 QPointF(clipBox.right, clipBox.bottom));
1397 if (boundingRect.isNull()) {
1398 QColrPaintGraphRenderer boundingRectCalculator;
1399 boundingRectCalculator.beginCalculateBoundingBox();
1400 if (traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &boundingRectCalculator, 0))
1401 boundingRect = boundingRectCalculator.boundingRect();
1404 QTransform initialTransform;
1405 initialTransform.scale(fontDef.pixelSize, fontDef.pixelSize);
1406 boundingRect = initialTransform.mapRect(boundingRect);
1408 QTransform originalXform = matrixToTransform(matrix);
1409 boundingRect = originalXform.mapRect(boundingRect);
1411 qCDebug(lcColrv1).noquote() <<
"Bounds of"
1412 << glyphRun->glyphIndices[0]
1413 <<
" in device coordinates:"
1416 QColrPaintGraphRenderer graphRenderer;
1417 graphRenderer.setBoundingRect(boundingRect);
1418 graphRenderer.beginRender(fontDef.pixelSize / m_unitsPerEm, matrixToTransform(matrix));
1419 if (!traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &graphRenderer, 0))
1422 *image = graphRenderer.endRender();
1424 qCDebug(lcColrv1) <<
"Font face does not support IDWriteFontFace7 interface";
1431QImage QWindowsFontEngineDirectWrite::renderColorGlyph(DWRITE_GLYPH_RUN *glyphRun,
1432 const DWRITE_MATRIX &transform,
1433 DWRITE_RENDERING_MODE renderMode,
1434 DWRITE_MEASURING_MODE measureMode,
1435 DWRITE_GRID_FIT_MODE gridFitMode,
1437 QRect boundingRect)
const
1441#if QT_CONFIG(directwritecolrv1)
1444 renderColr1GlyphRun(&ret, glyphRun, transform, color);
1447#if QT_CONFIG(directwrite3)
1451 ComPtr<IDWriteFactory4> factory4;
1452 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory4),
1454 if (SUCCEEDED(hr)) {
1455 const DWRITE_GLYPH_IMAGE_FORMATS supportedBitmapFormats =
1456 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_PNG
1457 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
1458 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF);
1460 const DWRITE_GLYPH_IMAGE_FORMATS glyphFormats =
1461 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_COLR
1462 | supportedBitmapFormats
1463 | DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE
1464 | DWRITE_GLYPH_IMAGE_FORMATS_CFF);
1466 ComPtr<IDWriteColorGlyphRunEnumerator1> enumerator;
1467 hr = factory4->TranslateColorGlyphRun(D2D1::Point2F(0.0f, 0.0f),
1476 while (SUCCEEDED(hr) && ok) {
1477 hr = enumerator->MoveNext(&ok);
1481 const DWRITE_COLOR_GLYPH_RUN1 *colorGlyphRun =
nullptr;
1482 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1484 qErrnoWarning(hr,
"%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed",
__FUNCTION__);
1488 if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_NONE) {
1492 }
else if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_COLR) {
1494 ret = QImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied);
1498 if (renderColr0GlyphRun(&ret,
1499 reinterpret_cast<
const DWRITE_COLOR_GLYPH_RUN *>(colorGlyphRun),
1508 }
else if (colorGlyphRun->glyphImageFormat & supportedBitmapFormats) {
1509 ComPtr<IDWriteFontFace4> face4;
1510 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
1512 DWRITE_GLYPH_IMAGE_DATA data;
1514 Q_ASSERT(glyphRun->glyphCount == 1);
1515 HRESULT hr = face4->GetGlyphImageData(glyphRun->glyphIndices[0],
1517 DWRITE_GLYPH_IMAGE_FORMATS(colorGlyphRun->glyphImageFormat & supportedBitmapFormats),
1521 qErrnoWarning(
"%s: GetGlyphImageData failed",
__FUNCTION__);
1525 auto fnc = qScopeGuard([&]() {
1526 if (data.imageData !=
nullptr)
1527 face4->ReleaseGlyphImageData(ctx);
1530 if (data.pixelsPerEm == 0 || data.imageData ==
nullptr || data.imageDataSize == 0) {
1531 qCWarning(lcQpaFonts) <<
__FUNCTION__
1532 <<
"Failed to retrieve image data for glyph"
1533 << glyphRun->glyphIndices[0]
1537 << colorGlyphRun->glyphImageFormat;
1540 switch (colorGlyphRun->glyphImageFormat) {
1541 case DWRITE_GLYPH_IMAGE_FORMATS_JPEG:
1544 case DWRITE_GLYPH_IMAGE_FORMATS_TIFF:
1552 ret = QImage::fromData(
reinterpret_cast<
const uchar *>(data.imageData),
1556 QTransform matrix(transform.m11, transform.m12,
1557 transform.m21, transform.m22,
1558 transform.dx, transform.dy);
1560 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
1561 matrix.scale(scale, scale);
1563 if (!matrix.isIdentity())
1564 ret = ret.transformed(matrix, Qt::SmoothTransformation);
1571 qCDebug(lcQpaFonts) <<
"Found glyph run with unsupported format"
1572 << colorGlyphRun->glyphImageFormat;
1581 ComPtr<IDWriteFactory2> factory2;
1582 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1587 ComPtr<IDWriteColorGlyphRunEnumerator> enumerator;
1588 hr = factory2->TranslateColorGlyphRun(0.0f,
1596 if (SUCCEEDED(hr)) {
1597 ret = QImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied);
1601 while (SUCCEEDED(hr) && ok) {
1602 hr = enumerator->MoveNext(&ok);
1604 qErrnoWarning(hr,
"%s: IDWriteColorGlyphRunEnumerator::MoveNext failed",
__FUNCTION__);
1609 const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun =
nullptr;
1610 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1612 qErrnoWarning(hr,
"%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed",
__FUNCTION__);
1616 if (!renderColr0GlyphRun(&ret,
1634QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
1635 const QFixedPoint &subPixelPosition,
1637 const QTransform &originalTransform,
1638 const QColor &color)
1640 UINT16 glyphIndex = t;
1641 FLOAT glyphAdvance = 0;
1643 DWRITE_GLYPH_OFFSET glyphOffset;
1644 glyphOffset.advanceOffset = 0;
1645 glyphOffset.ascenderOffset = 0;
1647 DWRITE_GLYPH_RUN glyphRun;
1648 glyphRun.fontFace = m_directWriteFontFace;
1649 glyphRun.fontEmSize = fontDef.pixelSize;
1650 glyphRun.glyphCount = 1;
1651 glyphRun.glyphIndices = &glyphIndex;
1652 glyphRun.glyphAdvances = &glyphAdvance;
1653 glyphRun.isSideways =
false;
1654 glyphRun.bidiLevel = 0;
1655 glyphRun.glyphOffsets = &glyphOffset;
1657 QTransform xform = originalTransform;
1658 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
1659 xform.scale(fontDef.stretch / 100.0, 1.0);
1661 DWRITE_MATRIX transform;
1662 transform.dx = subPixelPosition.x.toReal();
1664 transform.m11 = xform.m11();
1665 transform.m12 = xform.m12();
1666 transform.m21 = xform.m21();
1667 transform.m22 = xform.m22();
1669 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1670 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1671 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1672 ? DWRITE_GRID_FIT_MODE_DISABLED
1673 : DWRITE_GRID_FIT_MODE_DEFAULT;
1675 ComPtr<IDWriteFactory2> factory2;
1676 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1678 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1679 if (!SUCCEEDED(hr)) {
1680 qErrnoWarning(hr,
"%s: Failed to query IDWriteFactory2 interface.",
__FUNCTION__);
1681 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1691 hr = factory2->CreateGlyphRunAnalysis(
1697 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1703 if (SUCCEEDED(hr)) {
1704 QRect rect = paintGraphBounds(t, transform);
1706 rect = alphaTextureBounds(t, transform);
1708 rect = colorBitmapBounds(t, transform);
1710 if (rect.isEmpty()) {
1711 qCDebug(lcQpaFonts) <<
__FUNCTION__ <<
"Cannot get alpha texture bounds. Falling back to slower rendering path.";
1715 if (glyphBoundingBoxExceedsLimit(rect.width(),
1717 effectivePixelSizeSquared(xform))) {
1721 QRect boundingRect = rect.adjusted(-margin, -margin, margin, margin);
1724 if (glyphFormat == QFontEngine::Format_ARGB) {
1725 image = renderColorGlyph(&glyphRun,
1735 if (image.isNull()) {
1736 image = QImage(boundingRect.size(),
1737 glyphFormat == QFontEngine::Format_ARGB
1738 ? QImage::Format_ARGB32_Premultiplied
1739 : QImage::Format_RGB32);
1740 image.fill(image.format() == QImage::Format_ARGB32_Premultiplied ? 0x0 : 0xffffffff);
1743 if (glyphFormat == QFontEngine::Format_ARGB) {
1744 r =
float(color.redF());
1745 g =
float(color.greenF());
1746 b =
float(color.blueF());
1747 a =
float(color.alphaF());
1749 r = g = b = a = 0.0;
1752 renderGlyphRun(&image,
1757 glyphAnalysis.Get(),
1764 qErrnoWarning(hr,
"%s: CreateGlyphRunAnalysis failed",
__FUNCTION__);
1769void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
1774 IDWriteGlyphRunAnalysis *glyphAnalysis,
1775 const QRect &boundingRect,
1776 DWRITE_RENDERING_MODE renderMode)
const
1778 const int width = destination->width();
1779 const int height = destination->height();
1785 const int size = width * height * 3;
1788 rect.left = boundingRect.left();
1789 rect.top = boundingRect.top();
1790 rect.right = boundingRect.left() + boundingRect.width();
1791 rect.bottom = boundingRect.top() + boundingRect.height();
1793 QVarLengthArray<BYTE, 1024> alphaValueArray(size);
1794 BYTE *alphaValues = alphaValueArray.data();
1795 memset(alphaValues, 0, size);
1797 HRESULT hr = glyphAnalysis->CreateAlphaTexture(renderMode == DWRITE_RENDERING_MODE_ALIASED
1798 ? DWRITE_TEXTURE_ALIASED_1x1
1799 : DWRITE_TEXTURE_CLEARTYPE_3x1,
1803 if (SUCCEEDED(hr)) {
1804 if (destination->hasAlphaChannel()) {
1805 for (
int y = 0; y < height; ++y) {
1806 uint *dest =
reinterpret_cast<uint *>(destination->scanLine(y));
1807 BYTE *src = alphaValues + width * 3 * y;
1809 for (
int x = 0; x < width; ++x) {
1810 float redAlpha = a * *src++ / 255.0;
1811 float greenAlpha = a * *src++ / 255.0;
1812 float blueAlpha = a * *src++ / 255.0;
1813 float averageAlpha = (redAlpha + greenAlpha + blueAlpha) / 3.0;
1815 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1816 qSwap(redAlpha, blueAlpha);
1818 QRgb currentRgb = dest[x];
1819 dest[x] = qRgba(qRound(qRed(currentRgb) * (1.0 - averageAlpha) + averageAlpha * r),
1820 qRound(qGreen(currentRgb) * (1.0 - averageAlpha) + averageAlpha * g),
1821 qRound(qBlue(currentRgb) * (1.0 - averageAlpha) + averageAlpha * b),
1822 qRound(qAlpha(currentRgb) * (1.0 - averageAlpha) + averageAlpha * 255));
1825 }
else if (renderMode == DWRITE_RENDERING_MODE_ALIASED) {
1826 for (
int y = 0; y < height; ++y) {
1827 uint *dest =
reinterpret_cast<uint *>(destination->scanLine(y));
1828 BYTE *src = alphaValues + width * y;
1830 for (
int x = 0; x < width; ++x) {
1831 int alpha = *(src++);
1832 dest[x] = (alpha << 16) + (alpha << 8) + alpha;
1836 for (
int y = 0; y < height; ++y) {
1837 uint *dest =
reinterpret_cast<uint *>(destination->scanLine(y));
1838 BYTE *src = alphaValues + width * 3 * y;
1840 for (
int x = 0; x < width; ++x) {
1841 BYTE redAlpha = *(src + 0);
1842 BYTE greenAlpha = *(src + 1);
1843 BYTE blueAlpha = *(src + 2);
1845 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1846 qSwap(redAlpha, blueAlpha);
1848 dest[x] = qRgb(redAlpha, greenAlpha, blueAlpha);
1854 qErrnoWarning(
"%s: CreateAlphaTexture failed",
__FUNCTION__);
1859QImage QWindowsFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t,
1860 const QFixedPoint &subPixelPosition,
1861 const QTransform &xform)
1863 QImage mask = imageForGlyph(t,
1865 glyphMargin(QFontEngine::Format_A32),
1868 if (mask.isNull()) {
1869 if (xform.type() > QTransform::TxTranslate && glyphTransformExceedsLimit(t, xform))
1872 mask = QFontEngine::renderedPathForGlyph(t, Qt::white);
1873 if (!xform.isIdentity())
1874 mask = mask.transformed(xform);
1877 return mask.depth() == 32
1879 : mask.convertToFormat(QImage::Format_RGB32);
1882QFontEngine *QWindowsFontEngineDirectWrite::cloneWithSize(qreal pixelSize)
const
1884 QWindowsFontEngineDirectWrite *fontEngine =
new QWindowsFontEngineDirectWrite(m_directWriteFontFace,
1888 fontEngine->fontDef = fontDef;
1889 fontEngine->fontDef.pixelSize = pixelSize;
1890 if (!m_uniqueFamilyName.isEmpty()) {
1891 fontEngine->setUniqueFamilyName(m_uniqueFamilyName);
1892 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
1893 static_cast<QWindowsFontDatabase *>(pfdb)->refUniqueFont(m_uniqueFamilyName);
1899Qt::HANDLE QWindowsFontEngineDirectWrite::handle()
const
1901 return m_directWriteFontFace;
1904void QWindowsFontEngineDirectWrite::initFontInfo(
const QFontDef &request,
1909 if (fontDef.pointSize < 0)
1910 fontDef.pointSize = fontDef.pixelSize * 72. / dpi;
1911 else if (fontDef.pixelSize == -1)
1912 fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.);
1914 m_faceId.variableAxes = request.variableAxisValues;
1916#if QT_CONFIG(directwrite3)
1917 IDWriteFontFace3 *face3 =
nullptr;
1918 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace3),
1919 reinterpret_cast<
void **>(&face3)))) {
1921 fontDef.weight = QWindowsDirectWriteFontDatabase::fromDirectWriteWeight(face3->GetWeight());
1922 fontDef.style = QWindowsDirectWriteFontDatabase::fromDirectWriteStyle(face3->GetStyle());
1924 IDWriteLocalizedStrings *names;
1925 if (SUCCEEDED(face3->GetFaceNames(&names))) {
1926 wchar_t englishLocale[] = L"en-us";
1927 fontDef.styleName = QWindowsDirectWriteFontDatabase::localeString(names, englishLocale);
1932 if (face3->IsColorFont())
1933 glyphFormat = QFontEngine::Format_ARGB;
1940QString QWindowsFontEngineDirectWrite::fontNameSubstitute(
const QString &familyName)
1942 const QString substitute =
1943 QWinRegistryKey(HKEY_LOCAL_MACHINE,
1944 LR"(Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes)")
1945 .stringValue(familyName);
1946 return substitute.isEmpty() ? familyName : substitute;
1949QRect QWindowsFontEngineDirectWrite::alphaTextureBounds(glyph_t glyph,
1950 const DWRITE_MATRIX &transform)
1952 UINT16 glyphIndex = glyph;
1953 FLOAT glyphAdvance = 0;
1955 DWRITE_GLYPH_OFFSET glyphOffset;
1956 glyphOffset.advanceOffset = 0;
1957 glyphOffset.ascenderOffset = 0;
1959 DWRITE_GLYPH_RUN glyphRun;
1960 glyphRun.fontFace = m_directWriteFontFace;
1961 glyphRun.fontEmSize = fontDef.pixelSize;
1962 glyphRun.glyphCount = 1;
1963 glyphRun.glyphIndices = &glyphIndex;
1964 glyphRun.glyphAdvances = &glyphAdvance;
1965 glyphRun.isSideways =
false;
1966 glyphRun.bidiLevel = 0;
1967 glyphRun.glyphOffsets = &glyphOffset;
1969 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1970 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1971 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1972 ? DWRITE_GRID_FIT_MODE_DISABLED
1973 : DWRITE_GRID_FIT_MODE_DEFAULT;
1975 ComPtr<IDWriteFactory2> factory2 =
nullptr;
1976 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1979 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1980 if (SUCCEEDED(hr)) {
1981 hr = factory2->CreateGlyphRunAnalysis(
1987 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1992 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
2003 if (SUCCEEDED(hr)) {
2005 hr = glyphAnalysis->GetAlphaTextureBounds(renderMode == DWRITE_RENDERING_MODE_ALIASED
2006 ? DWRITE_TEXTURE_ALIASED_1x1
2007 : DWRITE_TEXTURE_CLEARTYPE_3x1,
2009 if (FAILED(hr) || rect.left == rect.right || rect.top == rect.bottom)
2012 return QRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
2018QRect QWindowsFontEngineDirectWrite::colorBitmapBounds(glyph_t glyph,
const DWRITE_MATRIX &transform)
2020#if QT_CONFIG(directwrite3)
2021 ComPtr<IDWriteFontFace4> face4;
2022 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
2024 DWRITE_GLYPH_IMAGE_FORMATS formats = face4->GetGlyphImageFormats();
2026 const DWRITE_GLYPH_IMAGE_FORMATS supportedBitmapFormats =
2027 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_PNG
2028 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
2029 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF);
2031 if (formats & supportedBitmapFormats) {
2032 DWRITE_GLYPH_IMAGE_DATA data;
2034 HRESULT hr = face4->GetGlyphImageData(glyph,
2036 DWRITE_GLYPH_IMAGE_FORMATS(formats & supportedBitmapFormats),
2040 qErrnoWarning(
"%s: GetGlyphImageData failed",
__FUNCTION__);
2044 auto fnc = qScopeGuard([&]() {
2045 if (data.imageData !=
nullptr)
2046 face4->ReleaseGlyphImageData(ctx);
2049 if (data.pixelsPerEm == 0 || data.imageData ==
nullptr || data.imageDataSize == 0)
2052 QRect rect(-data.horizontalLeftOrigin.x,
2053 -data.horizontalLeftOrigin.y,
2054 data.pixelSize.width,
2055 data.pixelSize.height);
2057 QTransform matrix(transform.m11, transform.m12,
2058 transform.m21, transform.m22,
2059 transform.dx, transform.dy);
2062 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
2063 matrix.scale(scale, scale);
2064 rect = matrix.mapRect(rect);
2073 Q_UNUSED(transform);
2078glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph,
2079 const QFixedPoint &subPixelPosition,
2080 const QTransform &originalTransform,
2083 QTransform matrix = originalTransform;
2084 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
2085 matrix.scale(fontDef.stretch / 100.0, 1.0);
2087 glyph_metrics_t bbox = QFontEngine::boundingBox(glyph, matrix);
2089 DWRITE_MATRIX transform;
2090 transform.dx = subPixelPosition.x.toReal();
2092 transform.m11 = matrix.m11();
2093 transform.m12 = matrix.m12();
2094 transform.m21 = matrix.m21();
2095 transform.m22 = matrix.m22();
2098 QRect rect = paintGraphBounds(glyph, transform);
2102 rect = alphaTextureBounds(glyph, transform);
2106 rect = colorBitmapBounds(glyph, transform);
2112 int margin = glyphMargin(format);
2113 return glyph_metrics_t(rect.left(),
2115 rect.width() + margin * 2,
2116 rect.height() + margin * 2,
2117 bbox.xoff, bbox.yoff);
2120QImage QWindowsFontEngineDirectWrite::bitmapForGlyph(glyph_t glyph,
2121 const QFixedPoint &subPixelPosition,
2122 const QTransform &t,
2123 const QColor &color)
2125 return imageForGlyph(glyph, subPixelPosition, glyphMargin(QFontEngine::Format_ARGB), t, color);
2128QList<QFontVariableAxis> QWindowsFontEngineDirectWrite::variableAxes()
const
2130 return m_variableAxes;
2133bool QWindowsFontEngineDirectWrite::expectsGammaCorrectedBlending(QFontEngine::GlyphFormat format)
const
2135 return format == QFontEngine::Format_A32;
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)