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.constData());
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.constData() + 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);
540 if (buffer && *length >= tableSize)
541 memcpy(buffer, tableData, tableSize);
543 Q_ASSERT(
int(*length) > 0);
545 m_directWriteFontFace->ReleaseFontTable(tableContext);
547 qErrnoWarning(
"%s: TryGetFontTable failed",
__FUNCTION__);
553QFixed QWindowsFontEngineDirectWrite::emSquareSize()
const
558glyph_t QWindowsFontEngineDirectWrite::glyphIndex(uint ucs4)
const
562 HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(&ucs4, 1, &glyphIndex);
564 qErrnoWarning(
"%s: glyphIndex failed",
__FUNCTION__);
571int QWindowsFontEngineDirectWrite::stringToCMap(
const QChar *str,
int len, QGlyphLayout *glyphs,
572 int *nglyphs, QFontEngine::ShaperFlags flags)
const
574 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
575 if (*nglyphs < len) {
580 QVarLengthArray<UINT32> codePoints(len);
581 int actualLength = 0;
582 QStringIterator it(str, str + len);
584 codePoints[actualLength++] = it.next();
586 QVarLengthArray<UINT16> glyphIndices(actualLength);
587 HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(codePoints.data(), actualLength,
588 glyphIndices.data());
590 qErrnoWarning(
"%s: GetGlyphIndicesW failed",
__FUNCTION__);
594 int mappedGlyphs = 0;
595 for (
int i = 0; i < actualLength; ++i) {
596 glyphs->glyphs[i] = glyphIndices.at(i);
597 if (glyphs->glyphs[i] != 0 || isIgnorableChar(codePoints.at(i)))
601 *nglyphs = actualLength;
602 glyphs->numGlyphs = actualLength;
604 if (!(flags & GlyphIndicesOnly))
605 recalcAdvances(glyphs, {});
610QFontEngine::FaceId QWindowsFontEngineDirectWrite::faceId()
const
615void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags shaperFlags)
const
617 QVarLengthArray<UINT16> glyphIndices(glyphs->numGlyphs);
620 for(
int i=0; i<glyphs->numGlyphs; i++)
621 glyphIndices[i] = UINT16(glyphs->glyphs[i]);
623 QVarLengthArray<DWRITE_GLYPH_METRICS> glyphMetrics(glyphIndices.size());
626 QFont::HintingPreference hint = determineHinting(fontDef);
627 bool needsDesignMetrics = shaperFlags & QFontEngine::DesignMetrics;
628 if (!needsDesignMetrics && hint == QFont::PreferFullHinting) {
629 const DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
630 const bool needsNaturalMetrics = renderMode == DWRITE_RENDERING_MODE_NATURAL
631 || renderMode == DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
633 hr = m_directWriteFontFace->GetGdiCompatibleGlyphMetrics(
float(fontDef.pixelSize),
639 glyphMetrics.data());
641 hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(),
643 glyphMetrics.data());
646 qreal stretch = fontDef.stretch != QFont::AnyStretch ? fontDef.stretch / 100.0 : 1.0;
647 for (
int i = 0; i < glyphs->numGlyphs; ++i)
650 qErrnoWarning(
"%s: GetDesignGlyphMetrics failed",
__FUNCTION__);
654QPainterPath QWindowsFontEngineDirectWrite::unscaledGlyph(glyph_t glyph)
const
656 float advance = 0.0f;
658 DWRITE_GLYPH_OFFSET offset;
659 offset.advanceOffset = 0;
660 offset.ascenderOffset = 0;
663 GeometrySink geometrySink(&ret);
664 HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(m_unitsPerEm,
673 qErrnoWarning(
"%s: GetGlyphRunOutline failed",
__FUNCTION__);
678void QWindowsFontEngineDirectWrite::getUnscaledGlyph(glyph_t glyph,
680 glyph_metrics_t *metric)
682 *path = unscaledGlyph(glyph);
685 DWRITE_GLYPH_METRICS glyphMetrics;
686 HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&g, 1, &glyphMetrics);
688 qErrnoWarning(
"%s: GetDesignGlyphMetrics failed",
__FUNCTION__);
692 QFixed advanceWidth = QFixed(
int(glyphMetrics.advanceWidth));
693 QFixed leftSideBearing = QFixed(glyphMetrics.leftSideBearing);
694 QFixed rightSideBearing = QFixed(glyphMetrics.rightSideBearing);
695 QFixed advanceHeight = QFixed(
int(glyphMetrics.advanceHeight));
696 QFixed verticalOriginY = QFixed(glyphMetrics.verticalOriginY);
697 QFixed topSideBearing = QFixed(glyphMetrics.topSideBearing);
698 QFixed bottomSideBearing = QFixed(glyphMetrics.bottomSideBearing);
699 QFixed width = advanceWidth - leftSideBearing - rightSideBearing;
700 QFixed height = advanceHeight - topSideBearing - bottomSideBearing;
701 *metric = glyph_metrics_t(leftSideBearing,
702 -verticalOriginY + topSideBearing,
709void QWindowsFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions,
int nglyphs,
710 QPainterPath *path, QTextItem::RenderFlags flags)
713 QVarLengthArray<UINT16> glyphIndices(nglyphs);
714 QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(nglyphs);
715 QVarLengthArray<FLOAT> glyphAdvances(nglyphs);
717 for (
int i=0; i<nglyphs; ++i) {
718 glyphIndices[i] = glyphs[i];
719 glyphOffsets[i].advanceOffset = positions[i].x.toReal();
720 glyphOffsets[i].ascenderOffset = -positions[i].y.toReal();
721 glyphAdvances[i] = 0.0;
724 GeometrySink geometrySink(path);
725 HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(
728 glyphAdvances.data(),
737 qErrnoWarning(
"%s: GetGlyphRunOutline failed",
__FUNCTION__);
740glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(
const QGlyphLayout &glyphs)
742 if (glyphs.numGlyphs == 0)
743 return glyph_metrics_t();
745 for (
int i = 0; i < glyphs.numGlyphs; ++i)
746 w += glyphs.effectiveAdvance(i);
748 const QFixed leftBearing = firstLeftBearing(glyphs);
749 return glyph_metrics_t(leftBearing, -ascent(), w - leftBearing - lastRightBearing(glyphs),
750 ascent() + descent(), w, 0);
753glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(glyph_t g)
755 UINT16 glyphIndex = g;
757 DWRITE_GLYPH_METRICS glyphMetrics;
758 HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics);
767 QFixed width = advanceWidth - leftSideBearing - rightSideBearing;
768 QFixed height = advanceHeight - topSideBearing - bottomSideBearing;
769 return glyph_metrics_t(leftSideBearing,
770 -verticalOriginY + topSideBearing,
776 qErrnoWarning(
"%s: GetDesignGlyphMetrics failed",
__FUNCTION__);
779 return glyph_metrics_t();
782QFixed QWindowsFontEngineDirectWrite::capHeight()
const
784 if (m_capHeight <= 0)
785 return calculatedCapHeight();
790QFixed QWindowsFontEngineDirectWrite::xHeight()
const
795qreal QWindowsFontEngineDirectWrite::maxCharWidth()
const
797 return m_maxAdvanceWidth.toReal();
800QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
801 const QFixedPoint &subPixelPosition,
804 QImage im = imageForGlyph(glyph, subPixelPosition, glyphMargin(Format_A8), t);
807 QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
809 for (
int y=0; y<im.height(); ++y) {
810 const uint *src =
reinterpret_cast<
const uint *>(im.constScanLine(y));
811 uchar *dst = alphaMap.scanLine(y);
812 for (
int x=0; x<im.width(); ++x) {
821 return QFontEngine::alphaMapForGlyph(glyph, t);
825QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
826 const QFixedPoint &subPixelPosition)
828 return alphaMapForGlyph(glyph, subPixelPosition, QTransform());
831bool QWindowsFontEngineDirectWrite::supportsHorizontalSubPixelPositions()
const
833 QFont::HintingPreference hinting = determineHinting(fontDef);
834 return (hinting != QFont::PreferFullHinting && !(fontDef.styleStrategy & QFont::NoAntialias));
837QFontEngine::Properties QWindowsFontEngineDirectWrite::properties()
const
839 IDWriteFontFace2 *directWriteFontFace2;
840 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace2),
841 reinterpret_cast<
void **>(&directWriteFontFace2)))) {
842 DWRITE_FONT_METRICS1 metrics;
843 directWriteFontFace2->GetMetrics(&metrics);
845 Properties p = QFontEngine::properties();
846 p.emSquare = metrics.designUnitsPerEm;
847 p.boundingBox = QRectF(metrics.glyphBoxLeft,
848 -metrics.glyphBoxTop,
849 metrics.glyphBoxRight - metrics.glyphBoxLeft,
850 metrics.glyphBoxTop - metrics.glyphBoxBottom);
851 p.ascent = metrics.ascent;
852 p.descent = metrics.descent;
853 p.leading = metrics.lineGap;
854 p.capHeight = metrics.capHeight;
855 p.lineWidth = metrics.underlineThickness;
857 directWriteFontFace2->Release();
860 return QFontEngine::properties();
864bool QWindowsFontEngineDirectWrite::renderColr0GlyphRun(QImage *image,
865 const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun,
866 const DWRITE_MATRIX &transform,
867 DWRITE_RENDERING_MODE renderMode,
868 DWRITE_MEASURING_MODE measureMode,
869 DWRITE_GRID_FIT_MODE gridFitMode,
871 QRect boundingRect)
const
873 ComPtr<IDWriteFactory2> factory2;
874 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
879 ComPtr<IDWriteGlyphRunAnalysis> colorGlyphsAnalysis;
880 hr = factory2->CreateGlyphRunAnalysis(
881 &colorGlyphRun->glyphRun,
886 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
892 qErrnoWarning(hr,
"%s: CreateGlyphRunAnalysis failed for color run",
__FUNCTION__);
897 if (colorGlyphRun->paletteIndex == 0xFFFF) {
898 r =
float(color.redF());
899 g =
float(color.greenF());
900 b =
float(color.blueF());
903 r = qBound(0.0f, colorGlyphRun->runColor.r, 1.0f);
904 g = qBound(0.0f, colorGlyphRun->runColor.g, 1.0f);
905 b = qBound(0.0f, colorGlyphRun->runColor.b, 1.0f);
906 a = qBound(0.0f, colorGlyphRun->runColor.a, 1.0f);
909 if (!qFuzzyIsNull(a))
910 renderGlyphRun(image, r, g, b, a, colorGlyphsAnalysis.Get(), boundingRect, renderMode);
915#if QT_CONFIG(directwritecolrv1)
916static inline QTransform matrixToTransform(
const DWRITE_MATRIX &matrix,
919 return QTransform(matrix.m11, matrix.m12,
920 matrix.m21, matrix.m22,
921 matrix.dx * unitsPerEm, matrix.dy * unitsPerEm);
924static inline QColor colorToColor(
const DWRITE_COLOR_F &color,
float alphaMultiplier = 1.0)
926 return QColor::fromRgbF(color.r, color.g, color.b, color.a * alphaMultiplier);
929static inline QGradient::Spread extendToSpread(UINT32 extendMode)
931 switch (extendMode) {
932 case D2D1_EXTEND_MODE_WRAP:
return QGradient::RepeatSpread;
933 case D2D1_EXTEND_MODE_MIRROR:
return QGradient::ReflectSpread;
934 default:
return QGradient::PadSpread;
938static inline QPainter::CompositionMode compositeToCompositionMode(DWRITE_COLOR_COMPOSITE_MODE mode)
941 case DWRITE_COLOR_COMPOSITE_CLEAR:
942 return QPainter::CompositionMode_Clear;
944 case DWRITE_COLOR_COMPOSITE_SRC:
945 return QPainter::CompositionMode_Source;
947 case DWRITE_COLOR_COMPOSITE_DEST:
948 return QPainter::CompositionMode_Destination;
950 case DWRITE_COLOR_COMPOSITE_SRC_OVER:
951 return QPainter::CompositionMode_SourceOver;
953 case DWRITE_COLOR_COMPOSITE_DEST_OVER:
954 return QPainter::CompositionMode_DestinationOver;
956 case DWRITE_COLOR_COMPOSITE_SRC_IN:
957 return QPainter::CompositionMode_SourceIn;
959 case DWRITE_COLOR_COMPOSITE_DEST_IN:
960 return QPainter::CompositionMode_DestinationIn;
962 case DWRITE_COLOR_COMPOSITE_SRC_OUT:
963 return QPainter::CompositionMode_SourceOut;
965 case DWRITE_COLOR_COMPOSITE_DEST_OUT:
966 return QPainter::CompositionMode_DestinationOut;
968 case DWRITE_COLOR_COMPOSITE_SRC_ATOP:
969 return QPainter::CompositionMode_SourceAtop;
971 case DWRITE_COLOR_COMPOSITE_DEST_ATOP:
972 return QPainter::CompositionMode_DestinationAtop;
974 case DWRITE_COLOR_COMPOSITE_XOR:
975 return QPainter::CompositionMode_Xor;
977 case DWRITE_COLOR_COMPOSITE_PLUS:
978 return QPainter::CompositionMode_Plus;
980 case DWRITE_COLOR_COMPOSITE_SCREEN:
981 return QPainter::CompositionMode_Screen;
983 case DWRITE_COLOR_COMPOSITE_OVERLAY:
984 return QPainter::CompositionMode_Overlay;
986 case DWRITE_COLOR_COMPOSITE_DARKEN:
987 return QPainter::CompositionMode_Darken;
989 case DWRITE_COLOR_COMPOSITE_LIGHTEN:
990 return QPainter::CompositionMode_Lighten;
992 case DWRITE_COLOR_COMPOSITE_COLOR_DODGE:
993 return QPainter::CompositionMode_ColorDodge;
995 case DWRITE_COLOR_COMPOSITE_COLOR_BURN:
996 return QPainter::CompositionMode_ColorBurn;
998 case DWRITE_COLOR_COMPOSITE_HARD_LIGHT:
999 return QPainter::CompositionMode_HardLight;
1001 case DWRITE_COLOR_COMPOSITE_SOFT_LIGHT:
1002 return QPainter::CompositionMode_SoftLight;
1004 case DWRITE_COLOR_COMPOSITE_DIFFERENCE:
1005 return QPainter::CompositionMode_Difference;
1007 case DWRITE_COLOR_COMPOSITE_EXCLUSION:
1008 return QPainter::CompositionMode_Exclusion;
1010 case DWRITE_COLOR_COMPOSITE_MULTIPLY:
1011 return QPainter::CompositionMode_Multiply;
1014 qCWarning(lcColrv1) <<
"Unhandled color composite mode:" << mode;
1015 return QPainter::CompositionMode_SourceOver;
1020QRect QWindowsFontEngineDirectWrite::paintGraphBounds(glyph_t glyph,
1021 const DWRITE_MATRIX &matrix)
const
1023#if QT_CONFIG(directwritecolrv1)
1024 ComPtr<IDWriteFontFace7> face7;
1025 HRESULT hr = m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace7),
1027 if (SUCCEEDED(hr)) {
1028 DWRITE_PAINT_FEATURE_LEVEL featureLevel = face7->GetPaintFeatureLevel(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE);
1029 if (featureLevel != DWRITE_PAINT_FEATURE_LEVEL_COLR_V1)
1032 ComPtr<IDWritePaintReader> paintReader;
1033 hr = face7->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
1037 qErrnoWarning(hr,
"%s: CreatePaintReader failed",
__FUNCTION__);
1041 DWRITE_PAINT_ELEMENT paintElement;
1043 hr = paintReader->SetCurrentGlyph(glyph,
1045 sizeof(paintElement),
1050 qErrnoWarning(hr,
"%s: SetCurrentGlyph failed",
__FUNCTION__);
1054 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE)
1057 QRectF boundingRect = QRectF(QPointF(clipBox.left, clipBox.top),
1058 QPointF(clipBox.right, clipBox.bottom));
1059 if (boundingRect.isNull()) {
1060 QColrPaintGraphRenderer boundingRectCalculator;
1061 boundingRectCalculator.beginCalculateBoundingBox();
1062 if (traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &boundingRectCalculator))
1063 boundingRect = boundingRectCalculator.boundingRect();
1066 QTransform initialTransform;
1067 initialTransform.scale(fontDef.pixelSize, fontDef.pixelSize);
1068 boundingRect = initialTransform.mapRect(boundingRect);
1070 QTransform originalXform = matrixToTransform(matrix);
1071 boundingRect = originalXform.mapRect(boundingRect);
1073 return boundingRect.toAlignedRect();
1075 qCDebug(lcColrv1) <<
"Font face does not support IDWriteFontFace7 interface";
1085#if QT_CONFIG(directwritecolrv1)
1086bool QWindowsFontEngineDirectWrite::traverseColr1(IDWritePaintReader *paintReader,
1087 IDWriteFontFace7 *face7,
1088 const DWRITE_PAINT_ELEMENT *paintElement,
1089 QColrPaintGraphRenderer *paintGraphRenderer)
const
1091 paintGraphRenderer->save();
1092 auto cleanup = qScopeGuard([&paintGraphRenderer]() {
1093 paintGraphRenderer->restore();
1096 auto traverseChildren = [&](quint32 childCount) {
1097 DWRITE_PAINT_ELEMENT childPaintElement;
1098 if (FAILED(paintReader->MoveToFirstChild(&childPaintElement,
sizeof(DWRITE_PAINT_ELEMENT))))
1101 while (childCount-- > 0) {
1102 traverseColr1(paintReader, face7, &childPaintElement, paintGraphRenderer);
1103 if (childCount > 0) {
1104 if (FAILED(paintReader->MoveToNextSibling(&childPaintElement,
sizeof(DWRITE_PAINT_ELEMENT)))) {
1110 return SUCCEEDED(paintReader->MoveToParent());
1113 auto collectStops = [&paintReader](
int stopCount) {
1116 QVarLengthArray<D2D1_GRADIENT_STOP> stops(stopCount);
1117 HRESULT hr = paintReader->GetGradientStops(0, stopCount, stops.data());
1121 for (
int i = 0; i < stopCount; ++i) {
1122 const D2D1_GRADIENT_STOP &stop = stops[i];
1123 QColor color = QColor::fromRgbF(stop.color.r, stop.color.g, stop.color.b, stop.color.a);
1124 ret.append({stop.position, color});
1130 switch (paintElement->paintType) {
1131 case DWRITE_PAINT_TYPE_LAYERS:
1133 if (!traverseChildren(paintElement->paint.layers.childCount))
1139 case DWRITE_PAINT_TYPE_SOLID_GLYPH:
1141 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.solidGlyph.glyphIndex);
1143 QColor color = colorToColor(paintElement->paint.solidGlyph.color.value);
1144 paintGraphRenderer->setPath(glyphPath);
1145 paintGraphRenderer->setSolidColor(color);
1146 paintGraphRenderer->drawCurrentPath();
1151 case DWRITE_PAINT_TYPE_SOLID:
1153 QColor color = colorToColor(paintElement->paint.solid.value,
1154 paintElement->paint.solid.alphaMultiplier);
1155 paintGraphRenderer->setSolidColor(color);
1156 paintGraphRenderer->drawCurrentPath();
1160 case DWRITE_PAINT_TYPE_COMPOSITE:
1162 if (!paintGraphRenderer->isRendering()) {
1163 traverseChildren(2);
1165 DWRITE_PAINT_ELEMENT childElement;
1167 HRESULT hr = paintReader->MoveToFirstChild(&childElement,
sizeof(DWRITE_PAINT_ELEMENT));
1169 qErrnoWarning(hr,
"%s: Cannot move to first child of composite node",
1175 hr = paintReader->MoveToNextSibling(&childElement,
sizeof(DWRITE_PAINT_ELEMENT));
1177 qErrnoWarning(hr,
"%s: Cannot move to second child of composite node",
1182 DWRITE_COLOR_COMPOSITE_MODE compositeMode = paintElement->paint.composite.mode;
1184 QColrPaintGraphRenderer compositeRenderer;
1185 compositeRenderer.setBoundingRect(paintGraphRenderer->boundingRect());
1186 compositeRenderer.beginRender(fontDef.pixelSize / m_unitsPerEm,
1187 paintGraphRenderer->currentTransform());
1188 if (!traverseColr1(paintReader, face7, &childElement, &compositeRenderer))
1192 hr = paintReader->MoveToParent();
1194 qErrnoWarning(hr,
"%s: Cannot move back to parent composite node",
1199 hr = paintReader->MoveToFirstChild(&childElement,
sizeof(DWRITE_PAINT_ELEMENT));
1201 qErrnoWarning(hr,
"%s: Cannot move to first child of composite node",
1206 compositeRenderer.setCompositionMode(compositeToCompositionMode(compositeMode));
1207 if (!traverseColr1(paintReader, face7, &childElement, &compositeRenderer))
1210 hr = paintReader->MoveToParent();
1212 qErrnoWarning(hr,
"%s: Cannot move back to parent composite node",
1217 paintGraphRenderer->drawImage(compositeRenderer.endRender());
1223 case DWRITE_PAINT_TYPE_RADIAL_GRADIENT:
1225 const QPointF c0(paintElement->paint.radialGradient.x0 * m_unitsPerEm,
1226 paintElement->paint.radialGradient.y0 * m_unitsPerEm);
1227 const QPointF c1(paintElement->paint.radialGradient.x1 * m_unitsPerEm,
1228 paintElement->paint.radialGradient.y1 * m_unitsPerEm);
1229 const qreal r0 = paintElement->paint.radialGradient.radius0 * m_unitsPerEm;
1230 const qreal r1 = paintElement->paint.radialGradient.radius1 * m_unitsPerEm;
1232 const QGradient::Spread spread
1233 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1235 const QGradientStops gradientStops
1236 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1238 paintGraphRenderer->setRadialGradient(c0, r0, c1, r1, spread, gradientStops);
1239 paintGraphRenderer->drawCurrentPath();
1243 case DWRITE_PAINT_TYPE_SWEEP_GRADIENT:
1245 const QPointF center(paintElement->paint.sweepGradient.centerX * m_unitsPerEm,
1246 paintElement->paint.sweepGradient.centerY * m_unitsPerEm);
1247 const qreal startAngle = paintElement->paint.sweepGradient.startAngle;
1248 const qreal endAngle = paintElement->paint.sweepGradient.endAngle;
1250 const QGradient::Spread spread
1251 = extendToSpread(paintElement->paint.radialGradient.extendMode);
1252 const QGradientStops gradientStops
1253 = collectStops(paintElement->paint.radialGradient.gradientStopCount);
1255 paintGraphRenderer->setConicalGradient(center,
1260 paintGraphRenderer->drawCurrentPath();
1264 case DWRITE_PAINT_TYPE_LINEAR_GRADIENT:
1266 const QPointF p0(paintElement->paint.linearGradient.x0 * m_unitsPerEm,
1267 paintElement->paint.linearGradient.y0 * m_unitsPerEm);
1268 const QPointF p1(paintElement->paint.linearGradient.x1 * m_unitsPerEm,
1269 paintElement->paint.linearGradient.y1 * m_unitsPerEm);
1270 const QPointF p2(paintElement->paint.linearGradient.x2 * m_unitsPerEm,
1271 paintElement->paint.linearGradient.y2 * m_unitsPerEm);
1273 const QGradient::Spread spread
1274 = extendToSpread(paintElement->paint.linearGradient.extendMode);
1275 const QGradientStops gradientStops
1276 = collectStops(paintElement->paint.linearGradient.gradientStopCount);
1278 paintGraphRenderer->setLinearGradient(p0, p1, p2, spread, gradientStops);
1279 paintGraphRenderer->drawCurrentPath();
1283 case DWRITE_PAINT_TYPE_GLYPH:
1285 QPainterPath glyphPath = unscaledGlyph(paintElement->paint.glyph.glyphIndex);
1286 paintGraphRenderer->appendPath(glyphPath);
1287 if (!traverseChildren(1))
1292 case DWRITE_PAINT_TYPE_COLOR_GLYPH:
1294 D2D_RECT_F rect = paintElement->paint.colorGlyph.clipBox;
1295 QRect clipBox = QRectF(QPointF(rect.left, rect.top),
1296 QPointF(rect.right, rect.bottom)).toAlignedRect();
1297 if (!clipBox.isEmpty()) {
1298 QTransform coordinatesTransform;
1299 coordinatesTransform.scale(m_unitsPerEm, m_unitsPerEm);
1300 clipBox = coordinatesTransform.mapRect(clipBox);
1302 paintGraphRenderer->setClip(clipBox);
1305 DWRITE_PAINT_ELEMENT childElement;
1306 if (FAILED(paintReader->MoveToFirstChild(&childElement,
sizeof(DWRITE_PAINT_ELEMENT))))
1309 if (!traverseColr1(paintReader, face7, &childElement, paintGraphRenderer))
1312 if (FAILED(paintReader->MoveToParent()))
1318 case DWRITE_PAINT_TYPE_TRANSFORM:
1320 QTransform transform = matrixToTransform(paintElement->paint.transform, m_unitsPerEm);
1321 paintGraphRenderer->prependTransform(transform);
1322 if (!traverseChildren(1))
1329 qCDebug(lcColrv1) <<
"Unhandled paint graph node type" << paintElement->paintType;
1336bool QWindowsFontEngineDirectWrite::renderColr1GlyphRun(QImage *image,
1337 const DWRITE_GLYPH_RUN *glyphRun,
1338 const DWRITE_MATRIX &matrix,
1341 qCDebug(lcColrv1) <<
"renderColr1GlyphRun,"
1342 <<
"families:" << fontDef.families;
1343 ComPtr<IDWriteFontFace7> face7;
1344 HRESULT hr = m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace7),
1346 if (SUCCEEDED(hr)) {
1347 DWRITE_PAINT_FEATURE_LEVEL featureLevel =
1348 face7->GetPaintFeatureLevel(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE);
1349 if (featureLevel != DWRITE_PAINT_FEATURE_LEVEL_COLR_V1) {
1350 qCDebug(lcColrv1) <<
"Unsupported feature level:" << featureLevel;
1354 ComPtr<IDWritePaintReader> paintReader;
1355 hr = face7->CreatePaintReader(DWRITE_GLYPH_IMAGE_FORMATS_COLR_PAINT_TREE,
1359 qErrnoWarning(hr,
"%s: CreatePaintReader failed",
__FUNCTION__);
1363 Q_ASSERT(glyphRun->glyphCount == 1);
1364 DWRITE_PAINT_ELEMENT paintElement;
1366 hr = paintReader->SetCurrentGlyph(glyphRun->glyphIndices[0],
1368 sizeof(DWRITE_PAINT_ELEMENT),
1372 qErrnoWarning(hr,
"%s: SetCurrentGlyph failed",
__FUNCTION__);
1376 if (paintElement.paintType == DWRITE_PAINT_TYPE_NONE) {
1377 qCDebug(lcColrv1) <<
"Glyph" << glyphRun->glyphIndices[0]
1378 <<
"does not have a paint graph";
1382 DWRITE_COLOR_F dwColor;
1383 dwColor.r = color.redF();
1384 dwColor.g = color.greenF();
1385 dwColor.b = color.blueF();
1386 dwColor.a = color.alphaF();
1387 paintReader->SetTextColor(dwColor);
1389 QRectF boundingRect = QRectF(QPointF(clipBox.left, clipBox.top),
1390 QPointF(clipBox.right, clipBox.bottom));
1391 if (boundingRect.isNull()) {
1392 QColrPaintGraphRenderer boundingRectCalculator;
1393 boundingRectCalculator.beginCalculateBoundingBox();
1394 if (traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &boundingRectCalculator))
1395 boundingRect = boundingRectCalculator.boundingRect();
1398 QTransform initialTransform;
1399 initialTransform.scale(fontDef.pixelSize, fontDef.pixelSize);
1400 boundingRect = initialTransform.mapRect(boundingRect);
1402 QTransform originalXform = matrixToTransform(matrix);
1403 boundingRect = originalXform.mapRect(boundingRect);
1405 qCDebug(lcColrv1).noquote() <<
"Bounds of"
1406 << glyphRun->glyphIndices[0]
1407 <<
" in device coordinates:"
1410 QColrPaintGraphRenderer graphRenderer;
1411 graphRenderer.setBoundingRect(boundingRect);
1412 graphRenderer.beginRender(fontDef.pixelSize / m_unitsPerEm, matrixToTransform(matrix));
1413 if (!traverseColr1(paintReader.Get(), face7.Get(), &paintElement, &graphRenderer))
1416 *image = graphRenderer.endRender();
1418 qCDebug(lcColrv1) <<
"Font face does not support IDWriteFontFace7 interface";
1425QImage QWindowsFontEngineDirectWrite::renderColorGlyph(DWRITE_GLYPH_RUN *glyphRun,
1426 const DWRITE_MATRIX &transform,
1427 DWRITE_RENDERING_MODE renderMode,
1428 DWRITE_MEASURING_MODE measureMode,
1429 DWRITE_GRID_FIT_MODE gridFitMode,
1431 QRect boundingRect)
const
1435#if QT_CONFIG(directwritecolrv1)
1438 renderColr1GlyphRun(&ret, glyphRun, transform, color);
1441#if QT_CONFIG(directwrite3)
1445 ComPtr<IDWriteFactory4> factory4;
1446 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory4),
1448 if (SUCCEEDED(hr)) {
1449 const DWRITE_GLYPH_IMAGE_FORMATS supportedBitmapFormats =
1450 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_PNG
1451 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
1452 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF);
1454 const DWRITE_GLYPH_IMAGE_FORMATS glyphFormats =
1455 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_COLR
1456 | supportedBitmapFormats
1457 | DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE
1458 | DWRITE_GLYPH_IMAGE_FORMATS_CFF);
1460 ComPtr<IDWriteColorGlyphRunEnumerator1> enumerator;
1461 hr = factory4->TranslateColorGlyphRun(D2D1::Point2F(0.0f, 0.0f),
1470 while (SUCCEEDED(hr) && ok) {
1471 hr = enumerator->MoveNext(&ok);
1475 const DWRITE_COLOR_GLYPH_RUN1 *colorGlyphRun =
nullptr;
1476 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1478 qErrnoWarning(hr,
"%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed",
__FUNCTION__);
1482 if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_NONE) {
1486 }
else if (colorGlyphRun->glyphImageFormat == DWRITE_GLYPH_IMAGE_FORMATS_COLR) {
1488 ret = QImage(boundingRect.width() - 1,
1489 boundingRect.height() - 1,
1490 QImage::Format_ARGB32_Premultiplied);
1494 if (renderColr0GlyphRun(&ret,
1495 reinterpret_cast<
const DWRITE_COLOR_GLYPH_RUN *>(colorGlyphRun),
1504 }
else if (colorGlyphRun->glyphImageFormat & supportedBitmapFormats) {
1505 ComPtr<IDWriteFontFace4> face4;
1506 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
1508 DWRITE_GLYPH_IMAGE_DATA data;
1510 Q_ASSERT(glyphRun->glyphCount == 1);
1511 HRESULT hr = face4->GetGlyphImageData(glyphRun->glyphIndices[0],
1513 DWRITE_GLYPH_IMAGE_FORMATS(colorGlyphRun->glyphImageFormat & supportedBitmapFormats),
1517 qErrnoWarning(
"%s: GetGlyphImageData failed",
__FUNCTION__);
1521 auto fnc = qScopeGuard([&]() {
1522 if (data.imageData !=
nullptr)
1523 face4->ReleaseGlyphImageData(ctx);
1526 if (data.pixelsPerEm == 0 || data.imageData ==
nullptr || data.imageDataSize == 0) {
1527 qCWarning(lcQpaFonts) <<
__FUNCTION__
1528 <<
"Failed to retrieve image data for glyph"
1529 << glyphRun->glyphIndices[0]
1533 << colorGlyphRun->glyphImageFormat;
1536 switch (colorGlyphRun->glyphImageFormat) {
1537 case DWRITE_GLYPH_IMAGE_FORMATS_JPEG:
1540 case DWRITE_GLYPH_IMAGE_FORMATS_TIFF:
1548 ret = QImage::fromData(
reinterpret_cast<
const uchar *>(data.imageData),
1552 QTransform matrix(transform.m11, transform.m12,
1553 transform.m21, transform.m22,
1554 transform.dx, transform.dy);
1556 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
1557 matrix.scale(scale, scale);
1559 if (!matrix.isIdentity())
1560 ret = ret.transformed(matrix, Qt::SmoothTransformation);
1567 qCDebug(lcQpaFonts) <<
"Found glyph run with unsupported format"
1568 << colorGlyphRun->glyphImageFormat;
1577 ComPtr<IDWriteFactory2> factory2;
1578 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1583 ComPtr<IDWriteColorGlyphRunEnumerator> enumerator;
1584 hr = factory2->TranslateColorGlyphRun(0.0f,
1592 if (SUCCEEDED(hr)) {
1593 ret = QImage(boundingRect.width() - 1,
1594 boundingRect.height() - 1,
1595 QImage::Format_ARGB32_Premultiplied);
1599 while (SUCCEEDED(hr) && ok) {
1600 hr = enumerator->MoveNext(&ok);
1602 qErrnoWarning(hr,
"%s: IDWriteColorGlyphRunEnumerator::MoveNext failed",
__FUNCTION__);
1607 const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun =
nullptr;
1608 hr = enumerator->GetCurrentRun(&colorGlyphRun);
1610 qErrnoWarning(hr,
"%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed",
__FUNCTION__);
1614 if (!renderColr0GlyphRun(&ret,
1632QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
1633 const QFixedPoint &subPixelPosition,
1635 const QTransform &originalTransform,
1636 const QColor &color)
1638 UINT16 glyphIndex = t;
1639 FLOAT glyphAdvance = 0;
1641 DWRITE_GLYPH_OFFSET glyphOffset;
1642 glyphOffset.advanceOffset = 0;
1643 glyphOffset.ascenderOffset = 0;
1645 DWRITE_GLYPH_RUN glyphRun;
1646 glyphRun.fontFace = m_directWriteFontFace;
1647 glyphRun.fontEmSize = fontDef.pixelSize;
1648 glyphRun.glyphCount = 1;
1649 glyphRun.glyphIndices = &glyphIndex;
1650 glyphRun.glyphAdvances = &glyphAdvance;
1651 glyphRun.isSideways =
false;
1652 glyphRun.bidiLevel = 0;
1653 glyphRun.glyphOffsets = &glyphOffset;
1655 QTransform xform = originalTransform;
1656 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
1657 xform.scale(fontDef.stretch / 100.0, 1.0);
1659 DWRITE_MATRIX transform;
1660 transform.dx = subPixelPosition.x.toReal();
1662 transform.m11 = xform.m11();
1663 transform.m12 = xform.m12();
1664 transform.m21 = xform.m21();
1665 transform.m22 = xform.m22();
1667 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1668 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1669 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1670 ? DWRITE_GRID_FIT_MODE_DISABLED
1671 : DWRITE_GRID_FIT_MODE_DEFAULT;
1673 ComPtr<IDWriteFactory2> factory2;
1674 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1676 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1677 if (!SUCCEEDED(hr)) {
1678 qErrnoWarning(hr,
"%s: Failed to query IDWriteFactory2 interface.",
__FUNCTION__);
1679 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1689 hr = factory2->CreateGlyphRunAnalysis(
1695 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1701 if (SUCCEEDED(hr)) {
1702 QRect rect = paintGraphBounds(t, transform);
1704 rect = alphaTextureBounds(t, transform);
1706 rect = colorBitmapBounds(t, transform);
1708 if (rect.isEmpty()) {
1709 qCDebug(lcQpaFonts) <<
__FUNCTION__ <<
"Cannot get alpha texture bounds. Falling back to slower rendering path.";
1713 QRect boundingRect = QRect(QPoint(rect.left() - margin,
1714 rect.top() - margin),
1715 QPoint(rect.right() + margin,
1716 rect.bottom() + margin));
1719 if (glyphFormat == QFontEngine::Format_ARGB) {
1720 image = renderColorGlyph(&glyphRun,
1730 if (image.isNull()) {
1732 image = QImage(boundingRect.width() - 1,
1733 boundingRect.height() - 1,
1734 glyphFormat == QFontEngine::Format_ARGB
1735 ? QImage::Format_ARGB32_Premultiplied
1736 : QImage::Format_RGB32);
1737 image.fill(image.format() == QImage::Format_ARGB32_Premultiplied ? 0x0 : 0xffffffff);
1740 if (glyphFormat == QFontEngine::Format_ARGB) {
1741 r =
float(color.redF());
1742 g =
float(color.greenF());
1743 b =
float(color.blueF());
1744 a =
float(color.alphaF());
1746 r = g = b = a = 0.0;
1749 renderGlyphRun(&image,
1754 glyphAnalysis.Get(),
1761 qErrnoWarning(hr,
"%s: CreateGlyphRunAnalysis failed",
__FUNCTION__);
1766void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
1771 IDWriteGlyphRunAnalysis *glyphAnalysis,
1772 const QRect &boundingRect,
1773 DWRITE_RENDERING_MODE renderMode)
const
1775 const int width = destination->width();
1776 const int height = destination->height();
1782 const int size = width * height * 3;
1785 rect.left = boundingRect.left();
1786 rect.top = boundingRect.top();
1787 rect.right = boundingRect.right();
1788 rect.bottom = boundingRect.bottom();
1790 QVarLengthArray<BYTE, 1024> alphaValueArray(size);
1791 BYTE *alphaValues = alphaValueArray.data();
1792 memset(alphaValues, 0, size);
1794 HRESULT hr = glyphAnalysis->CreateAlphaTexture(renderMode == DWRITE_RENDERING_MODE_ALIASED
1795 ? DWRITE_TEXTURE_ALIASED_1x1
1796 : DWRITE_TEXTURE_CLEARTYPE_3x1,
1800 if (SUCCEEDED(hr)) {
1801 if (destination->hasAlphaChannel()) {
1802 for (
int y = 0; y < height; ++y) {
1803 uint *dest =
reinterpret_cast<uint *>(destination->scanLine(y));
1804 BYTE *src = alphaValues + width * 3 * y;
1806 for (
int x = 0; x < width; ++x) {
1807 float redAlpha = a * *src++ / 255.0;
1808 float greenAlpha = a * *src++ / 255.0;
1809 float blueAlpha = a * *src++ / 255.0;
1810 float averageAlpha = (redAlpha + greenAlpha + blueAlpha) / 3.0;
1812 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1813 qSwap(redAlpha, blueAlpha);
1815 QRgb currentRgb = dest[x];
1816 dest[x] = qRgba(qRound(qRed(currentRgb) * (1.0 - averageAlpha) + averageAlpha * r),
1817 qRound(qGreen(currentRgb) * (1.0 - averageAlpha) + averageAlpha * g),
1818 qRound(qBlue(currentRgb) * (1.0 - averageAlpha) + averageAlpha * b),
1819 qRound(qAlpha(currentRgb) * (1.0 - averageAlpha) + averageAlpha * 255));
1822 }
else if (renderMode == DWRITE_RENDERING_MODE_ALIASED) {
1823 for (
int y = 0; y < height; ++y) {
1824 uint *dest =
reinterpret_cast<uint *>(destination->scanLine(y));
1825 BYTE *src = alphaValues + width * y;
1827 for (
int x = 0; x < width; ++x) {
1828 int alpha = *(src++);
1829 dest[x] = (alpha << 16) + (alpha << 8) + alpha;
1833 for (
int y = 0; y < height; ++y) {
1834 uint *dest =
reinterpret_cast<uint *>(destination->scanLine(y));
1835 BYTE *src = alphaValues + width * 3 * y;
1837 for (
int x = 0; x < width; ++x) {
1838 BYTE redAlpha = *(src + 0);
1839 BYTE greenAlpha = *(src + 1);
1840 BYTE blueAlpha = *(src + 2);
1842 if (m_pixelGeometry == DWRITE_PIXEL_GEOMETRY_BGR)
1843 qSwap(redAlpha, blueAlpha);
1845 dest[x] = qRgb(redAlpha, greenAlpha, blueAlpha);
1851 qErrnoWarning(
"%s: CreateAlphaTexture failed",
__FUNCTION__);
1856QImage QWindowsFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t,
1857 const QFixedPoint &subPixelPosition,
1858 const QTransform &xform)
1860 QImage mask = imageForGlyph(t,
1862 glyphMargin(QFontEngine::Format_A32),
1865 if (mask.isNull()) {
1866 mask = QFontEngine::renderedPathForGlyph(t, Qt::white);
1867 if (!xform.isIdentity())
1868 mask = mask.transformed(xform);
1871 return mask.depth() == 32
1873 : mask.convertToFormat(QImage::Format_RGB32);
1876QFontEngine *QWindowsFontEngineDirectWrite::cloneWithSize(qreal pixelSize)
const
1878 QWindowsFontEngineDirectWrite *fontEngine =
new QWindowsFontEngineDirectWrite(m_directWriteFontFace,
1882 fontEngine->fontDef = fontDef;
1883 fontEngine->fontDef.pixelSize = pixelSize;
1884 if (!m_uniqueFamilyName.isEmpty()) {
1885 fontEngine->setUniqueFamilyName(m_uniqueFamilyName);
1886 QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
1887 static_cast<QWindowsFontDatabase *>(pfdb)->refUniqueFont(m_uniqueFamilyName);
1893Qt::HANDLE QWindowsFontEngineDirectWrite::handle()
const
1895 return m_directWriteFontFace;
1898void QWindowsFontEngineDirectWrite::initFontInfo(
const QFontDef &request,
1903 if (fontDef.pointSize < 0)
1904 fontDef.pointSize = fontDef.pixelSize * 72. / dpi;
1905 else if (fontDef.pixelSize == -1)
1906 fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.);
1908 m_faceId.variableAxes = request.variableAxisValues;
1910#if QT_CONFIG(directwrite3)
1911 IDWriteFontFace3 *face3 =
nullptr;
1912 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace3),
1913 reinterpret_cast<
void **>(&face3)))) {
1915 fontDef.weight = QWindowsDirectWriteFontDatabase::fromDirectWriteWeight(face3->GetWeight());
1916 fontDef.style = QWindowsDirectWriteFontDatabase::fromDirectWriteStyle(face3->GetStyle());
1918 IDWriteLocalizedStrings *names;
1919 if (SUCCEEDED(face3->GetFaceNames(&names))) {
1920 wchar_t englishLocale[] = L"en-us";
1921 fontDef.styleName = QWindowsDirectWriteFontDatabase::localeString(names, englishLocale);
1926 if (face3->IsColorFont())
1927 glyphFormat = QFontEngine::Format_ARGB;
1934QString QWindowsFontEngineDirectWrite::fontNameSubstitute(
const QString &familyName)
1936 const QString substitute =
1937 QWinRegistryKey(HKEY_LOCAL_MACHINE,
1938 LR"(Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes)")
1939 .stringValue(familyName);
1940 return substitute.isEmpty() ? familyName : substitute;
1943QRect QWindowsFontEngineDirectWrite::alphaTextureBounds(glyph_t glyph,
1944 const DWRITE_MATRIX &transform)
1946 UINT16 glyphIndex = glyph;
1947 FLOAT glyphAdvance = 0;
1949 DWRITE_GLYPH_OFFSET glyphOffset;
1950 glyphOffset.advanceOffset = 0;
1951 glyphOffset.ascenderOffset = 0;
1953 DWRITE_GLYPH_RUN glyphRun;
1954 glyphRun.fontFace = m_directWriteFontFace;
1955 glyphRun.fontEmSize = fontDef.pixelSize;
1956 glyphRun.glyphCount = 1;
1957 glyphRun.glyphIndices = &glyphIndex;
1958 glyphRun.glyphAdvances = &glyphAdvance;
1959 glyphRun.isSideways =
false;
1960 glyphRun.bidiLevel = 0;
1961 glyphRun.glyphOffsets = &glyphOffset;
1963 DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
1964 DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
1965 DWRITE_GRID_FIT_MODE gridFitMode = fontDef.hintingPreference == QFont::PreferNoHinting
1966 ? DWRITE_GRID_FIT_MODE_DISABLED
1967 : DWRITE_GRID_FIT_MODE_DEFAULT;
1969 ComPtr<IDWriteFactory2> factory2 =
nullptr;
1970 HRESULT hr = m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
1973 ComPtr<IDWriteGlyphRunAnalysis> glyphAnalysis;
1974 if (SUCCEEDED(hr)) {
1975 hr = factory2->CreateGlyphRunAnalysis(
1981 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1986 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1997 if (SUCCEEDED(hr)) {
1999 hr = glyphAnalysis->GetAlphaTextureBounds(renderMode == DWRITE_RENDERING_MODE_ALIASED
2000 ? DWRITE_TEXTURE_ALIASED_1x1
2001 : DWRITE_TEXTURE_CLEARTYPE_3x1,
2003 if (FAILED(hr) || rect.left == rect.right || rect.top == rect.bottom)
2006 return QRect(QPoint(rect.left, rect.top), QPoint(rect.right, rect.bottom));
2012QRect QWindowsFontEngineDirectWrite::colorBitmapBounds(glyph_t glyph,
const DWRITE_MATRIX &transform)
2014#if QT_CONFIG(directwrite3)
2015 ComPtr<IDWriteFontFace4> face4;
2016 if (SUCCEEDED(m_directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace4),
2018 DWRITE_GLYPH_IMAGE_FORMATS formats = face4->GetGlyphImageFormats();
2020 const DWRITE_GLYPH_IMAGE_FORMATS supportedBitmapFormats =
2021 DWRITE_GLYPH_IMAGE_FORMATS(DWRITE_GLYPH_IMAGE_FORMATS_PNG
2022 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
2023 | DWRITE_GLYPH_IMAGE_FORMATS_TIFF);
2025 if (formats & supportedBitmapFormats) {
2026 DWRITE_GLYPH_IMAGE_DATA data;
2028 HRESULT hr = face4->GetGlyphImageData(glyph,
2030 DWRITE_GLYPH_IMAGE_FORMATS(formats & supportedBitmapFormats),
2034 qErrnoWarning(
"%s: GetGlyphImageData failed",
__FUNCTION__);
2038 auto fnc = qScopeGuard([&]() {
2039 if (data.imageData !=
nullptr)
2040 face4->ReleaseGlyphImageData(ctx);
2043 if (data.pixelsPerEm == 0 || data.imageData ==
nullptr || data.imageDataSize == 0)
2046 QRect rect(-data.horizontalLeftOrigin.x,
2047 -data.horizontalLeftOrigin.y,
2048 data.pixelSize.width,
2049 data.pixelSize.height);
2051 QTransform matrix(transform.m11, transform.m12,
2052 transform.m21, transform.m22,
2053 transform.dx, transform.dy);
2056 const qreal scale = fontDef.pixelSize / data.pixelsPerEm;
2057 matrix.scale(scale, scale);
2058 rect = matrix.mapRect(rect);
2067 Q_UNUSED(transform);
2072glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph,
2073 const QFixedPoint &subPixelPosition,
2074 const QTransform &originalTransform,
2077 QTransform matrix = originalTransform;
2078 if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
2079 matrix.scale(fontDef.stretch / 100.0, 1.0);
2081 glyph_metrics_t bbox = QFontEngine::boundingBox(glyph, matrix);
2083 DWRITE_MATRIX transform;
2084 transform.dx = subPixelPosition.x.toReal();
2086 transform.m11 = matrix.m11();
2087 transform.m12 = matrix.m12();
2088 transform.m21 = matrix.m21();
2089 transform.m22 = matrix.m22();
2092 QRect rect = paintGraphBounds(glyph, transform);
2096 rect = alphaTextureBounds(glyph, transform);
2100 rect = colorBitmapBounds(glyph, transform);
2106 int margin = glyphMargin(format);
2107 return glyph_metrics_t(rect.left(),
2109 rect.right() - rect.left() + margin * 2,
2110 rect.bottom() - rect.top() + margin * 2,
2111 bbox.xoff, bbox.yoff);
2114QImage QWindowsFontEngineDirectWrite::bitmapForGlyph(glyph_t glyph,
2115 const QFixedPoint &subPixelPosition,
2116 const QTransform &t,
2117 const QColor &color)
2119 return imageForGlyph(glyph, subPixelPosition, glyphMargin(QFontEngine::Format_ARGB), t, color);
2122QList<QFontVariableAxis> QWindowsFontEngineDirectWrite::variableAxes()
const
2124 return m_variableAxes;
2127bool QWindowsFontEngineDirectWrite::expectsGammaCorrectedBlending(QFontEngine::GlyphFormat format)
const
2129 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)