1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #ifndef QT_NO_DIRECTWRITE
41 
42 #include "qwindowsfontenginedirectwrite_p.h"
43 #include "qwindowsfontdatabase_p.h"
44 
45 #include <QtCore/QtEndian>
46 #include <QtCore/QVarLengthArray>
47 #include <QtCore/QFile>
48 #include <private/qstringiterator_p.h>
49 #include <QtCore/private/qsystemlibrary_p.h>
50 #include <QtCore/private/qwinregistry_p.h>
51 #include <QtGui/private/qguiapplication_p.h>
52 #include <qpa/qplatformintegration.h>
53 #include <QtGui/private/qhighdpiscaling_p.h>
54 #include <QtGui/qpainterpath.h>
55 
56 #if defined(QT_USE_DIRECTWRITE2)
57 #  include <dwrite_2.h>
58 #else
59 #  include <dwrite.h>
60 #endif
61 
62 #include <d2d1.h>
63 
64 QT_BEGIN_NAMESPACE
65 
66 // Clang does not consider __declspec(nothrow) as nothrow
67 QT_WARNING_DISABLE_CLANG("-Wmicrosoft-exception-spec")
68 
69 // Convert from design units to logical pixels
70 #define DESIGN_TO_LOGICAL(DESIGN_UNIT_VALUE) \
71     QFixed::fromReal((qreal(DESIGN_UNIT_VALUE) / qreal(m_unitsPerEm)) * fontDef.pixelSize)
72 
73 namespace {
74 
75     class GeometrySink: public IDWriteGeometrySink
76     {
77         Q_DISABLE_COPY_MOVE(GeometrySink)
78     public:
GeometrySink(QPainterPath * path)79         GeometrySink(QPainterPath *path)
80             : m_refCount(0), m_path(path)
81         {
82             Q_ASSERT(m_path != 0);
83         }
84         virtual ~GeometrySink() = default;
85 
86         IFACEMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *beziers, UINT bezierCount);
87         IFACEMETHOD_(void, AddLines)(const D2D1_POINT_2F *points, UINT pointCount);
88         IFACEMETHOD_(void, BeginFigure)(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin);
89         IFACEMETHOD(Close)();
90         IFACEMETHOD_(void, EndFigure)(D2D1_FIGURE_END figureEnd);
91         IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode);
92         IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags);
93 
94         IFACEMETHOD_(unsigned long, AddRef)();
95         IFACEMETHOD_(unsigned long, Release)();
96         IFACEMETHOD(QueryInterface)(IID const &riid, void **ppvObject);
97 
98     private:
fromD2D1_POINT_2F(const D2D1_POINT_2F & inp)99         inline static QPointF fromD2D1_POINT_2F(const D2D1_POINT_2F &inp)
100         {
101             return QPointF(inp.x, inp.y);
102         }
103 
104         unsigned long m_refCount;
105         QPointF m_startPoint;
106         QPainterPath *m_path;
107     };
108 
AddBeziers(const D2D1_BEZIER_SEGMENT * beziers,UINT bezierCount)109     void GeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers,
110                                   UINT bezierCount)
111     {
112         for (uint i=0; i<bezierCount; ++i) {
113             QPointF c1 = fromD2D1_POINT_2F(beziers[i].point1);
114             QPointF c2 = fromD2D1_POINT_2F(beziers[i].point2);
115             QPointF p2 = fromD2D1_POINT_2F(beziers[i].point3);
116 
117             m_path->cubicTo(c1, c2, p2);
118         }
119     }
120 
AddLines(const D2D1_POINT_2F * points,UINT pointsCount)121     void GeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount)
122     {
123         for (uint i=0; i<pointsCount; ++i)
124             m_path->lineTo(fromD2D1_POINT_2F(points[i]));
125     }
126 
BeginFigure(D2D1_POINT_2F startPoint,D2D1_FIGURE_BEGIN)127     void GeometrySink::BeginFigure(D2D1_POINT_2F startPoint,
128                                    D2D1_FIGURE_BEGIN /*figureBegin*/)
129     {
130         m_startPoint = fromD2D1_POINT_2F(startPoint);
131         m_path->moveTo(m_startPoint);
132     }
133 
Close()134     IFACEMETHODIMP GeometrySink::Close()
135     {
136         return E_NOTIMPL;
137     }
138 
EndFigure(D2D1_FIGURE_END figureEnd)139     void GeometrySink::EndFigure(D2D1_FIGURE_END figureEnd)
140     {
141         if (figureEnd == D2D1_FIGURE_END_CLOSED)
142             m_path->closeSubpath();
143     }
144 
SetFillMode(D2D1_FILL_MODE fillMode)145     void GeometrySink::SetFillMode(D2D1_FILL_MODE fillMode)
146     {
147         m_path->setFillRule(fillMode == D2D1_FILL_MODE_ALTERNATE
148                             ? Qt::OddEvenFill
149                             : Qt::WindingFill);
150     }
151 
SetSegmentFlags(D2D1_PATH_SEGMENT)152     void GeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT /*vertexFlags*/)
153     {
154         /* Not implemented */
155     }
156 
IFACEMETHODIMP_(unsigned long)157     IFACEMETHODIMP_(unsigned long) GeometrySink::AddRef()
158     {
159         return InterlockedIncrement(&m_refCount);
160     }
161 
IFACEMETHODIMP_(unsigned long)162     IFACEMETHODIMP_(unsigned long) GeometrySink::Release()
163     {
164         unsigned long newCount = InterlockedDecrement(&m_refCount);
165         if (newCount == 0)
166         {
167             delete this;
168             return 0;
169         }
170 
171         return newCount;
172     }
173 
QueryInterface(IID const & riid,void ** ppvObject)174     IFACEMETHODIMP GeometrySink::QueryInterface(IID const &riid, void **ppvObject)
175     {
176         if (__uuidof(IDWriteGeometrySink) == riid) {
177             *ppvObject = this;
178         } else if (__uuidof(IUnknown) == riid) {
179             *ppvObject = this;
180         } else {
181             *ppvObject = NULL;
182             return E_FAIL;
183         }
184 
185         AddRef();
186         return S_OK;
187     }
188 
189 }
190 
hintingPreferenceToRenderingMode(QFont::HintingPreference hintingPreference)191 static DWRITE_RENDERING_MODE hintingPreferenceToRenderingMode(QFont::HintingPreference hintingPreference)
192 {
193     if (QHighDpiScaling::isActive() && hintingPreference == QFont::PreferDefaultHinting)
194         hintingPreference = QFont::PreferVerticalHinting;
195 
196     switch (hintingPreference) {
197     case QFont::PreferNoHinting:
198         return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
199     case QFont::PreferVerticalHinting:
200         return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
201     default:
202         return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
203     }
204 }
205 
206 /*!
207     \class QWindowsFontEngineDirectWrite
208     \brief Windows font engine using Direct Write.
209     \internal
210 
211     Font engine for subpixel positioned text on Windows Vista
212     (with platform update) and Windows 7. If selected during
213     configuration, the engine will be selected only when the hinting
214     preference of a font is set to None or Vertical hinting. The font
215     database uses most of the same logic but creates a direct write
216     font based on the LOGFONT rather than a GDI handle.
217 
218     Will probably be superseded by a common Free Type font engine in Qt 5.X.
219 */
220 
QWindowsFontEngineDirectWrite(IDWriteFontFace * directWriteFontFace,qreal pixelSize,const QSharedPointer<QWindowsFontEngineData> & d)221 QWindowsFontEngineDirectWrite::QWindowsFontEngineDirectWrite(IDWriteFontFace *directWriteFontFace,
222                                                              qreal pixelSize,
223                                                              const QSharedPointer<QWindowsFontEngineData> &d)
224     : QFontEngine(DirectWrite)
225     , m_fontEngineData(d)
226     , m_directWriteFontFace(directWriteFontFace)
227     , m_directWriteBitmapRenderTarget(0)
228     , m_lineThickness(-1)
229     , m_unitsPerEm(-1)
230     , m_ascent(-1)
231     , m_capHeight(-1)
232     , m_descent(-1)
233     , m_xHeight(-1)
234     , m_lineGap(-1)
235 {
236     qCDebug(lcQpaFonts) << __FUNCTION__ << pixelSize;
237 
238     Q_ASSERT(m_directWriteFontFace);
239 
240     m_fontEngineData->directWriteFactory->AddRef();
241     m_directWriteFontFace->AddRef();
242 
243     fontDef.pixelSize = pixelSize;
244     collectMetrics();
245     cache_cost = (m_ascent.toInt() + m_descent.toInt()) * m_xHeight.toInt() * 2000;
246 }
247 
~QWindowsFontEngineDirectWrite()248 QWindowsFontEngineDirectWrite::~QWindowsFontEngineDirectWrite()
249 {
250     qCDebug(lcQpaFonts) << __FUNCTION__;
251 
252     m_fontEngineData->directWriteFactory->Release();
253     m_directWriteFontFace->Release();
254 
255     if (m_directWriteBitmapRenderTarget != 0)
256         m_directWriteBitmapRenderTarget->Release();
257 
258     if (!m_uniqueFamilyName.isEmpty()) {
259         QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
260         static_cast<QWindowsFontDatabase *>(pfdb)->derefUniqueFont(m_uniqueFamilyName);
261     }
262 }
263 
264 #ifndef Q_CC_MINGW
265 typedef IDWriteLocalFontFileLoader QIdWriteLocalFontFileLoader;
266 
uuidIdWriteLocalFontFileLoader()267 static UUID uuidIdWriteLocalFontFileLoader()
268 {
269     return __uuidof(IDWriteLocalFontFileLoader);
270 }
271 #else // !Q_CC_MINGW
DECLARE_INTERFACE_(QIdWriteLocalFontFileLoader,IDWriteFontFileLoader)272 DECLARE_INTERFACE_(QIdWriteLocalFontFileLoader, IDWriteFontFileLoader)
273 {
274     STDMETHOD(GetFilePathLengthFromKey)(THIS_ void const *, UINT32, UINT32*) PURE;
275     STDMETHOD(GetFilePathFromKey)(THIS_ void const *, UINT32, WCHAR *, UINT32) PURE;
276     STDMETHOD(GetLastWriteTimeFromKey)(THIS_ void const *, UINT32, FILETIME *) PURE;
277 };
278 
uuidIdWriteLocalFontFileLoader()279 static UUID uuidIdWriteLocalFontFileLoader()
280 {
281     static const UUID result = { 0xb2d9f3ec, 0xc9fe, 0x4a11, {0xa2, 0xec, 0xd8, 0x62, 0x8, 0xf7, 0xc0, 0xa2}};
282     return result;
283 }
284 #endif // Q_CC_MINGW
285 
filenameFromFontFile(IDWriteFontFile * fontFile)286 QString QWindowsFontEngineDirectWrite::filenameFromFontFile(IDWriteFontFile *fontFile)
287 {
288     IDWriteFontFileLoader *loader = nullptr;
289 
290     HRESULT hr = fontFile->GetLoader(&loader);
291     if (FAILED(hr)) {
292         qErrnoWarning("%s: GetLoader failed", __FUNCTION__);
293         return QString();
294     }
295 
296     QIdWriteLocalFontFileLoader *localLoader = nullptr;
297     hr = loader->QueryInterface(uuidIdWriteLocalFontFileLoader(),
298                                 reinterpret_cast<void **>(&localLoader));
299 
300     const void *fontFileReferenceKey = nullptr;
301     UINT32 fontFileReferenceKeySize = 0;
302     if (SUCCEEDED(hr)) {
303         hr = fontFile->GetReferenceKey(&fontFileReferenceKey,
304                                        &fontFileReferenceKeySize);
305         if (FAILED(hr))
306             qErrnoWarning(hr, "%s: GetReferenceKey failed", __FUNCTION__);
307     }
308 
309     UINT32 filePathLength = 0;
310     if (SUCCEEDED(hr)) {
311         hr = localLoader->GetFilePathLengthFromKey(fontFileReferenceKey,
312                                                    fontFileReferenceKeySize,
313                                                    &filePathLength);
314         if (FAILED(hr))
315             qErrnoWarning(hr, "GetFilePathLength failed", __FUNCTION__);
316     }
317 
318     QString ret;
319     if (SUCCEEDED(hr) && filePathLength > 0) {
320         QVarLengthArray<wchar_t> filePath(filePathLength + 1);
321 
322         hr = localLoader->GetFilePathFromKey(fontFileReferenceKey,
323                                              fontFileReferenceKeySize,
324                                              filePath.data(),
325                                              filePathLength + 1);
326         if (FAILED(hr))
327             qErrnoWarning(hr, "%s: GetFilePathFromKey failed", __FUNCTION__);
328         else
329             ret = QString::fromWCharArray(filePath.data());
330     }
331 
332     if (localLoader != nullptr)
333         localLoader->Release();
334 
335     if (loader != nullptr)
336         loader->Release();
337     return ret;
338 }
339 
collectMetrics()340 void QWindowsFontEngineDirectWrite::collectMetrics()
341 {
342     DWRITE_FONT_METRICS metrics;
343 
344     m_directWriteFontFace->GetMetrics(&metrics);
345     m_unitsPerEm = metrics.designUnitsPerEm;
346 
347     m_lineThickness = DESIGN_TO_LOGICAL(metrics.underlineThickness);
348     m_ascent = DESIGN_TO_LOGICAL(metrics.ascent);
349     m_capHeight = DESIGN_TO_LOGICAL(metrics.capHeight);
350     m_descent = DESIGN_TO_LOGICAL(metrics.descent);
351     m_xHeight = DESIGN_TO_LOGICAL(metrics.xHeight);
352     m_lineGap = DESIGN_TO_LOGICAL(metrics.lineGap);
353     m_underlinePosition = DESIGN_TO_LOGICAL(metrics.underlinePosition);
354 
355     IDWriteFontFile *fontFile = nullptr;
356     UINT32 numberOfFiles = 1;
357     if (SUCCEEDED(m_directWriteFontFace->GetFiles(&numberOfFiles, &fontFile))) {
358         m_faceId.filename = QFile::encodeName(filenameFromFontFile(fontFile));
359         fontFile->Release();
360     }
361 
362     QByteArray table = getSfntTable(MAKE_TAG('h', 'h', 'e', 'a'));
363     const int advanceWidthMaxLocation = 10;
364     if (table.size() >= advanceWidthMaxLocation + int(sizeof(quint16))) {
365         quint16 advanceWidthMax = qFromBigEndian<quint16>(table.constData() + advanceWidthMaxLocation);
366         m_maxAdvanceWidth = DESIGN_TO_LOGICAL(advanceWidthMax);
367     }
368 }
369 
underlinePosition() const370 QFixed QWindowsFontEngineDirectWrite::underlinePosition() const
371 {
372     if (m_underlinePosition > 0)
373         return m_underlinePosition;
374     else
375         return QFontEngine::underlinePosition();
376 }
377 
lineThickness() const378 QFixed QWindowsFontEngineDirectWrite::lineThickness() const
379 {
380     if (m_lineThickness > 0)
381         return m_lineThickness;
382     else
383         return QFontEngine::lineThickness();
384 }
385 
getSfntTableData(uint tag,uchar * buffer,uint * length) const386 bool QWindowsFontEngineDirectWrite::getSfntTableData(uint tag, uchar *buffer, uint *length) const
387 {
388     bool ret = false;
389 
390     const void *tableData = 0;
391     UINT32 tableSize;
392     void *tableContext = 0;
393     BOOL exists;
394     HRESULT hr = m_directWriteFontFace->TryGetFontTable(qbswap<quint32>(tag),
395                                                         &tableData, &tableSize,
396                                                         &tableContext, &exists);
397     if (SUCCEEDED(hr)) {
398         if (exists) {
399             ret = true;
400             if (buffer && *length >= tableSize)
401                 memcpy(buffer, tableData, tableSize);
402             *length = tableSize;
403             Q_ASSERT(int(*length) > 0);
404         }
405         m_directWriteFontFace->ReleaseFontTable(tableContext);
406     } else {
407         qErrnoWarning("%s: TryGetFontTable failed", __FUNCTION__);
408     }
409 
410     return ret;
411 }
412 
emSquareSize() const413 QFixed QWindowsFontEngineDirectWrite::emSquareSize() const
414 {
415     if (m_unitsPerEm > 0)
416         return m_unitsPerEm;
417     else
418         return QFontEngine::emSquareSize();
419 }
420 
glyphIndex(uint ucs4) const421 glyph_t QWindowsFontEngineDirectWrite::glyphIndex(uint ucs4) const
422 {
423     UINT16 glyphIndex;
424 
425     HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(&ucs4, 1, &glyphIndex);
426     if (FAILED(hr)) {
427         qErrnoWarning("%s: glyphIndex failed", __FUNCTION__);
428         glyphIndex = 0;
429     }
430 
431     return glyphIndex;
432 }
433 
stringToCMap(const QChar * str,int len,QGlyphLayout * glyphs,int * nglyphs,QFontEngine::ShaperFlags flags) const434 bool QWindowsFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
435                                                  int *nglyphs, QFontEngine::ShaperFlags flags) const
436 {
437     Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
438     if (*nglyphs < len) {
439         *nglyphs = len;
440         return false;
441     }
442 
443     QVarLengthArray<UINT32> codePoints(len);
444     int actualLength = 0;
445     QStringIterator it(str, str + len);
446     while (it.hasNext())
447         codePoints[actualLength++] = it.next();
448 
449     QVarLengthArray<UINT16> glyphIndices(actualLength);
450     HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(codePoints.data(), actualLength,
451                                                          glyphIndices.data());
452     if (FAILED(hr)) {
453         qErrnoWarning("%s: GetGlyphIndicesW failed", __FUNCTION__);
454         return false;
455     }
456 
457     for (int i = 0; i < actualLength; ++i)
458         glyphs->glyphs[i] = glyphIndices.at(i);
459 
460     *nglyphs = actualLength;
461     glyphs->numGlyphs = actualLength;
462 
463     if (!(flags & GlyphIndicesOnly))
464         recalcAdvances(glyphs, {});
465 
466     return true;
467 }
468 
faceId() const469 QFontEngine::FaceId QWindowsFontEngineDirectWrite::faceId() const
470 {
471     return m_faceId;
472 }
473 
recalcAdvances(QGlyphLayout * glyphs,QFontEngine::ShaperFlags) const474 void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags) const
475 {
476     QVarLengthArray<UINT16> glyphIndices(glyphs->numGlyphs);
477 
478     // ### Caching?
479     for(int i=0; i<glyphs->numGlyphs; i++)
480         glyphIndices[i] = UINT16(glyphs->glyphs[i]);
481 
482     QVarLengthArray<DWRITE_GLYPH_METRICS> glyphMetrics(glyphIndices.size());
483     HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(),
484                                                               glyphIndices.size(),
485                                                               glyphMetrics.data());
486     if (SUCCEEDED(hr)) {
487         qreal stretch = fontDef.stretch != QFont::AnyStretch ? fontDef.stretch / 100.0 : 1.0;
488         for (int i = 0; i < glyphs->numGlyphs; ++i)
489             glyphs->advances[i] = DESIGN_TO_LOGICAL(glyphMetrics[i].advanceWidth * stretch);
490 QT_WARNING_PUSH
491 QT_WARNING_DISABLE_DEPRECATED
492         if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
493 QT_WARNING_POP
494             for (int i = 0; i < glyphs->numGlyphs; ++i)
495                 glyphs->advances[i] = glyphs->advances[i].round();
496         }
497     } else {
498         qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__);
499     }
500 }
501 
addGlyphsToPath(glyph_t * glyphs,QFixedPoint * positions,int nglyphs,QPainterPath * path,QTextItem::RenderFlags flags)502 void QWindowsFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
503                                              QPainterPath *path, QTextItem::RenderFlags flags)
504 {
505     QVarLengthArray<UINT16> glyphIndices(nglyphs);
506     QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(nglyphs);
507     QVarLengthArray<FLOAT> glyphAdvances(nglyphs);
508 
509     for (int i=0; i<nglyphs; ++i) {
510         glyphIndices[i] = glyphs[i];
511         glyphOffsets[i].advanceOffset  = positions[i].x.toReal();
512         glyphOffsets[i].ascenderOffset = -positions[i].y.toReal();
513         glyphAdvances[i] = 0.0;
514     }
515 
516     GeometrySink geometrySink(path);
517     HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(
518                 fontDef.pixelSize,
519                 glyphIndices.data(),
520                 glyphAdvances.data(),
521                 glyphOffsets.data(),
522                 nglyphs,
523                 false,
524                 flags & QTextItem::RightToLeft,
525                 &geometrySink
526                 );
527 
528     if (FAILED(hr))
529         qErrnoWarning("%s: GetGlyphRunOutline failed", __FUNCTION__);
530 }
531 
boundingBox(const QGlyphLayout & glyphs)532 glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(const QGlyphLayout &glyphs)
533 {
534     if (glyphs.numGlyphs == 0)
535         return glyph_metrics_t();
536 
537 QT_WARNING_PUSH
538 QT_WARNING_DISABLE_DEPRECATED
539     bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics;
540 QT_WARNING_POP
541 
542     QFixed w = 0;
543     for (int i = 0; i < glyphs.numGlyphs; ++i) {
544         w += round ? glyphs.effectiveAdvance(i).round() : glyphs.effectiveAdvance(i);
545 
546     }
547 
548     return glyph_metrics_t(0, -m_ascent, w - lastRightBearing(glyphs), m_ascent + m_descent, w, 0);
549 }
550 
boundingBox(glyph_t g)551 glyph_metrics_t QWindowsFontEngineDirectWrite::boundingBox(glyph_t g)
552 {
553     UINT16 glyphIndex = g;
554 
555     DWRITE_GLYPH_METRICS glyphMetrics;
556     HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics);
557     if (SUCCEEDED(hr)) {
558         QFixed advanceWidth = DESIGN_TO_LOGICAL(glyphMetrics.advanceWidth);
559         QFixed leftSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.leftSideBearing);
560         QFixed rightSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.rightSideBearing);
561         QFixed advanceHeight = DESIGN_TO_LOGICAL(glyphMetrics.advanceHeight);
562         QFixed verticalOriginY = DESIGN_TO_LOGICAL(glyphMetrics.verticalOriginY);
563         QFixed topSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.topSideBearing);
564         QFixed bottomSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.bottomSideBearing);
565 
566 QT_WARNING_PUSH
567 QT_WARNING_DISABLE_DEPRECATED
568         if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
569 QT_WARNING_POP
570             advanceWidth = advanceWidth.round();
571             advanceHeight = advanceHeight.round();
572         }
573 
574         QFixed width = advanceWidth - leftSideBearing - rightSideBearing;
575         QFixed height = advanceHeight - topSideBearing - bottomSideBearing;
576         return glyph_metrics_t(leftSideBearing,
577                                -verticalOriginY + topSideBearing,
578                                width,
579                                height,
580                                advanceWidth,
581                                0);
582     } else {
583         qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__);
584     }
585 
586     return glyph_metrics_t();
587 }
588 
ascent() const589 QFixed QWindowsFontEngineDirectWrite::ascent() const
590 {
591 QT_WARNING_PUSH
592 QT_WARNING_DISABLE_DEPRECATED
593     return fontDef.styleStrategy & QFont::ForceIntegerMetrics
594             ? m_ascent.round()
595             : m_ascent;
596 QT_WARNING_POP
597 }
598 
capHeight() const599 QFixed QWindowsFontEngineDirectWrite::capHeight() const
600 {
601     if (m_capHeight <= 0)
602         return calculatedCapHeight();
603 
604 QT_WARNING_PUSH
605 QT_WARNING_DISABLE_DEPRECATED
606     return fontDef.styleStrategy & QFont::ForceIntegerMetrics
607             ? m_capHeight.round()
608             : m_capHeight;
609 QT_WARNING_POP
610 }
611 
descent() const612 QFixed QWindowsFontEngineDirectWrite::descent() const
613 {
614 QT_WARNING_PUSH
615 QT_WARNING_DISABLE_DEPRECATED
616     return fontDef.styleStrategy & QFont::ForceIntegerMetrics
617            ? m_descent.round()
618            : m_descent;
619 QT_WARNING_POP
620 }
621 
leading() const622 QFixed QWindowsFontEngineDirectWrite::leading() const
623 {
624 QT_WARNING_PUSH
625 QT_WARNING_DISABLE_DEPRECATED
626     return fontDef.styleStrategy & QFont::ForceIntegerMetrics
627            ? m_lineGap.round()
628            : m_lineGap;
629 QT_WARNING_POP
630 }
631 
xHeight() const632 QFixed QWindowsFontEngineDirectWrite::xHeight() const
633 {
634 QT_WARNING_PUSH
635 QT_WARNING_DISABLE_DEPRECATED
636     return fontDef.styleStrategy & QFont::ForceIntegerMetrics
637            ? m_xHeight.round()
638            : m_xHeight;
639 QT_WARNING_POP
640 }
641 
maxCharWidth() const642 qreal QWindowsFontEngineDirectWrite::maxCharWidth() const
643 {
644 QT_WARNING_PUSH
645 QT_WARNING_DISABLE_DEPRECATED
646     return fontDef.styleStrategy & QFont::ForceIntegerMetrics
647            ? m_maxAdvanceWidth.round().toReal()
648            : m_maxAdvanceWidth.toReal();
649 QT_WARNING_POP
650 }
651 
alphaMapForGlyph(glyph_t glyph,QFixed subPixelPosition,const QTransform & t)652 QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t)
653 {
654     QImage im = imageForGlyph(glyph, subPixelPosition, glyphMargin(Format_A8), t);
655 
656     QImage alphaMap(im.width(), im.height(), QImage::Format_Alpha8);
657 
658     for (int y=0; y<im.height(); ++y) {
659         const uint *src = reinterpret_cast<const uint *>(im.constScanLine(y));
660         uchar *dst = alphaMap.scanLine(y);
661         for (int x=0; x<im.width(); ++x) {
662             *dst = 255 - (m_fontEngineData->pow_gamma[qGray(0xffffffff - *src)] * 255. / 2047.);
663             ++dst;
664             ++src;
665         }
666     }
667 
668     return alphaMap;
669 }
670 
alphaMapForGlyph(glyph_t glyph,QFixed subPixelPosition)671 QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition)
672 {
673     return alphaMapForGlyph(glyph, subPixelPosition, QTransform());
674 }
675 
supportsSubPixelPositions() const676 bool QWindowsFontEngineDirectWrite::supportsSubPixelPositions() const
677 {
678     return true;
679 }
680 
imageForGlyph(glyph_t t,QFixed subPixelPosition,int margin,const QTransform & originalTransform,const QColor & color)681 QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
682                                              QFixed subPixelPosition,
683                                              int margin,
684                                              const QTransform &originalTransform,
685                                              const QColor &color)
686 {
687     UINT16 glyphIndex = t;
688     FLOAT glyphAdvance = 0;
689 
690     DWRITE_GLYPH_OFFSET glyphOffset;
691     glyphOffset.advanceOffset = 0;
692     glyphOffset.ascenderOffset = 0;
693 
694     DWRITE_GLYPH_RUN glyphRun;
695     glyphRun.fontFace = m_directWriteFontFace;
696     glyphRun.fontEmSize = fontDef.pixelSize;
697     glyphRun.glyphCount = 1;
698     glyphRun.glyphIndices = &glyphIndex;
699     glyphRun.glyphAdvances = &glyphAdvance;
700     glyphRun.isSideways = false;
701     glyphRun.bidiLevel = 0;
702     glyphRun.glyphOffsets = &glyphOffset;
703 
704     QTransform xform = originalTransform;
705     if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
706         xform.scale(fontDef.stretch / 100.0, 1.0);
707 
708     DWRITE_MATRIX transform;
709     transform.dx = subPixelPosition.toReal();
710     transform.dy = 0;
711     transform.m11 = xform.m11();
712     transform.m12 = xform.m12();
713     transform.m21 = xform.m21();
714     transform.m22 = xform.m22();
715 
716     DWRITE_RENDERING_MODE renderMode =
717             hintingPreferenceToRenderingMode(QFont::HintingPreference(fontDef.hintingPreference));
718 
719     IDWriteGlyphRunAnalysis *glyphAnalysis = NULL;
720     HRESULT hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
721                 &glyphRun,
722                 1.0f,
723                 &transform,
724                 renderMode,
725                 DWRITE_MEASURING_MODE_NATURAL,
726                 0.0, 0.0,
727                 &glyphAnalysis
728                 );
729 
730     if (SUCCEEDED(hr)) {
731         RECT rect;
732         glyphAnalysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &rect);
733 
734         if (rect.top == rect.bottom || rect.left == rect.right)
735             return QImage();
736 
737         QRect boundingRect = QRect(QPoint(rect.left - margin,
738                                           rect.top - margin),
739                                    QPoint(rect.right + margin,
740                                           rect.bottom + margin));
741 
742 
743         const int width = boundingRect.width() - 1; // -1 due to Qt's off-by-one definition of a QRect
744         const int height = boundingRect.height() - 1;
745 
746         QImage image;
747 #if defined(QT_USE_DIRECTWRITE2)
748         HRESULT hr = DWRITE_E_NOCOLOR;
749         IDWriteColorGlyphRunEnumerator *enumerator = 0;
750         IDWriteFactory2 *factory2 = nullptr;
751         if (glyphFormat == QFontEngine::Format_ARGB
752                 && SUCCEEDED(m_fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory2),
753                                                                                   reinterpret_cast<void **>(&factory2)))) {
754             hr = factory2->TranslateColorGlyphRun(0.0f,
755                                                   0.0f,
756                                                   &glyphRun,
757                                                   NULL,
758                                                   DWRITE_MEASURING_MODE_NATURAL,
759                                                   NULL,
760                                                   0,
761                                                   &enumerator);
762             image = QImage(width, height, QImage::Format_ARGB32_Premultiplied);
763             image.fill(0);
764         } else
765 #endif
766         {
767             image = QImage(width, height, QImage::Format_RGB32);
768             image.fill(0xffffffff);
769         }
770 
771 #if defined(QT_USE_DIRECTWRITE2)
772         BOOL ok = true;
773 
774         if (SUCCEEDED(hr)) {
775             while (SUCCEEDED(hr) && ok) {
776                 const DWRITE_COLOR_GLYPH_RUN *colorGlyphRun = 0;
777                 hr = enumerator->GetCurrentRun(&colorGlyphRun);
778                 if (FAILED(hr)) { // No colored runs, only outline
779                     qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::GetCurrentRun failed", __FUNCTION__);
780                     break;
781                 }
782 
783                 IDWriteGlyphRunAnalysis *colorGlyphsAnalysis = NULL;
784                 hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
785                             &colorGlyphRun->glyphRun,
786                             1.0f,
787                             &transform,
788                             renderMode,
789                             DWRITE_MEASURING_MODE_NATURAL,
790                             0.0, 0.0,
791                             &colorGlyphsAnalysis
792                             );
793                 if (FAILED(hr)) {
794                     qErrnoWarning(hr, "%s: CreateGlyphRunAnalysis failed for color run", __FUNCTION__);
795                     break;
796                 }
797 
798                 float r, g, b, a;
799                 if (colorGlyphRun->paletteIndex == 0xFFFF) {
800                     r = float(color.redF());
801                     g = float(color.greenF());
802                     b = float(color.blueF());
803                     a = float(color.alphaF());
804                 } else {
805                     r = qBound(0.0f, colorGlyphRun->runColor.r, 1.0f);
806                     g = qBound(0.0f, colorGlyphRun->runColor.g, 1.0f);
807                     b = qBound(0.0f, colorGlyphRun->runColor.b, 1.0f);
808                     a = qBound(0.0f, colorGlyphRun->runColor.a, 1.0f);
809                 }
810 
811                 if (!qFuzzyIsNull(a)) {
812                     renderGlyphRun(&image,
813                                    r,
814                                    g,
815                                    b,
816                                    a,
817                                    colorGlyphsAnalysis,
818                                    boundingRect);
819                 }
820                 colorGlyphsAnalysis->Release();
821 
822                 hr = enumerator->MoveNext(&ok);
823                 if (FAILED(hr)) {
824                     qErrnoWarning(hr, "%s: IDWriteColorGlyphRunEnumerator::MoveNext failed", __FUNCTION__);
825                     break;
826                 }
827             }
828         } else
829 #endif
830         {
831             float r, g, b, a;
832             if (glyphFormat == QFontEngine::Format_ARGB) {
833                 r = float(color.redF());
834                 g = float(color.greenF());
835                 b = float(color.blueF());
836                 a = float(color.alphaF());
837             } else {
838                 r = g = b = a = 0.0;
839             }
840 
841             renderGlyphRun(&image,
842                            r,
843                            g,
844                            b,
845                            a,
846                            glyphAnalysis,
847                            boundingRect);
848         }
849 
850         glyphAnalysis->Release();
851         return image;
852     } else {
853         qErrnoWarning(hr, "%s: CreateGlyphRunAnalysis failed", __FUNCTION__);
854         return QImage();
855     }
856 }
857 
858 
renderGlyphRun(QImage * destination,float r,float g,float b,float a,IDWriteGlyphRunAnalysis * glyphAnalysis,const QRect & boundingRect)859 void QWindowsFontEngineDirectWrite::renderGlyphRun(QImage *destination,
860                                                    float r,
861                                                    float g,
862                                                    float b,
863                                                    float a,
864                                                    IDWriteGlyphRunAnalysis *glyphAnalysis,
865                                                    const QRect &boundingRect)
866 {
867     const int width = destination->width();
868     const int height = destination->height();
869 
870     r *= 255.0;
871     g *= 255.0;
872     b *= 255.0;
873 
874     const int size = width * height * 3;
875     if (size > 0) {
876         RECT rect;
877         rect.left = boundingRect.left();
878         rect.top = boundingRect.top();
879         rect.right = boundingRect.right();
880         rect.bottom = boundingRect.bottom();
881 
882         QVarLengthArray<BYTE, 1024> alphaValueArray(size);
883         BYTE *alphaValues = alphaValueArray.data();
884         memset(alphaValues, 0, size);
885 
886         HRESULT hr = glyphAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1,
887                                                        &rect,
888                                                        alphaValues,
889                                                        size);
890         if (SUCCEEDED(hr)) {
891             if (destination->hasAlphaChannel()) {
892                 for (int y = 0; y < height; ++y) {
893                     uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
894                     BYTE *src = alphaValues + width * 3 * y;
895 
896                     for (int x = 0; x < width; ++x) {
897                         float redAlpha = a * *src++ / 255.0;
898                         float greenAlpha = a * *src++ / 255.0;
899                         float blueAlpha = a * *src++ / 255.0;
900                         float averageAlpha = (redAlpha + greenAlpha + blueAlpha) / 3.0;
901 
902                         QRgb currentRgb = dest[x];
903                         dest[x] = qRgba(qRound(qRed(currentRgb) * (1.0 - averageAlpha) + averageAlpha * r),
904                                         qRound(qGreen(currentRgb) * (1.0 - averageAlpha) + averageAlpha * g),
905                                         qRound(qBlue(currentRgb) * (1.0 - averageAlpha) + averageAlpha * b),
906                                         qRound(qAlpha(currentRgb) * (1.0 - averageAlpha) + averageAlpha * 255));
907                     }
908                 }
909 
910             } else {
911                 for (int y = 0; y < height; ++y) {
912                     uint *dest = reinterpret_cast<uint *>(destination->scanLine(y));
913                     BYTE *src = alphaValues + width * 3 * y;
914 
915                     for (int x = 0; x < width; ++x) {
916                         dest[x] = *(src + 0) << 16
917                                 | *(src + 1) << 8
918                                 | *(src + 2);
919 
920                         src += 3;
921                     }
922                 }
923             }
924         } else {
925             qErrnoWarning("%s: CreateAlphaTexture failed", __FUNCTION__);
926         }
927     } else {
928         glyphAnalysis->Release();
929         qWarning("%s: Glyph has no bounds", __FUNCTION__);
930     }
931 }
932 
alphaRGBMapForGlyph(glyph_t t,QFixed subPixelPosition,const QTransform & xform)933 QImage QWindowsFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t,
934                                                           QFixed subPixelPosition,
935                                                           const QTransform &xform)
936 {
937     QImage mask = imageForGlyph(t,
938                                 subPixelPosition,
939                                 glyphMargin(QFontEngine::Format_A32),
940                                 xform);
941 
942     return mask.depth() == 32
943            ? mask
944            : mask.convertToFormat(QImage::Format_RGB32);
945 }
946 
cloneWithSize(qreal pixelSize) const947 QFontEngine *QWindowsFontEngineDirectWrite::cloneWithSize(qreal pixelSize) const
948 {
949     QWindowsFontEngineDirectWrite *fontEngine = new QWindowsFontEngineDirectWrite(m_directWriteFontFace,
950                                                                                   pixelSize,
951                                                                                   m_fontEngineData);
952 
953     fontEngine->fontDef = fontDef;
954     fontEngine->fontDef.pixelSize = pixelSize;
955     if (!m_uniqueFamilyName.isEmpty()) {
956         fontEngine->setUniqueFamilyName(m_uniqueFamilyName);
957         QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase();
958         static_cast<QWindowsFontDatabase *>(pfdb)->refUniqueFont(m_uniqueFamilyName);
959     }
960 
961     return fontEngine;
962 }
963 
handle() const964 Qt::HANDLE QWindowsFontEngineDirectWrite::handle() const
965 {
966     return m_directWriteFontFace;
967 }
968 
initFontInfo(const QFontDef & request,int dpi)969 void QWindowsFontEngineDirectWrite::initFontInfo(const QFontDef &request,
970                                                  int dpi)
971 {
972     fontDef = request;
973 
974     if (fontDef.pointSize < 0)
975         fontDef.pointSize = fontDef.pixelSize * 72. / dpi;
976     else if (fontDef.pixelSize == -1)
977         fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.);
978 }
979 
fontNameSubstitute(const QString & familyName)980 QString QWindowsFontEngineDirectWrite::fontNameSubstitute(const QString &familyName)
981 {
982     const QString substitute =
983         QWinRegistryKey(HKEY_LOCAL_MACHINE,
984                         LR"(Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes)")
985         .stringValue(familyName);
986     return substitute.isEmpty() ? familyName : substitute;
987 }
988 
alphaMapBoundingBox(glyph_t glyph,QFixed subPixelPosition,const QTransform & originalTransform,GlyphFormat format)989 glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph,
990                                                                    QFixed subPixelPosition,
991                                                                    const QTransform &originalTransform,
992                                                                    GlyphFormat format)
993 {
994     Q_UNUSED(format);
995 
996     QTransform matrix = originalTransform;
997     if (fontDef.stretch != 100 && fontDef.stretch != QFont::AnyStretch)
998         matrix.scale(fontDef.stretch / 100.0, 1.0);
999 
1000     glyph_metrics_t bbox = QFontEngine::boundingBox(glyph, matrix); // To get transformed advance
1001 
1002     UINT16 glyphIndex = glyph;
1003     FLOAT glyphAdvance = 0;
1004 
1005     DWRITE_GLYPH_OFFSET glyphOffset;
1006     glyphOffset.advanceOffset = 0;
1007     glyphOffset.ascenderOffset = 0;
1008 
1009     DWRITE_GLYPH_RUN glyphRun;
1010     glyphRun.fontFace = m_directWriteFontFace;
1011     glyphRun.fontEmSize = fontDef.pixelSize;
1012     glyphRun.glyphCount = 1;
1013     glyphRun.glyphIndices = &glyphIndex;
1014     glyphRun.glyphAdvances = &glyphAdvance;
1015     glyphRun.isSideways = false;
1016     glyphRun.bidiLevel = 0;
1017     glyphRun.glyphOffsets = &glyphOffset;
1018 
1019     DWRITE_MATRIX transform;
1020     transform.dx = subPixelPosition.toReal();
1021     transform.dy = 0;
1022     transform.m11 = matrix.m11();
1023     transform.m12 = matrix.m12();
1024     transform.m21 = matrix.m21();
1025     transform.m22 = matrix.m22();
1026 
1027     DWRITE_RENDERING_MODE renderMode =
1028             hintingPreferenceToRenderingMode(QFont::HintingPreference(fontDef.hintingPreference));
1029 
1030     IDWriteGlyphRunAnalysis *glyphAnalysis = NULL;
1031     HRESULT hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
1032                 &glyphRun,
1033                 1.0f,
1034                 &transform,
1035                 renderMode,
1036                 DWRITE_MEASURING_MODE_NATURAL,
1037                 0.0, 0.0,
1038                 &glyphAnalysis
1039                 );
1040 
1041     if (SUCCEEDED(hr)) {
1042         RECT rect;
1043         glyphAnalysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &rect);
1044         glyphAnalysis->Release();
1045 
1046         int margin = glyphMargin(format);
1047 
1048         if (rect.left == rect.right || rect.top == rect.bottom)
1049             return glyph_metrics_t();
1050 
1051         return glyph_metrics_t(rect.left,
1052                                rect.top,
1053                                rect.right - rect.left + margin * 2,
1054                                rect.bottom - rect.top + margin * 2,
1055                                bbox.xoff, bbox.yoff);
1056     } else {
1057         return glyph_metrics_t();
1058     }
1059 }
1060 
bitmapForGlyph(glyph_t glyph,QFixed subPixelPosition,const QTransform & t,const QColor & color)1061 QImage QWindowsFontEngineDirectWrite::bitmapForGlyph(glyph_t glyph, QFixed subPixelPosition, const QTransform &t, const QColor &color)
1062 {
1063     return imageForGlyph(glyph, subPixelPosition, glyphMargin(QFontEngine::Format_ARGB), t, color);
1064 }
1065 
1066 QT_END_NAMESPACE
1067 
1068 #endif // QT_NO_DIRECTWRITE
1069