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))
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)
const
1090 paintGraphRenderer->save();
1091 auto cleanup = qScopeGuard([&paintGraphRenderer]() {
1092 paintGraphRenderer->restore();
1095 auto traverseChildren = [&](quint32 childCount) {
1096 DWRITE_PAINT_ELEMENT childPaintElement;
1097 if (FAILED(paintReader->MoveToFirstChild(&childPaintElement,
sizeof(DWRITE_PAINT_ELEMENT))))
1100 while (childCount-- > 0) {
1101 traverseColr1(paintReader, face7, &childPaintElement, paintGraphRenderer);
1102 if (childCount > 0) {
1103 if (FAILED(paintReader->MoveToNextSibling(&childPaintElement,
sizeof(DWRITE_PAINT_ELEMENT)))) {
1109 return SUCCEEDED(paintReader->MoveToParent());
1112 auto collectStops = [&paintReader](
int stopCount) {
1115 QVarLengthArray<D2D1_GRADIENT_STOP> stops(stopCount);
1116 HRESULT hr = paintReader->GetGradientStops(0, stopCount, stops.data());
1120 for (
int i = 0; i < stopCount; ++i) {
1121 const D2D1_GRADIENT_STOP &stop = stops[i];
1122 QColor color = QColor::fromRgbF(stop.color.r, stop.color.g, stop.color.b, stop.color.a);
1123 ret.append({stop.position, color});
1129 switch (paintElement->paintType) {
1130 case DWRITE_PAINT_TYPE_LAYERS:
1132 if (!traverseChildren(paintElement->paint.layers.childCount))
1138 case DWRITE_PAINT_TYPE_SOLID_GLYPH:
1140 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.solidGlyph.glyphIndex);
1142 QColor color = colorToColor(paintElement->paint.solidGlyph.color.value);
1143 paintGraphRenderer->setPath(glyphPath);
1144 paintGraphRenderer->setSolidColor(color);
1145 paintGraphRenderer->drawCurrentPath();
1150 case DWRITE_PAINT_TYPE_SOLID:
1152 QColor color = colorToColor(paintElement->paint.solid.value,
1153 paintElement->paint.solid.alphaMultiplier);
1154 paintGraphRenderer->setSolidColor(color);
1155 paintGraphRenderer->drawCurrentPath();
1159 case DWRITE_PAINT_TYPE_COMPOSITE:
1161 if (!paintGraphRenderer->isRendering()) {
1162 traverseChildren(2);
1164 DWRITE_PAINT_ELEMENT childElement;
1166 HRESULT hr = paintReader->MoveToFirstChild(&childElement,
sizeof(DWRITE_PAINT_ELEMENT));
1168 qErrnoWarning(hr,
"%s: Cannot move to first child of composite node",
1174 hr = paintReader->MoveToNextSibling(&childElement,
sizeof(DWRITE_PAINT_ELEMENT));
1176 qErrnoWarning(hr,
"%s: Cannot move to second child of composite node",
1181 DWRITE_COLOR_COMPOSITE_MODE compositeMode = paintElement->paint.composite.mode;
1183 QColrPaintGraphRenderer compositeRenderer;
1184 compositeRenderer.setBoundingRect(paintGraphRenderer->boundingRect());
1185 compositeRenderer.beginRender(fontDef.pixelSize / m_unitsPerEm,
1186 paintGraphRenderer->currentTransform());
1187 if (!traverseColr1(paintReader, face7, &childElement, &compositeRenderer))
1191 hr = paintReader->MoveToParent();
1193 qErrnoWarning(hr,
"%s: Cannot move back to parent composite node",
1198 hr = paintReader->MoveToFirstChild(&childElement,
sizeof(DWRITE_PAINT_ELEMENT));
1200 qErrnoWarning(hr,
"%s: Cannot move to first child of composite node",
1205 compositeRenderer.setCompositionMode(compositeToCompositionMode(compositeMode));
1206 if (!traverseColr1(paintReader, face7, &childElement, &compositeRenderer))
1209 hr = paintReader->MoveToParent();
1211 qErrnoWarning(hr,
"%s: Cannot move back to parent composite node",
1216 paintGraphRenderer->drawImage(compositeRenderer.endRender());
1222 case DWRITE_PAINT_TYPE_RADIAL_GRADIENT:
1224 const QPointF c0(paintElement->paint.radialGradient.x0 * m_unitsPerEm,
1225 paintElement->paint.radialGradient.y0 * m_unitsPerEm);
1226 const QPointF c1(paintElement->paint.radialGradient.x1 * m_unitsPerEm,
1227 paintElement->paint.radialGradient.y1 * m_unitsPerEm);
1228 const qreal r0 = paintElement->paint.radialGradient.radius0 * m_unitsPerEm;
1229 const qreal r1 = paintElement->paint.radialGradient.radius1 * m_unitsPerEm;
1231 const QGradient::Spread spread
1232 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1234 const QGradientStops gradientStops
1235 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1237 paintGraphRenderer->setRadialGradient(c0, r0, c1, r1, spread, gradientStops);
1238 paintGraphRenderer->drawCurrentPath();
1242 case DWRITE_PAINT_TYPE_SWEEP_GRADIENT:
1244 const QPointF center(paintElement->paint.sweepGradient.centerX * m_unitsPerEm,
1245 paintElement->paint.sweepGradient.centerY * m_unitsPerEm);
1246 const qreal startAngle = paintElement->paint.sweepGradient.startAngle;
1247 const qreal endAngle = paintElement->paint.sweepGradient.endAngle;
1249 const QGradient::Spread spread
1250 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1251 const QGradientStops gradientStops
1252 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1254 paintGraphRenderer->setConicalGradient(center,
1259 paintGraphRenderer->drawCurrentPath();
1263 case DWRITE_PAINT_TYPE_LINEAR_GRADIENT:
1265 const QPointF p0(paintElement->paint.linearGradient.x0 * m_unitsPerEm,
1266 paintElement->paint.linearGradient.y0 * m_unitsPerEm);
1267 const QPointF p1(paintElement->paint.linearGradient.x1 * m_unitsPerEm,
1268 paintElement->paint.linearGradient.y1 * m_unitsPerEm);
1269 const QPointF p2(paintElement->paint.linearGradient.x2 * m_unitsPerEm,
1270 paintElement->paint.linearGradient.y2 * m_unitsPerEm);
1272 const QGradient::Spread spread
1273 = extendToSpread(paintElement->paint.linearGradient.extendMode);
1274 const QGradientStops gradientStops
1275 = collectStops(paintElement->paint.linearGradient.gradientStopCount);
1277 paintGraphRenderer->setLinearGradient(p0, p1, p2, spread, gradientStops);
1278 paintGraphRenderer->drawCurrentPath();
1282 case DWRITE_PAINT_TYPE_GLYPH:
1284 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.glyph.glyphIndex);
1285 paintGraphRenderer->appendPath(glyphPath);
1286 if (!traverseChildren(1))
1291 case DWRITE_PAINT_TYPE_COLOR_GLYPH:
1293 D2D_RECT_F rect = paintElement->paint.colorGlyph.clipBox;
1294 QRect clipBox = QRectF(QPointF(rect.left, rect.top),
1295 QPointF(rect.right, rect.bottom)).toAlignedRect();
1296 if (!clipBox.isEmpty()) {
1297 QTransform coordinatesTransform;
1298 coordinatesTransform.scale(m_unitsPerEm, m_unitsPerEm);
1299 clipBox = coordinatesTransform.mapRect(clipBox);
1301 paintGraphRenderer->setClip(clipBox);
1304 DWRITE_PAINT_ELEMENT childElement;
1305 if (FAILED(paintReader->MoveToFirstChild(&childElement,
sizeof(DWRITE_PAINT_ELEMENT))))
1308 if (!traverseColr1(paintReader, face7, &childElement, paintGraphRenderer))
1311 if (FAILED(paintReader->MoveToParent()))
1317 case DWRITE_PAINT_TYPE_TRANSFORM:
1319 QTransform transform = matrixToTransform(paintElement->paint.transform, m_unitsPerEm);
1320 paintGraphRenderer->prependTransform(transform);
1321 if (!traverseChildren(1))
1328 qCDebug(lcColrv1) <<
"Unhandled paint graph node type" << paintElement->paintType;
1335bool QWindowsFontEngineDirectWrite::renderColr1GlyphRun(QImage *image,
1336 const DWRITE_GLYPH_RUN *glyphRun,
1337 const DWRITE_MATRIX &matrix,
1340 qCDebug(lcColrv1) <<
"renderColr1GlyphRun,"
1341 <<
"families:" << fontDef.families;
1342 ComPtr<IDWriteFontFace7> face7;
1343 HRESULT hr = m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace7),
1345 if (SUCCEEDED(hr)) {
1346 DWRITE_PAINT_FEATURE_LEVEL featureLevel =
1347 face7->GetPaintFeatureLevel(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE);
1348 if (featureLevel != DWRITE_PAINT_FEATURE_LEVEL_COLR_V1) {
1349 qCDebug(lcColrv1) <<
"Unsupported feature level:" << featureLevel;
1353 ComPtr<IDWritePaintReader> paintReader;
1354 hr = face7->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
1358 qErrnoWarning(hr,
"%s: CreatePaintReader failed",
__FUNCTION__);
1362 Q_ASSERT(glyphRun->glyphCount == 1);
1363 DWRITE_PAINT_ELEMENT paintElement;
1365 hr = paintReader->SetCurrentGlyph(glyphRun->glyphIndices[0],
1367 sizeof(DWRITE_PAINT_ELEMENT),
1371 qErrnoWarning(hr,
"%s: SetCurrentGlyph failed",
__FUNCTION__);
1375 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE) {
1376 qCDebug(lcColrv1) <<
"Glyph" << glyphRun->glyphIndices[0]
1377 <<
"does not have a paint graph";
1381 DWRITE_COLOR_F dwColor;
1382 dwColor.r = color.redF();
1383 dwColor.g = color.greenF();
1384 dwColor.b = color.blueF();
1385 dwColor.a = color.alphaF();
1386 paintReader->SetTextColor(dwColor);
1388 QRectF boundingRect = QRectF(QPointF(clipBox.left, clipBox.top),
1389 QPointF(clipBox.right, clipBox.bottom));
1390 if (boundingRect.isNull()) {
1391 QColrPaintGraphRenderer boundingRectCalculator;
1392 boundingRectCalculator.beginCalculateBoundingBox();
1393 if (traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &boundingRectCalculator))
1394 boundingRect = boundingRectCalculator.boundingRect();
1397 QTransform initialTransform;
1398 initialTransform.scale(fontDef.pixelSize, fontDef.pixelSize);
1399 boundingRect = initialTransform.mapRect(boundingRect);
1401 QTransform originalXform = matrixToTransform(matrix);
1402 boundingRect = originalXform.mapRect(boundingRect);
1404 qCDebug(lcColrv1).noquote() <<
"Bounds of"
1405 << glyphRun->glyphIndices[0]
1406 <<
" in device coordinates:"
1409 QColrPaintGraphRenderer graphRenderer;
1410 graphRenderer.setBoundingRect(boundingRect);
1411 graphRenderer.beginRender(fontDef.pixelSize / m_unitsPerEm, matrixToTransform(matrix));
1412 if (!traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &graphRenderer))
1415 *image = graphRenderer.endRender();
1417 qCDebug(lcColrv1) <<
"Font face does not support IDWriteFontFace7 interface";
1424QImage QWindowsFontEngineDirectWrite::renderColorGlyph(DWRITE_GLYPH_RUN *glyphRun,
1425 const DWRITE_MATRIX &transform,
1426 DWRITE_RENDERING_MODE renderMode,
1427 DWRITE_MEASURING_MODE measureMode,
1428 DWRITE_GRID_FIT_MODE gridFitMode,
1430 QRect boundingRect)
const
1434#if QT_CONFIG(directwritecolrv1)
1437 renderColr1GlyphRun(&ret, glyphRun, transform, color);
1440#if QT_CONFIG(directwrite3)
1444 ComPtr<IDWriteFactory4> factory4;
1445 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory4),
1447 if (SUCCEEDED(hr)) {
1448 const DWRITE_GLYPH_IMAGE_FORMATS supportedBitmapFormats =
1449 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_PNG
1450 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
1451 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF);
1453 const DWRITE_GLYPH_IMAGE_FORMATS glyphFormats =
1454 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_COLR
1455 | supportedBitmapFormats
1456 | DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE
1457 | DWRITE_GLYPH_IMAGE_FORMATS_CFF);
1459 ComPtr<IDWriteColorGlyphRunEnumerator1> enumerator;
1460 hr = factory4->TranslateColorGlyphRun(D2D1::Point2F(0.0f, 0.0f),
1469 while (SUCCEEDED(hr) && ok) {
1470 hr = enumerator->MoveNext(&ok);
1474 const DWRITE_COLOR_GLYPH_RUN1 *colorGlyphRun =
nullptr;
1475 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1477 qErrnoWarning(hr,
"%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed",
__FUNCTION__);
1481 if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_NONE) {
1485 }
else if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_COLR) {
1487 ret = QImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied);
1491 if (renderColr0GlyphRun(&ret,
1492 reinterpret_cast<
const DWRITE_COLOR_GLYPH_RUN *>(colorGlyphRun),
1501 }
else if (colorGlyphRun->glyphImageFormat & supportedBitmapFormats) {
1502 ComPtr<IDWriteFontFace4> face4;
1503 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
1505 DWRITE_GLYPH_IMAGE_DATA data;
1507 Q_ASSERT(glyphRun->glyphCount == 1);
1508 HRESULT hr = face4->GetGlyphImageData(glyphRun->glyphIndices[0],
1510 DWRITE_GLYPH_IMAGE_FORMATS(colorGlyphRun->glyphImageFormat & supportedBitmapFormats),
1514 qErrnoWarning(
"%s: GetGlyphImageData failed",
__FUNCTION__);
1518 auto fnc = qScopeGuard([&]() {
1519 if (data.imageData !=
nullptr)
1520 face4->ReleaseGlyphImageData(ctx);
1523 if (data.pixelsPerEm == 0 || data.imageData ==
nullptr || data.imageDataSize == 0) {
1524 qCWarning(lcQpaFonts) <<
__FUNCTION__
1525 <<
"Failed to retrieve image data for glyph"
1526 << glyphRun->glyphIndices[0]
1530 << colorGlyphRun->glyphImageFormat;
1533 switch (colorGlyphRun->glyphImageFormat) {
1534 case DWRITE_GLYPH_IMAGE_FORMATS_JPEG:
1537 case DWRITE_GLYPH_IMAGE_FORMATS_TIFF:
1545 ret = QImage::fromData(
reinterpret_cast<
const uchar *>(data.imageData),
1549 QTransform matrix(transform.m11, transform.m12,
1550 transform.m21, transform.m22,
1551 transform.dx, transform.dy);
1553 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
1554 matrix.scale(scale, scale);
1556 if (!matrix.isIdentity())
1557 ret = ret.transformed(matrix, Qt::SmoothTransformation);
1564 qCDebug(lcQpaFonts) <<
"Found glyph run with unsupported format"
1565 << colorGlyphRun->glyphImageFormat;
1574 ComPtr<IDWriteFactory2> factory2;
1575 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1580 ComPtr<IDWriteColorGlyphRunEnumerator> enumerator;
1581 hr = factory2->TranslateColorGlyphRun(0.0f,
1589 if (SUCCEEDED(hr)) {
1590 ret = QImage(boundingRect.size(), QImage::Format_ARGB32_Premultiplied);
1594 while (SUCCEEDED(hr) && ok) {
1595 hr = enumerator->MoveNext(&ok);
1597 qErrnoWarning(hr,
"%s: IDWriteColorGlyphRunEnumerator::MoveNext failed",
__FUNCTION__);
1602 const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun =
nullptr;
1603 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1605 qErrnoWarning(hr,
"%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed",
__FUNCTION__);
1609 if (!renderColr0GlyphRun(&ret,
1627QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
1628 const QFixedPoint &subPixelPosition,
1630 const QTransform &originalTransform,
1631 const QColor &color)
1633 UINT16 glyphIndex = t;
1634 FLOAT glyphAdvance = 0;
1636 DWRITE_GLYPH_OFFSET glyphOffset;
1637 glyphOffset.advanceOffset = 0;
1638 glyphOffset.ascenderOffset = 0;
1640 DWRITE_GLYPH_RUN glyphRun;
1641 glyphRun.fontFace = m_directWriteFontFace;
1642 glyphRun.fontEmSize = fontDef.pixelSize;
1643 glyphRun.glyphCount = 1;
1644 glyphRun.glyphIndices = &glyphIndex;
1645 glyphRun.glyphAdvances = &glyphAdvance;
1646 glyphRun.isSideways =
false;
1647 glyphRun.bidiLevel = 0;
1648 glyphRun.glyphOffsets = &glyphOffset;
1650 QTransform xform = originalTransform;
1651 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
1652 xform.scale(fontDef.stretch / 100.0, 1.0);
1654 DWRITE_MATRIX transform;
1655 transform.dx = subPixelPosition.x.toReal();
1657 transform.m11 = xform.m11();
1658 transform.m12 = xform.m12();
1659 transform.m21 = xform.m21();
1660 transform.m22 = xform.m22();
1662 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1663 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1664 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1665 ? DWRITE_GRID_FIT_MODE_DISABLED
1666 : DWRITE_GRID_FIT_MODE_DEFAULT;
1668 ComPtr<IDWriteFactory2> factory2;
1669 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1671 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1672 if (!SUCCEEDED(hr)) {
1673 qErrnoWarning(hr,
"%s: Failed to query IDWriteFactory2 interface.",
__FUNCTION__);
1674 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1684 hr = factory2->CreateGlyphRunAnalysis(
1690 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1696 if (SUCCEEDED(hr)) {
1697 QRect rect = paintGraphBounds(t, transform);
1699 rect = alphaTextureBounds(t, transform);
1701 rect = colorBitmapBounds(t, transform);
1703 if (rect.isEmpty()) {
1704 qCDebug(lcQpaFonts) <<
__FUNCTION__ <<
"Cannot get alpha texture bounds. Falling back to slower rendering path.";
1708 if (glyphBoundingBoxExceedsLimit(rect.width(),
1710 effectivePixelSizeSquared(xform))) {
1714 QRect boundingRect = rect.adjusted(-margin, -margin, margin, margin);
1717 if (glyphFormat == QFontEngine::Format_ARGB) {
1718 image = renderColorGlyph(&glyphRun,
1728 if (image.isNull()) {
1729 image = QImage(boundingRect.size(),
1730 glyphFormat == QFontEngine::Format_ARGB
1731 ? QImage::Format_ARGB32_Premultiplied
1732 : QImage::Format_RGB32);
1733 image.fill(image.format() == QImage::Format_ARGB32_Premultiplied ? 0x0 : 0xffffffff);
1736 if (glyphFormat == QFontEngine::Format_ARGB) {
1737 r =
float(color.redF());
1738 g =
float(color.greenF());
1739 b =
float(color.blueF());
1740 a =
float(color.alphaF());
1742 r = g = b = a = 0.0;
1745 renderGlyphRun(&image,
1750 glyphAnalysis.Get(),
1757 qErrnoWarning(hr,
"%s: CreateGlyphRunAnalysis failed",
__FUNCTION__);
1762void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
1767 IDWriteGlyphRunAnalysis *glyphAnalysis,
1768 const QRect &boundingRect,
1769 DWRITE_RENDERING_MODE renderMode)
const
1771 const int width = destination->width();
1772 const int height = destination->height();
1778 const int size = width * height * 3;
1781 rect.left = boundingRect.left();
1782 rect.top = boundingRect.top();
1783 rect.right = boundingRect.left() + boundingRect.width();
1784 rect.bottom = boundingRect.top() + boundingRect.height();
1786 QVarLengthArray<BYTE, 1024> alphaValueArray(size);
1787 BYTE *alphaValues = alphaValueArray.data();
1788 memset(alphaValues, 0, size);
1790 HRESULT hr = glyphAnalysis->CreateAlphaTexture(renderMode == DWRITE_RENDERING_MODE_ALIASED
1791 ? DWRITE_TEXTURE_ALIASED_1x1
1792 : DWRITE_TEXTURE_CLEARTYPE_3x1,
1796 if (SUCCEEDED(hr)) {
1797 if (destination->hasAlphaChannel()) {
1798 for (
int y = 0; y < height; ++y) {
1799 uint *dest =
reinterpret_cast<uint *>(destination->scanLine(y));
1800 BYTE *src = alphaValues + width * 3 * y;
1802 for (
int x = 0; x < width; ++x) {
1803 float redAlpha = a * *src++ / 255.0;
1804 float greenAlpha = a * *src++ / 255.0;
1805 float blueAlpha = a * *src++ / 255.0;
1806 float averageAlpha = (redAlpha + greenAlpha + blueAlpha) / 3.0;
1808 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1809 qSwap(redAlpha, blueAlpha);
1811 QRgb currentRgb = dest[x];
1812 dest[x] = qRgba(qRound(qRed(currentRgb) * (1.0 - averageAlpha) + averageAlpha * r),
1813 qRound(qGreen(currentRgb) * (1.0 - averageAlpha) + averageAlpha * g),
1814 qRound(qBlue(currentRgb) * (1.0 - averageAlpha) + averageAlpha * b),
1815 qRound(qAlpha(currentRgb) * (1.0 - averageAlpha) + averageAlpha * 255));
1818 }
else if (renderMode == DWRITE_RENDERING_MODE_ALIASED) {
1819 for (
int y = 0; y < height; ++y) {
1820 uint *dest =
reinterpret_cast<uint *>(destination->scanLine(y));
1821 BYTE *src = alphaValues + width * y;
1823 for (
int x = 0; x < width; ++x) {
1824 int alpha = *(src++);
1825 dest[x] = (alpha << 16) + (alpha << 8) + alpha;
1829 for (
int y = 0; y < height; ++y) {
1830 uint *dest =
reinterpret_cast<uint *>(destination->scanLine(y));
1831 BYTE *src = alphaValues + width * 3 * y;
1833 for (
int x = 0; x < width; ++x) {
1834 BYTE redAlpha = *(src + 0);
1835 BYTE greenAlpha = *(src + 1);
1836 BYTE blueAlpha = *(src + 2);
1838 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1839 qSwap(redAlpha, blueAlpha);
1841 dest[x] = qRgb(redAlpha, greenAlpha, blueAlpha);
1847 qErrnoWarning(
"%s: CreateAlphaTexture failed",
__FUNCTION__);
1852QImage QWindowsFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t,
1853 const QFixedPoint &subPixelPosition,
1854 const QTransform &xform)
1856 QImage mask = imageForGlyph(t,
1858 glyphMargin(QFontEngine::Format_A32),
1861 if (mask.isNull()) {
1862 if (xform.type() > QTransform::TxTranslate && glyphTransformExceedsLimit(t, xform))
1865 mask = QFontEngine::renderedPathForGlyph(t, Qt::white);
1866 if (!xform.isIdentity())
1867 mask = mask.transformed(xform);
1870 return mask.depth() == 32
1872 : mask.convertToFormat(QImage::Format_RGB32);
1875QFontEngine *QWindowsFontEngineDirectWrite::cloneWithSize(qreal pixelSize)
const
1877 QWindowsFontEngineDirectWrite *fontEngine =
new QWindowsFontEngineDirectWrite(m_directWriteFontFace,
1881 fontEngine->fontDef = fontDef;
1882 fontEngine->fontDef.pixelSize = pixelSize;
1883 if (!m_uniqueFamilyName.isEmpty()) {
1884 fontEngine->setUniqueFamilyName(m_uniqueFamilyName);
1885 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
1886 static_cast<QWindowsFontDatabase *>(pfdb)->refUniqueFont(m_uniqueFamilyName);
1892Qt::HANDLE QWindowsFontEngineDirectWrite::handle()
const
1894 return m_directWriteFontFace;
1897void QWindowsFontEngineDirectWrite::initFontInfo(
const QFontDef &request,
1902 if (fontDef.pointSize < 0)
1903 fontDef.pointSize = fontDef.pixelSize * 72. / dpi;
1904 else if (fontDef.pixelSize == -1)
1905 fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.);
1907 m_faceId.variableAxes = request.variableAxisValues;
1909#if QT_CONFIG(directwrite3)
1910 IDWriteFontFace3 *face3 =
nullptr;
1911 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace3),
1912 reinterpret_cast<
void **>(&face3)))) {
1914 fontDef.weight = QWindowsDirectWriteFontDatabase::fromDirectWriteWeight(face3->GetWeight());
1915 fontDef.style = QWindowsDirectWriteFontDatabase::fromDirectWriteStyle(face3->GetStyle());
1917 IDWriteLocalizedStrings *names;
1918 if (SUCCEEDED(face3->GetFaceNames(&names))) {
1919 wchar_t englishLocale[] = L"en-us";
1920 fontDef.styleName = QWindowsDirectWriteFontDatabase::localeString(names, englishLocale);
1925 if (face3->IsColorFont())
1926 glyphFormat = QFontEngine::Format_ARGB;
1933QString QWindowsFontEngineDirectWrite::fontNameSubstitute(
const QString &familyName)
1935 const QString substitute =
1936 QWinRegistryKey(HKEY_LOCAL_MACHINE,
1937 LR"(Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes)")
1938 .stringValue(familyName);
1939 return substitute.isEmpty() ? familyName : substitute;
1942QRect QWindowsFontEngineDirectWrite::alphaTextureBounds(glyph_t glyph,
1943 const DWRITE_MATRIX &transform)
1945 UINT16 glyphIndex = glyph;
1946 FLOAT glyphAdvance = 0;
1948 DWRITE_GLYPH_OFFSET glyphOffset;
1949 glyphOffset.advanceOffset = 0;
1950 glyphOffset.ascenderOffset = 0;
1952 DWRITE_GLYPH_RUN glyphRun;
1953 glyphRun.fontFace = m_directWriteFontFace;
1954 glyphRun.fontEmSize = fontDef.pixelSize;
1955 glyphRun.glyphCount = 1;
1956 glyphRun.glyphIndices = &glyphIndex;
1957 glyphRun.glyphAdvances = &glyphAdvance;
1958 glyphRun.isSideways =
false;
1959 glyphRun.bidiLevel = 0;
1960 glyphRun.glyphOffsets = &glyphOffset;
1962 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1963 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1964 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1965 ? DWRITE_GRID_FIT_MODE_DISABLED
1966 : DWRITE_GRID_FIT_MODE_DEFAULT;
1968 ComPtr<IDWriteFactory2> factory2 =
nullptr;
1969 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1972 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1973 if (SUCCEEDED(hr)) {
1974 hr = factory2->CreateGlyphRunAnalysis(
1980 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1985 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1996 if (SUCCEEDED(hr)) {
1998 hr = glyphAnalysis->GetAlphaTextureBounds(renderMode == DWRITE_RENDERING_MODE_ALIASED
1999 ? DWRITE_TEXTURE_ALIASED_1x1
2000 : DWRITE_TEXTURE_CLEARTYPE_3x1,
2002 if (FAILED(hr) || rect.left == rect.right || rect.top == rect.bottom)
2005 return QRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
2011QRect QWindowsFontEngineDirectWrite::colorBitmapBounds(glyph_t glyph,
const DWRITE_MATRIX &transform)
2013#if QT_CONFIG(directwrite3)
2014 ComPtr<IDWriteFontFace4> face4;
2015 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
2017 DWRITE_GLYPH_IMAGE_FORMATS formats = face4->GetGlyphImageFormats();
2019 const DWRITE_GLYPH_IMAGE_FORMATS supportedBitmapFormats =
2020 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_PNG
2021 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
2022 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF);
2024 if (formats & supportedBitmapFormats) {
2025 DWRITE_GLYPH_IMAGE_DATA data;
2027 HRESULT hr = face4->GetGlyphImageData(glyph,
2029 DWRITE_GLYPH_IMAGE_FORMATS(formats & supportedBitmapFormats),
2033 qErrnoWarning(
"%s: GetGlyphImageData failed",
__FUNCTION__);
2037 auto fnc = qScopeGuard([&]() {
2038 if (data.imageData !=
nullptr)
2039 face4->ReleaseGlyphImageData(ctx);
2042 if (data.pixelsPerEm == 0 || data.imageData ==
nullptr || data.imageDataSize == 0)
2045 QRect rect(-data.horizontalLeftOrigin.x,
2046 -data.horizontalLeftOrigin.y,
2047 data.pixelSize.width,
2048 data.pixelSize.height);
2050 QTransform matrix(transform.m11, transform.m12,
2051 transform.m21, transform.m22,
2052 transform.dx, transform.dy);
2055 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
2056 matrix.scale(scale, scale);
2057 rect = matrix.mapRect(rect);
2066 Q_UNUSED(transform);
2071glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph,
2072 const QFixedPoint &subPixelPosition,
2073 const QTransform &originalTransform,
2076 QTransform matrix = originalTransform;
2077 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
2078 matrix.scale(fontDef.stretch / 100.0, 1.0);
2080 glyph_metrics_t bbox = QFontEngine::boundingBox(glyph, matrix);
2082 DWRITE_MATRIX transform;
2083 transform.dx = subPixelPosition.x.toReal();
2085 transform.m11 = matrix.m11();
2086 transform.m12 = matrix.m12();
2087 transform.m21 = matrix.m21();
2088 transform.m22 = matrix.m22();
2091 QRect rect = paintGraphBounds(glyph, transform);
2095 rect = alphaTextureBounds(glyph, transform);
2099 rect = colorBitmapBounds(glyph, transform);
2105 int margin = glyphMargin(format);
2106 return glyph_metrics_t(rect.left(),
2108 rect.width() + margin * 2,
2109 rect.height() + margin * 2,
2110 bbox.xoff, bbox.yoff);
2113QImage QWindowsFontEngineDirectWrite::bitmapForGlyph(glyph_t glyph,
2114 const QFixedPoint &subPixelPosition,
2115 const QTransform &t,
2116 const QColor &color)
2118 return imageForGlyph(glyph, subPixelPosition, glyphMargin(QFontEngine::Format_ARGB), t, color);
2121QList<QFontVariableAxis> QWindowsFontEngineDirectWrite::variableAxes()
const
2123 return m_variableAxes;
2126bool QWindowsFontEngineDirectWrite::expectsGammaCorrectedBlending(QFontEngine::GlyphFormat format)
const
2128 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)