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