Warning: That file was not part of the compilation database. It may have many parsing errors.
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 | #include "qwindowsfontengine_p.h" |
41 | #include "qwindowsnativeimage_p.h" |
42 | #include "qwindowsfontdatabase_p.h" |
43 | #include <QtCore/qt_windows.h> |
44 | #include "qwindowsfontenginedirectwrite_p.h" |
45 | |
46 | #include <QtGui/qpa/qplatformintegration.h> |
47 | #include <QtGui/private/qtextengine_p.h> // glyph_metrics_t |
48 | #include <QtGui/private/qguiapplication_p.h> |
49 | #include <QtGui/QPaintDevice> |
50 | #include <QtGui/QBitmap> |
51 | #include <QtGui/QPainter> |
52 | #include <QtGui/private/qpainter_p.h> |
53 | #include <QtGui/QPaintEngine> |
54 | #include <QtGui/private/qpaintengine_raster_p.h> |
55 | |
56 | #include <QtCore/QtEndian> |
57 | #include <QtCore/QFile> |
58 | #include <QtCore/qmath.h> |
59 | #include <QtCore/QTextStream> |
60 | #include <QtCore/QThreadStorage> |
61 | #include <QtCore/private/qsystemlibrary_p.h> |
62 | #include <QtCore/private/qstringiterator_p.h> |
63 | |
64 | #include <QtCore/QDebug> |
65 | |
66 | #include <limits.h> |
67 | |
68 | #if !defined(QT_NO_DIRECTWRITE) |
69 | # include <dwrite.h> |
70 | # include <comdef.h> |
71 | #endif |
72 | |
73 | QT_BEGIN_NAMESPACE |
74 | |
75 | //### mingw needed define |
76 | #ifndef TT_PRIM_CSPLINE |
77 | #define TT_PRIM_CSPLINE 3 |
78 | #endif |
79 | |
80 | // GetFontData expects the tags in little endian ;( |
81 | #define MAKE_LITTLE_ENDIAN_TAG(ch1, ch2, ch3, ch4) (\ |
82 | (((quint32)(ch4)) << 24) | \ |
83 | (((quint32)(ch3)) << 16) | \ |
84 | (((quint32)(ch2)) << 8) | \ |
85 | ((quint32)(ch1)) \ |
86 | ) |
87 | |
88 | // common DC for all fonts |
89 | |
90 | typedef BOOL (WINAPI *PtrGetCharWidthI)(HDC, UINT, UINT, LPWORD, LPINT); |
91 | static PtrGetCharWidthI ptrGetCharWidthI = 0; |
92 | static bool resolvedGetCharWidthI = false; |
93 | |
94 | static void resolveGetCharWidthI() |
95 | { |
96 | if (resolvedGetCharWidthI) |
97 | return; |
98 | resolvedGetCharWidthI = true; |
99 | ptrGetCharWidthI = (PtrGetCharWidthI)QSystemLibrary::resolve(QStringLiteral("gdi32"), "GetCharWidthI"); |
100 | } |
101 | |
102 | // general font engine |
103 | |
104 | QFixed QWindowsFontEngine::lineThickness() const |
105 | { |
106 | if(lineWidth > 0) |
107 | return lineWidth; |
108 | |
109 | return QFontEngine::lineThickness(); |
110 | } |
111 | |
112 | static OUTLINETEXTMETRIC *getOutlineTextMetric(HDC hdc) |
113 | { |
114 | const auto size = GetOutlineTextMetrics(hdc, 0, nullptr); |
115 | auto otm = reinterpret_cast<OUTLINETEXTMETRIC *>(malloc(size)); |
116 | GetOutlineTextMetrics(hdc, size, otm); |
117 | return otm; |
118 | } |
119 | |
120 | bool QWindowsFontEngine::hasCFFTable() const |
121 | { |
122 | HDC hdc = m_fontEngineData->hdc; |
123 | SelectObject(hdc, hfont); |
124 | return GetFontData(hdc, MAKE_LITTLE_ENDIAN_TAG('C', 'F', 'F', ' '), 0, 0, 0) != GDI_ERROR; |
125 | } |
126 | |
127 | bool QWindowsFontEngine::hasCMapTable() const |
128 | { |
129 | HDC hdc = m_fontEngineData->hdc; |
130 | SelectObject(hdc, hfont); |
131 | return GetFontData(hdc, MAKE_LITTLE_ENDIAN_TAG('c', 'm', 'a', 'p'), 0, 0, 0) != GDI_ERROR; |
132 | } |
133 | |
134 | static inline QString stringFromOutLineTextMetric(const OUTLINETEXTMETRIC *otm, PSTR offset) |
135 | { |
136 | const uchar *p = reinterpret_cast<const uchar *>(otm) + quintptr(offset); |
137 | return QString::fromWCharArray(reinterpret_cast<const wchar_t *>(p)); |
138 | } |
139 | |
140 | void QWindowsFontEngine::getCMap() |
141 | { |
142 | ttf = (bool)(tm.tmPitchAndFamily & TMPF_TRUETYPE) || hasCMapTable(); |
143 | |
144 | cffTable = hasCFFTable(); |
145 | |
146 | HDC hdc = m_fontEngineData->hdc; |
147 | SelectObject(hdc, hfont); |
148 | bool symb = false; |
149 | if (ttf) { |
150 | cmapTable = getSfntTable(MAKE_TAG('c', 'm', 'a', 'p')); |
151 | cmap = QFontEngine::getCMap(reinterpret_cast<const uchar *>(cmapTable.constData()), |
152 | cmapTable.size(), &symb, &cmapSize); |
153 | } |
154 | if (!cmap) { |
155 | ttf = false; |
156 | symb = false; |
157 | } |
158 | symbol = symb; |
159 | designToDevice = 1; |
160 | _faceId.index = 0; |
161 | if(cmap) { |
162 | OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); |
163 | unitsPerEm = int(otm->otmEMSquare); |
164 | const QFixed unitsPerEmF(unitsPerEm); |
165 | designToDevice = unitsPerEmF / QFixed::fromReal(fontDef.pixelSize); |
166 | x_height = int(otm->otmsXHeight); |
167 | loadKerningPairs(designToDevice); |
168 | _faceId.filename = QFile::encodeName(stringFromOutLineTextMetric(otm, otm->otmpFullName)); |
169 | lineWidth = otm->otmsUnderscoreSize; |
170 | fsType = otm->otmfsType; |
171 | free(otm); |
172 | |
173 | } else { |
174 | unitsPerEm = tm.tmHeight; |
175 | } |
176 | } |
177 | |
178 | int QWindowsFontEngine::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs) const |
179 | { |
180 | int glyph_pos = 0; |
181 | { |
182 | if (symbol) { |
183 | QStringIterator it(str, str + numChars); |
184 | while (it.hasNext()) { |
185 | const uint uc = it.next(); |
186 | glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc); |
187 | if(!glyphs->glyphs[glyph_pos] && uc < 0x100) |
188 | glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc + 0xf000); |
189 | ++glyph_pos; |
190 | } |
191 | } else if (ttf) { |
192 | QStringIterator it(str, str + numChars); |
193 | while (it.hasNext()) { |
194 | const uint uc = it.next(); |
195 | glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, cmapSize, uc); |
196 | ++glyph_pos; |
197 | } |
198 | } else { |
199 | QStringIterator it(str, str + numChars); |
200 | while (it.hasNext()) { |
201 | const uint uc = it.next(); |
202 | if (uc >= tm.tmFirstChar && uc <= tm.tmLastChar) |
203 | glyphs->glyphs[glyph_pos] = uc; |
204 | else |
205 | glyphs->glyphs[glyph_pos] = 0; |
206 | ++glyph_pos; |
207 | } |
208 | } |
209 | } |
210 | glyphs->numGlyphs = glyph_pos; |
211 | return glyph_pos; |
212 | } |
213 | |
214 | /*! |
215 | \class QWindowsFontEngine |
216 | \brief Standard Windows font engine. |
217 | \internal |
218 | \ingroup qt-lighthouse-win |
219 | |
220 | Will probably be superseded by a common Free Type font engine in Qt 5.X. |
221 | */ |
222 | |
223 | QWindowsFontEngine::QWindowsFontEngine(const QString &name, |
224 | LOGFONT lf, |
225 | const QSharedPointer<QWindowsFontEngineData> &fontEngineData) |
226 | : QFontEngine(Win), |
227 | m_fontEngineData(fontEngineData), |
228 | _name(name), |
229 | m_logfont(lf), |
230 | ttf(0), |
231 | hasOutline(0) |
232 | { |
233 | qCDebug(lcQpaFonts) << __FUNCTION__ << name << lf.lfHeight; |
234 | hfont = CreateFontIndirect(&m_logfont); |
235 | if (!hfont) { |
236 | qErrnoWarning("%s: CreateFontIndirect failed for family '%s'", __FUNCTION__, qPrintable(name)); |
237 | hfont = QWindowsFontDatabase::systemFont(); |
238 | } |
239 | |
240 | HDC hdc = m_fontEngineData->hdc; |
241 | SelectObject(hdc, hfont); |
242 | const BOOL res = GetTextMetrics(hdc, &tm); |
243 | if (!res) { |
244 | qErrnoWarning("%s: GetTextMetrics failed", __FUNCTION__); |
245 | ZeroMemory(&tm, sizeof(TEXTMETRIC)); |
246 | } |
247 | |
248 | fontDef.pixelSize = -lf.lfHeight; |
249 | fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); |
250 | |
251 | cache_cost = tm.tmHeight * tm.tmAveCharWidth * 2000; |
252 | getCMap(); |
253 | |
254 | if (!resolvedGetCharWidthI) |
255 | resolveGetCharWidthI(); |
256 | |
257 | // ### Properties accessed by QWin32PrintEngine (QtPrintSupport) |
258 | QVariantMap userData; |
259 | userData.insert(QStringLiteral("logFont"), QVariant::fromValue(m_logfont)); |
260 | userData.insert(QStringLiteral("hFont"), QVariant::fromValue(hfont)); |
261 | userData.insert(QStringLiteral("trueType"), QVariant(bool(ttf))); |
262 | setUserData(userData); |
263 | |
264 | hasUnreliableOutline = (tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) == 0; |
265 | } |
266 | |
267 | QWindowsFontEngine::~QWindowsFontEngine() |
268 | { |
269 | if (designAdvances) |
270 | free(designAdvances); |
271 | |
272 | if (widthCache) |
273 | free(widthCache); |
274 | |
275 | // make sure we aren't by accident still selected |
276 | SelectObject(m_fontEngineData->hdc, QWindowsFontDatabase::systemFont()); |
277 | |
278 | if (!DeleteObject(hfont)) |
279 | qErrnoWarning("%s: QFontEngineWin: failed to delete font...", __FUNCTION__); |
280 | qCDebug(lcQpaFonts) << __FUNCTION__ << _name; |
281 | |
282 | if (!uniqueFamilyName.isEmpty()) { |
283 | if (QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration()) { |
284 | QPlatformFontDatabase *pfdb = pi->fontDatabase(); |
285 | static_cast<QWindowsFontDatabase *>(pfdb)->derefUniqueFont(uniqueFamilyName); |
286 | } |
287 | } |
288 | } |
289 | |
290 | glyph_t QWindowsFontEngine::glyphIndex(uint ucs4) const |
291 | { |
292 | glyph_t glyph = 0; |
293 | |
294 | if (symbol) { |
295 | glyph = getTrueTypeGlyphIndex(cmap, cmapSize, ucs4); |
296 | if (glyph == 0 && ucs4 < 0x100) |
297 | glyph = getTrueTypeGlyphIndex(cmap, cmapSize, ucs4 + 0xf000); |
298 | } else if (ttf) { |
299 | glyph = getTrueTypeGlyphIndex(cmap, cmapSize, ucs4); |
300 | } else if (ucs4 >= tm.tmFirstChar && ucs4 <= tm.tmLastChar) { |
301 | glyph = ucs4; |
302 | } |
303 | |
304 | return glyph; |
305 | } |
306 | |
307 | HGDIOBJ QWindowsFontEngine::selectDesignFont() const |
308 | { |
309 | LOGFONT f = m_logfont; |
310 | f.lfHeight = -unitsPerEm; |
311 | f.lfWidth = 0; |
312 | HFONT designFont = CreateFontIndirect(&f); |
313 | return SelectObject(m_fontEngineData->hdc, designFont); |
314 | } |
315 | |
316 | bool QWindowsFontEngine::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QFontEngine::ShaperFlags flags) const |
317 | { |
318 | Q_ASSERT(glyphs->numGlyphs >= *nglyphs); |
319 | if (*nglyphs < len) { |
320 | *nglyphs = len; |
321 | return false; |
322 | } |
323 | |
324 | glyphs->numGlyphs = *nglyphs; |
325 | *nglyphs = getGlyphIndexes(str, len, glyphs); |
326 | |
327 | if (!(flags & GlyphIndicesOnly)) |
328 | recalcAdvances(glyphs, flags); |
329 | |
330 | return true; |
331 | } |
332 | |
333 | inline void calculateTTFGlyphWidth(HDC hdc, UINT glyph, int &width) |
334 | { |
335 | if (ptrGetCharWidthI) |
336 | ptrGetCharWidthI(hdc, glyph, 1, 0, &width); |
337 | } |
338 | |
339 | void QWindowsFontEngine::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const |
340 | { |
341 | HGDIOBJ oldFont = 0; |
342 | HDC hdc = m_fontEngineData->hdc; |
343 | if (ttf && (flags & DesignMetrics)) { |
344 | for(int i = 0; i < glyphs->numGlyphs; i++) { |
345 | unsigned int glyph = glyphs->glyphs[i]; |
346 | if(int(glyph) >= designAdvancesSize) { |
347 | const int newSize = int(glyph + 256) >> 8 << 8; |
348 | designAdvances = reinterpret_cast<QFixed *>(realloc(designAdvances, size_t(newSize) * sizeof(QFixed))); |
349 | Q_CHECK_PTR(designAdvances); |
350 | for(int i = designAdvancesSize; i < newSize; ++i) |
351 | designAdvances[i] = -1000000; |
352 | designAdvancesSize = newSize; |
353 | } |
354 | if (designAdvances[glyph] < -999999) { |
355 | if (!oldFont) |
356 | oldFont = selectDesignFont(); |
357 | |
358 | int width = 0; |
359 | calculateTTFGlyphWidth(hdc, glyph, width); |
360 | designAdvances[glyph] = QFixed(width) / designToDevice; |
361 | } |
362 | glyphs->advances[i] = designAdvances[glyph]; |
363 | } |
364 | if(oldFont) |
365 | DeleteObject(SelectObject(hdc, oldFont)); |
366 | } else { |
367 | for(int i = 0; i < glyphs->numGlyphs; i++) { |
368 | unsigned int glyph = glyphs->glyphs[i]; |
369 | |
370 | if (glyph >= widthCacheSize) { |
371 | const uint newSize = (glyph + 256) >> 8 << 8; |
372 | widthCache = reinterpret_cast<unsigned char *>(realloc(widthCache, newSize * sizeof(QFixed))); |
373 | Q_CHECK_PTR(widthCache); |
374 | memset(widthCache + widthCacheSize, 0, newSize - widthCacheSize); |
375 | widthCacheSize = newSize; |
376 | } |
377 | glyphs->advances[i] = widthCache[glyph]; |
378 | // font-width cache failed |
379 | if (glyphs->advances[i].value() == 0) { |
380 | int width = 0; |
381 | if (!oldFont) |
382 | oldFont = SelectObject(hdc, hfont); |
383 | |
384 | if (!ttf) { |
385 | QChar ch[2] = { ushort(glyph), 0 }; |
386 | int chrLen = 1; |
387 | if (QChar::requiresSurrogates(glyph)) { |
388 | ch[0] = QChar::highSurrogate(glyph); |
389 | ch[1] = QChar::lowSurrogate(glyph); |
390 | ++chrLen; |
391 | } |
392 | SIZE size = {0, 0}; |
393 | GetTextExtentPoint32(hdc, reinterpret_cast<const wchar_t *>(ch), chrLen, &size); |
394 | width = size.cx; |
395 | } else { |
396 | calculateTTFGlyphWidth(hdc, glyph, width); |
397 | } |
398 | glyphs->advances[i] = width; |
399 | // if glyph's within cache range, store it for later |
400 | if (width > 0 && width < 0x100) |
401 | widthCache[glyph] = uchar(width); |
402 | } |
403 | } |
404 | |
405 | if (oldFont) |
406 | SelectObject(hdc, oldFont); |
407 | } |
408 | } |
409 | |
410 | glyph_metrics_t QWindowsFontEngine::boundingBox(const QGlyphLayout &glyphs) |
411 | { |
412 | if (glyphs.numGlyphs == 0) |
413 | return glyph_metrics_t(); |
414 | |
415 | QFixed w = 0; |
416 | for (int i = 0; i < glyphs.numGlyphs; ++i) |
417 | w += glyphs.effectiveAdvance(i); |
418 | |
419 | return glyph_metrics_t(0, -tm.tmAscent, w - lastRightBearing(glyphs), tm.tmHeight, w, 0); |
420 | } |
421 | |
422 | bool QWindowsFontEngine::getOutlineMetrics(glyph_t glyph, const QTransform &t, glyph_metrics_t *metrics) const |
423 | { |
424 | Q_ASSERT(metrics != 0); |
425 | |
426 | HDC hdc = m_fontEngineData->hdc; |
427 | |
428 | GLYPHMETRICS gm; |
429 | DWORD res = 0; |
430 | MAT2 mat; |
431 | mat.eM11.value = mat.eM22.value = 1; |
432 | mat.eM11.fract = mat.eM22.fract = 0; |
433 | mat.eM21.value = mat.eM12.value = 0; |
434 | mat.eM21.fract = mat.eM12.fract = 0; |
435 | |
436 | if (t.type() > QTransform::TxTranslate) { |
437 | // We need to set the transform using the HDC's world |
438 | // matrix rather than using the MAT2 above, because the |
439 | // results provided when transforming via MAT2 does not |
440 | // match the glyphs that are drawn using a WorldTransform |
441 | XFORM xform; |
442 | xform.eM11 = FLOAT(t.m11()); |
443 | xform.eM12 = FLOAT(t.m12()); |
444 | xform.eM21 = FLOAT(t.m21()); |
445 | xform.eM22 = FLOAT(t.m22()); |
446 | xform.eDx = 0; |
447 | xform.eDy = 0; |
448 | SetGraphicsMode(hdc, GM_ADVANCED); |
449 | SetWorldTransform(hdc, &xform); |
450 | } |
451 | |
452 | uint format = GGO_METRICS; |
453 | if (ttf) |
454 | format |= GGO_GLYPH_INDEX; |
455 | res = GetGlyphOutline(hdc, glyph, format, &gm, 0, 0, &mat); |
456 | |
457 | if (t.type() > QTransform::TxTranslate) { |
458 | XFORM xform; |
459 | xform.eM11 = xform.eM22 = 1; |
460 | xform.eM12 = xform.eM21 = xform.eDx = xform.eDy = 0; |
461 | SetWorldTransform(hdc, &xform); |
462 | SetGraphicsMode(hdc, GM_COMPATIBLE); |
463 | } |
464 | |
465 | if (res != GDI_ERROR) { |
466 | *metrics = glyph_metrics_t(gm.gmptGlyphOrigin.x, -gm.gmptGlyphOrigin.y, |
467 | int(gm.gmBlackBoxX), int(gm.gmBlackBoxY), |
468 | gm.gmCellIncX, gm.gmCellIncY); |
469 | return true; |
470 | } else { |
471 | return false; |
472 | } |
473 | } |
474 | |
475 | glyph_metrics_t QWindowsFontEngine::boundingBox(glyph_t glyph, const QTransform &t) |
476 | { |
477 | HDC hdc = m_fontEngineData->hdc; |
478 | SelectObject(hdc, hfont); |
479 | |
480 | glyph_metrics_t glyphMetrics; |
481 | bool success = getOutlineMetrics(glyph, t, &glyphMetrics); |
482 | |
483 | if (!ttf && !success) { |
484 | // Bitmap fonts |
485 | wchar_t ch = wchar_t(glyph); |
486 | ABCFLOAT abc; |
487 | GetCharABCWidthsFloat(hdc, ch, ch, &abc); |
488 | int width = qRound(abc.abcfB); |
489 | |
490 | return glyph_metrics_t(QFixed::fromReal(abc.abcfA), -tm.tmAscent, width, tm.tmHeight, width, 0).transformed(t); |
491 | } |
492 | |
493 | return glyphMetrics; |
494 | } |
495 | |
496 | QFixed QWindowsFontEngine::ascent() const |
497 | { |
498 | return tm.tmAscent; |
499 | } |
500 | |
501 | QFixed QWindowsFontEngine::descent() const |
502 | { |
503 | return tm.tmDescent; |
504 | } |
505 | |
506 | QFixed QWindowsFontEngine::leading() const |
507 | { |
508 | return tm.tmExternalLeading; |
509 | } |
510 | |
511 | namespace { |
512 | # pragma pack(1) |
513 | |
514 | struct OS2Table |
515 | { |
516 | quint16 version; |
517 | qint16 avgCharWidth; |
518 | quint16 weightClass; |
519 | quint16 widthClass; |
520 | quint16 type; |
521 | qint16 subscriptXSize; |
522 | qint16 subscriptYSize; |
523 | qint16 subscriptXOffset; |
524 | qint16 subscriptYOffset; |
525 | qint16 superscriptXSize; |
526 | qint16 superscriptYSize; |
527 | qint16 superscriptXOffset; |
528 | qint16 superscriptYOffset; |
529 | qint16 strikeOutSize; |
530 | qint16 strikeOutPosition; |
531 | qint16 familyClass; |
532 | quint8 panose[10]; |
533 | quint32 unicodeRanges[4]; |
534 | quint8 vendorID[4]; |
535 | quint16 selection; |
536 | quint16 firstCharIndex; |
537 | quint16 lastCharIndex; |
538 | qint16 typoAscender; |
539 | qint16 typoDescender; |
540 | qint16 typoLineGap; |
541 | quint16 winAscent; |
542 | quint16 winDescent; |
543 | quint32 codepageRanges[2]; |
544 | qint16 height; |
545 | qint16 capHeight; |
546 | quint16 defaultChar; |
547 | quint16 breakChar; |
548 | quint16 maxContext; |
549 | }; |
550 | |
551 | # pragma pack() |
552 | } |
553 | |
554 | QFixed QWindowsFontEngine::capHeight() const |
555 | { |
556 | const QByteArray tableData = getSfntTable(MAKE_TAG('O', 'S', '/', '2')); |
557 | if (size_t(tableData.size()) >= sizeof(OS2Table)) { |
558 | const OS2Table *table = reinterpret_cast<const OS2Table *>(tableData.constData()); |
559 | if (qFromBigEndian<quint16>(table->version) >= 2) { |
560 | qint16 capHeight = qFromBigEndian<qint16>(table->capHeight); |
561 | if (capHeight > 0) |
562 | return QFixed(capHeight) / designToDevice; |
563 | } |
564 | } |
565 | return calculatedCapHeight(); |
566 | } |
567 | |
568 | QFixed QWindowsFontEngine::xHeight() const |
569 | { |
570 | if(x_height >= 0) |
571 | return x_height; |
572 | return QFontEngine::xHeight(); |
573 | } |
574 | |
575 | QFixed QWindowsFontEngine::averageCharWidth() const |
576 | { |
577 | return tm.tmAveCharWidth; |
578 | } |
579 | |
580 | qreal QWindowsFontEngine::maxCharWidth() const |
581 | { |
582 | return tm.tmMaxCharWidth; |
583 | } |
584 | |
585 | enum { max_font_count = 256 }; |
586 | static const ushort char_table[] = { |
587 | 40, |
588 | 67, |
589 | 70, |
590 | 75, |
591 | 86, |
592 | 88, |
593 | 89, |
594 | 91, |
595 | 102, |
596 | 114, |
597 | 124, |
598 | 127, |
599 | 205, |
600 | 645, |
601 | 884, |
602 | 922, |
603 | 1070, |
604 | 12386, |
605 | 0 |
606 | }; |
607 | |
608 | static const int char_table_entries = sizeof(char_table)/sizeof(ushort); |
609 | |
610 | #ifndef Q_CC_MINGW |
611 | void QWindowsFontEngine::getGlyphBearings(glyph_t glyph, qreal *leftBearing, qreal *rightBearing) |
612 | { |
613 | HDC hdc = m_fontEngineData->hdc; |
614 | SelectObject(hdc, hfont); |
615 | |
616 | if (ttf) { |
617 | ABC abcWidths; |
618 | GetCharABCWidthsI(hdc, glyph, 1, 0, &abcWidths); |
619 | if (leftBearing) |
620 | *leftBearing = abcWidths.abcA; |
621 | if (rightBearing) |
622 | *rightBearing = abcWidths.abcC; |
623 | } else { |
624 | QFontEngine::getGlyphBearings(glyph, leftBearing, rightBearing); |
625 | } |
626 | } |
627 | #endif // Q_CC_MINGW |
628 | |
629 | bool QWindowsFontEngine::hasUnreliableGlyphOutline() const |
630 | { |
631 | return hasUnreliableOutline || QFontEngine::hasUnreliableGlyphOutline(); |
632 | } |
633 | |
634 | qreal QWindowsFontEngine::minLeftBearing() const |
635 | { |
636 | if (lbearing == SHRT_MIN) |
637 | minRightBearing(); // calculates both |
638 | |
639 | return lbearing; |
640 | } |
641 | |
642 | qreal QWindowsFontEngine::minRightBearing() const |
643 | { |
644 | if (rbearing == SHRT_MIN) { |
645 | int ml = 0; |
646 | int mr = 0; |
647 | HDC hdc = m_fontEngineData->hdc; |
648 | SelectObject(hdc, hfont); |
649 | if (ttf) { |
650 | ABC *abc = 0; |
651 | int n = tm.tmLastChar - tm.tmFirstChar; |
652 | if (n <= max_font_count) { |
653 | abc = new ABC[n+1]; |
654 | GetCharABCWidths(hdc, tm.tmFirstChar, tm.tmLastChar, abc); |
655 | } else { |
656 | abc = new ABC[char_table_entries+1]; |
657 | for(int i = 0; i < char_table_entries; i++) |
658 | GetCharABCWidths(hdc, char_table[i], char_table[i], abc + i); |
659 | n = char_table_entries; |
660 | } |
661 | ml = abc[0].abcA; |
662 | mr = abc[0].abcC; |
663 | for (int i = 1; i < n; i++) { |
664 | if (abc[i].abcA + abc[i].abcB + abc[i].abcC != 0) { |
665 | ml = qMin(ml,abc[i].abcA); |
666 | mr = qMin(mr,abc[i].abcC); |
667 | } |
668 | } |
669 | delete [] abc; |
670 | } else { |
671 | ABCFLOAT *abc = 0; |
672 | int n = tm.tmLastChar - tm.tmFirstChar+1; |
673 | if (n <= max_font_count) { |
674 | abc = new ABCFLOAT[n]; |
675 | GetCharABCWidthsFloat(hdc, tm.tmFirstChar, tm.tmLastChar, abc); |
676 | } else { |
677 | abc = new ABCFLOAT[char_table_entries]; |
678 | for(int i = 0; i < char_table_entries; i++) |
679 | GetCharABCWidthsFloat(hdc, char_table[i], char_table[i], abc+i); |
680 | n = char_table_entries; |
681 | } |
682 | float fml = abc[0].abcfA; |
683 | float fmr = abc[0].abcfC; |
684 | for (int i=1; i<n; i++) { |
685 | if (abc[i].abcfA + abc[i].abcfB + abc[i].abcfC != 0) { |
686 | fml = qMin(fml,abc[i].abcfA); |
687 | fmr = qMin(fmr,abc[i].abcfC); |
688 | } |
689 | } |
690 | ml = int(fml - 0.9999); |
691 | mr = int(fmr - 0.9999); |
692 | delete [] abc; |
693 | } |
694 | lbearing = ml; |
695 | rbearing = mr; |
696 | } |
697 | |
698 | return rbearing; |
699 | } |
700 | |
701 | static inline double qt_fixed_to_double(const FIXED &p) { |
702 | return ((p.value << 16) + p.fract) / 65536.0; |
703 | } |
704 | |
705 | static inline QPointF qt_to_qpointf(const POINTFX &pt, qreal scale, qreal stretch) { |
706 | return QPointF(qt_fixed_to_double(pt.x) * scale * stretch, -qt_fixed_to_double(pt.y) * scale); |
707 | } |
708 | |
709 | #ifndef GGO_UNHINTED |
710 | #define GGO_UNHINTED 0x0100 |
711 | #endif |
712 | |
713 | static bool addGlyphToPath(glyph_t glyph, const QFixedPoint &position, HDC hdc, |
714 | QPainterPath *path, bool ttf, glyph_metrics_t *metric = 0, |
715 | qreal scale = 1.0, qreal stretch = 1.0) |
716 | { |
717 | MAT2 mat; |
718 | mat.eM11.value = mat.eM22.value = 1; |
719 | mat.eM11.fract = mat.eM22.fract = 0; |
720 | mat.eM21.value = mat.eM12.value = 0; |
721 | mat.eM21.fract = mat.eM12.fract = 0; |
722 | |
723 | GLYPHMETRICS gMetric; |
724 | memset(&gMetric, 0, sizeof(GLYPHMETRICS)); |
725 | |
726 | if (metric) { |
727 | // If metrics requested, retrieve first using GGO_METRICS, because the returned |
728 | // values are incorrect for OpenType PS fonts if obtained at the same time as the |
729 | // glyph paths themselves (ie. with GGO_NATIVE as the format). |
730 | uint format = GGO_METRICS; |
731 | if (ttf) |
732 | format |= GGO_GLYPH_INDEX; |
733 | if (GetGlyphOutline(hdc, glyph, format, &gMetric, 0, 0, &mat) == GDI_ERROR) |
734 | return false; |
735 | // #### obey scale |
736 | *metric = glyph_metrics_t(gMetric.gmptGlyphOrigin.x, -gMetric.gmptGlyphOrigin.y, |
737 | int(gMetric.gmBlackBoxX), int(gMetric.gmBlackBoxY), |
738 | gMetric.gmCellIncX, gMetric.gmCellIncY); |
739 | } |
740 | |
741 | uint glyphFormat = GGO_NATIVE; |
742 | |
743 | if (ttf) |
744 | glyphFormat |= GGO_GLYPH_INDEX; |
745 | |
746 | const DWORD bufferSize = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, 0, 0, &mat); |
747 | if (bufferSize == GDI_ERROR) |
748 | return false; |
749 | |
750 | char *dataBuffer = new char[bufferSize]; |
751 | DWORD ret = GDI_ERROR; |
752 | ret = GetGlyphOutline(hdc, glyph, glyphFormat, &gMetric, bufferSize, dataBuffer, &mat); |
753 | if (ret == GDI_ERROR) { |
754 | delete [] dataBuffer; |
755 | return false; |
756 | } |
757 | |
758 | DWORD offset = 0; |
759 | DWORD headerOffset = 0; |
760 | |
761 | QPointF oset = position.toPointF(); |
762 | while (headerOffset < bufferSize) { |
763 | const TTPOLYGONHEADER *ttph = reinterpret_cast<const TTPOLYGONHEADER *>(dataBuffer + headerOffset); |
764 | |
765 | QPointF lastPoint(qt_to_qpointf(ttph->pfxStart, scale, stretch)); |
766 | path->moveTo(lastPoint + oset); |
767 | offset += sizeof(TTPOLYGONHEADER); |
768 | while (offset < headerOffset + ttph->cb) { |
769 | const TTPOLYCURVE *curve = reinterpret_cast<const TTPOLYCURVE *>(dataBuffer + offset); |
770 | switch (curve->wType) { |
771 | case TT_PRIM_LINE: { |
772 | for (int i=0; i<curve->cpfx; ++i) { |
773 | QPointF p = qt_to_qpointf(curve->apfx[i], scale, stretch) + oset; |
774 | path->lineTo(p); |
775 | } |
776 | break; |
777 | } |
778 | case TT_PRIM_QSPLINE: { |
779 | const QPainterPath::Element &elm = path->elementAt(path->elementCount()-1); |
780 | QPointF prev(elm.x, elm.y); |
781 | QPointF endPoint; |
782 | for (int i=0; i<curve->cpfx - 1; ++i) { |
783 | QPointF p1 = qt_to_qpointf(curve->apfx[i], scale, stretch) + oset; |
784 | QPointF p2 = qt_to_qpointf(curve->apfx[i+1], scale, stretch) + oset; |
785 | if (i < curve->cpfx - 2) { |
786 | endPoint = QPointF((p1.x() + p2.x()) / 2, (p1.y() + p2.y()) / 2); |
787 | } else { |
788 | endPoint = p2; |
789 | } |
790 | |
791 | path->quadTo(p1, endPoint); |
792 | prev = endPoint; |
793 | } |
794 | |
795 | break; |
796 | } |
797 | case TT_PRIM_CSPLINE: { |
798 | for (int i=0; i<curve->cpfx; ) { |
799 | QPointF p2 = qt_to_qpointf(curve->apfx[i++], scale, stretch) + oset; |
800 | QPointF p3 = qt_to_qpointf(curve->apfx[i++], scale, stretch) + oset; |
801 | QPointF p4 = qt_to_qpointf(curve->apfx[i++], scale, stretch) + oset; |
802 | path->cubicTo(p2, p3, p4); |
803 | } |
804 | break; |
805 | } |
806 | default: |
807 | qWarning("QFontEngineWin::addOutlineToPath, unhandled switch case"); |
808 | } |
809 | offset += sizeof(TTPOLYCURVE) + (curve->cpfx-1) * sizeof(POINTFX); |
810 | } |
811 | path->closeSubpath(); |
812 | headerOffset += ttph->cb; |
813 | } |
814 | delete [] dataBuffer; |
815 | |
816 | return true; |
817 | } |
818 | |
819 | void QWindowsFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, |
820 | QPainterPath *path, QTextItem::RenderFlags) |
821 | { |
822 | LOGFONT lf = m_logfont; |
823 | // The sign must be negative here to make sure we match against character height instead of |
824 | // hinted cell height. This ensures that we get linear matching, and we need this for |
825 | // paths since we later on apply a scaling transform to the glyph outline to get the |
826 | // font at the correct pixel size. |
827 | lf.lfHeight = -unitsPerEm; |
828 | lf.lfWidth = 0; |
829 | HFONT hf = CreateFontIndirect(&lf); |
830 | HDC hdc = m_fontEngineData->hdc; |
831 | HGDIOBJ oldfont = SelectObject(hdc, hf); |
832 | |
833 | qreal scale = qreal(fontDef.pixelSize) / unitsPerEm; |
834 | qreal stretch = fontDef.stretch ? qreal(fontDef.stretch) / 100 : 1.0; |
835 | for(int i = 0; i < nglyphs; ++i) { |
836 | if (!addGlyphToPath(glyphs[i], positions[i], hdc, path, ttf, /*metric*/0, |
837 | scale, stretch)) { |
838 | // Some windows fonts, like "Modern", are vector stroke |
839 | // fonts, which are reported as TMPF_VECTOR but do not |
840 | // support GetGlyphOutline, and thus we set this bit so |
841 | // that addOutLineToPath can check it and return safely... |
842 | hasOutline = false; |
843 | break; |
844 | } |
845 | } |
846 | DeleteObject(SelectObject(hdc, oldfont)); |
847 | } |
848 | |
849 | void QWindowsFontEngine::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, |
850 | QPainterPath *path, QTextItem::RenderFlags flags) |
851 | { |
852 | if(tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) { |
853 | hasOutline = true; |
854 | QFontEngine::addOutlineToPath(x, y, glyphs, path, flags); |
855 | if (hasOutline) { |
856 | // has_outline is set to false if addGlyphToPath gets |
857 | // false from GetGlyphOutline, meaning its not an outline |
858 | // font. |
859 | return; |
860 | } |
861 | } |
862 | QFontEngine::addBitmapFontToPath(x, y, glyphs, path, flags); |
863 | } |
864 | |
865 | QFontEngine::FaceId QWindowsFontEngine::faceId() const |
866 | { |
867 | return _faceId; |
868 | } |
869 | |
870 | QT_BEGIN_INCLUDE_NAMESPACE |
871 | #include <qdebug.h> |
872 | QT_END_INCLUDE_NAMESPACE |
873 | |
874 | int QWindowsFontEngine::synthesized() const |
875 | { |
876 | if(synthesized_flags == -1) { |
877 | synthesized_flags = 0; |
878 | if(ttf) { |
879 | const DWORD HEAD = MAKE_LITTLE_ENDIAN_TAG('h', 'e', 'a', 'd'); |
880 | HDC hdc = m_fontEngineData->hdc; |
881 | SelectObject(hdc, hfont); |
882 | uchar data[4]; |
883 | GetFontData(hdc, HEAD, 44, &data, 4); |
884 | USHORT macStyle = qt_getUShort(data); |
885 | if (tm.tmItalic && !(macStyle & 2)) |
886 | synthesized_flags = SynthesizedItalic; |
887 | if (fontDef.stretch != 100 && ttf) |
888 | synthesized_flags |= SynthesizedStretch; |
889 | if (tm.tmWeight >= 500 && tm.tmWeight < 750 && !(macStyle & 1)) |
890 | synthesized_flags |= SynthesizedBold; |
891 | //qDebug() << "font is" << _name << |
892 | // "it=" << (macStyle & 2) << fontDef.style << "flags=" << synthesized_flags; |
893 | } |
894 | } |
895 | return synthesized_flags; |
896 | } |
897 | |
898 | QFixed QWindowsFontEngine::emSquareSize() const |
899 | { |
900 | return unitsPerEm; |
901 | } |
902 | |
903 | QFontEngine::Properties QWindowsFontEngine::properties() const |
904 | { |
905 | LOGFONT lf = m_logfont; |
906 | lf.lfHeight = unitsPerEm; |
907 | HFONT hf = CreateFontIndirect(&lf); |
908 | HDC hdc = m_fontEngineData->hdc; |
909 | HGDIOBJ oldfont = SelectObject(hdc, hf); |
910 | OUTLINETEXTMETRIC *otm = getOutlineTextMetric(hdc); |
911 | Properties p; |
912 | p.emSquare = unitsPerEm; |
913 | p.italicAngle = otm->otmItalicAngle; |
914 | const QByteArray name = stringFromOutLineTextMetric(otm, otm->otmpFamilyName).toLatin1() |
915 | + stringFromOutLineTextMetric(otm, otm->otmpStyleName).toLatin1(); |
916 | p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(name); |
917 | p.boundingBox = QRectF(otm->otmrcFontBox.left, -otm->otmrcFontBox.top, |
918 | otm->otmrcFontBox.right - otm->otmrcFontBox.left, |
919 | otm->otmrcFontBox.top - otm->otmrcFontBox.bottom); |
920 | p.ascent = otm->otmAscent; |
921 | p.descent = -otm->otmDescent; |
922 | p.leading = int(otm->otmLineGap); |
923 | p.capHeight = 0; |
924 | p.lineWidth = otm->otmsUnderscoreSize; |
925 | free(otm); |
926 | DeleteObject(SelectObject(hdc, oldfont)); |
927 | return p; |
928 | } |
929 | |
930 | void QWindowsFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) |
931 | { |
932 | LOGFONT lf = m_logfont; |
933 | lf.lfHeight = -unitsPerEm; |
934 | int flags = synthesized(); |
935 | if(flags & SynthesizedItalic) |
936 | lf.lfItalic = false; |
937 | lf.lfWidth = 0; |
938 | HFONT hf = CreateFontIndirect(&lf); |
939 | HDC hdc = m_fontEngineData->hdc; |
940 | HGDIOBJ oldfont = SelectObject(hdc, hf); |
941 | QFixedPoint p; |
942 | p.x = 0; |
943 | p.y = 0; |
944 | addGlyphToPath(glyph, p, hdc, path, ttf, metrics); |
945 | DeleteObject(SelectObject(hdc, oldfont)); |
946 | } |
947 | |
948 | bool QWindowsFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const |
949 | { |
950 | if (!ttf && !cffTable) |
951 | return false; |
952 | HDC hdc = m_fontEngineData->hdc; |
953 | SelectObject(hdc, hfont); |
954 | DWORD t = qbswap<quint32>(tag); |
955 | *length = GetFontData(hdc, t, 0, buffer, *length); |
956 | Q_ASSERT(*length == GDI_ERROR || int(*length) > 0); |
957 | return *length != GDI_ERROR; |
958 | } |
959 | |
960 | #if !defined(CLEARTYPE_QUALITY) |
961 | # define CLEARTYPE_QUALITY 5 |
962 | #endif |
963 | |
964 | QWindowsNativeImage *QWindowsFontEngine::drawGDIGlyph(HFONT font, glyph_t glyph, int margin, |
965 | const QTransform &t, |
966 | QImage::Format mask_format) |
967 | { |
968 | Q_UNUSED(mask_format) |
969 | glyph_metrics_t gm = boundingBox(glyph); |
970 | |
971 | // printf(" -> for glyph %4x\n", glyph); |
972 | |
973 | int gx = gm.x.toInt(); |
974 | int gy = gm.y.toInt(); |
975 | int iw = gm.width.toInt(); |
976 | int ih = gm.height.toInt(); |
977 | |
978 | if (iw <= 0 || ih <= 0) |
979 | return 0; |
980 | |
981 | bool has_transformation = t.type() > QTransform::TxTranslate; |
982 | |
983 | unsigned int options = ttf ? ETO_GLYPH_INDEX : 0; |
984 | XFORM xform; |
985 | |
986 | if (has_transformation) { |
987 | xform.eM11 = FLOAT(t.m11()); |
988 | xform.eM12 = FLOAT(t.m12()); |
989 | xform.eM21 = FLOAT(t.m21()); |
990 | xform.eM22 = FLOAT(t.m22()); |
991 | xform.eDx = margin; |
992 | xform.eDy = margin; |
993 | |
994 | const HDC hdc = m_fontEngineData->hdc; |
995 | |
996 | SetGraphicsMode(hdc, GM_ADVANCED); |
997 | SetWorldTransform(hdc, &xform); |
998 | HGDIOBJ old_font = SelectObject(hdc, font); |
999 | |
1000 | const UINT ggo_options = GGO_METRICS | (ttf ? GGO_GLYPH_INDEX : 0); |
1001 | GLYPHMETRICS tgm; |
1002 | MAT2 mat; |
1003 | memset(&mat, 0, sizeof(mat)); |
1004 | mat.eM11.value = mat.eM22.value = 1; |
1005 | |
1006 | const DWORD result = GetGlyphOutline(hdc, glyph, ggo_options, &tgm, 0, 0, &mat); |
1007 | |
1008 | XFORM identity = {1, 0, 0, 1, 0, 0}; |
1009 | SetWorldTransform(hdc, &identity); |
1010 | SetGraphicsMode(hdc, GM_COMPATIBLE); |
1011 | SelectObject(hdc, old_font); |
1012 | |
1013 | if (result == GDI_ERROR) { |
1014 | const int errorCode = int(GetLastError()); |
1015 | qErrnoWarning(errorCode, "QWinFontEngine: unable to query transformed glyph metrics (GetGlyphOutline() failed, error %d)...", errorCode); |
1016 | return 0; |
1017 | } |
1018 | |
1019 | iw = int(tgm.gmBlackBoxX); |
1020 | ih = int(tgm.gmBlackBoxY); |
1021 | |
1022 | xform.eDx -= tgm.gmptGlyphOrigin.x; |
1023 | xform.eDy += tgm.gmptGlyphOrigin.y; |
1024 | } |
1025 | |
1026 | // The padding here needs to be kept in sync with the values in alphaMapBoundingBox. |
1027 | QWindowsNativeImage *ni = new QWindowsNativeImage(iw + 2 * margin, |
1028 | ih + 2 * margin, |
1029 | QWindowsNativeImage::systemFormat()); |
1030 | |
1031 | /*If cleartype is enabled we use the standard system format even on Windows CE |
1032 | and not the special textbuffer format we have to use if cleartype is disabled*/ |
1033 | |
1034 | ni->image().fill(0xffffffff); |
1035 | |
1036 | HDC hdc = ni->hdc(); |
1037 | |
1038 | SelectObject(hdc, GetStockObject(NULL_BRUSH)); |
1039 | SelectObject(hdc, GetStockObject(BLACK_PEN)); |
1040 | SetTextColor(hdc, RGB(0,0,0)); |
1041 | SetBkMode(hdc, TRANSPARENT); |
1042 | SetTextAlign(hdc, TA_BASELINE); |
1043 | |
1044 | HGDIOBJ old_font = SelectObject(hdc, font); |
1045 | |
1046 | if (has_transformation) { |
1047 | SetGraphicsMode(hdc, GM_ADVANCED); |
1048 | SetWorldTransform(hdc, &xform); |
1049 | ExtTextOut(hdc, 0, 0, options, 0, reinterpret_cast<LPCWSTR>(&glyph), 1, 0); |
1050 | } else { |
1051 | ExtTextOut(hdc, -gx + margin, -gy + margin, options, 0, reinterpret_cast<LPCWSTR>(&glyph), 1, 0); |
1052 | } |
1053 | |
1054 | SelectObject(hdc, old_font); |
1055 | return ni; |
1056 | } |
1057 | |
1058 | glyph_metrics_t QWindowsFontEngine::alphaMapBoundingBox(glyph_t glyph, QFixed, const QTransform &matrix, GlyphFormat format) |
1059 | { |
1060 | int margin = 0; |
1061 | if (format == QFontEngine::Format_A32 || format == QFontEngine::Format_ARGB) |
1062 | margin = glyphMargin(QFontEngine::Format_A32); |
1063 | glyph_metrics_t gm = boundingBox(glyph, matrix); |
1064 | gm.width += margin * 2; |
1065 | gm.height += margin * 2; |
1066 | return gm; |
1067 | } |
1068 | |
1069 | QImage QWindowsFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &xform) |
1070 | { |
1071 | HFONT font = hfont; |
1072 | |
1073 | bool clearTypeTemporarilyDisabled = (m_fontEngineData->clearTypeEnabled && m_logfont.lfQuality != NONANTIALIASED_QUALITY); |
1074 | if (clearTypeTemporarilyDisabled) { |
1075 | LOGFONT lf = m_logfont; |
1076 | lf.lfQuality = ANTIALIASED_QUALITY; |
1077 | font = CreateFontIndirect(&lf); |
1078 | } |
1079 | QImage::Format mask_format = QWindowsNativeImage::systemFormat(); |
1080 | mask_format = QImage::Format_RGB32; |
1081 | |
1082 | const QWindowsNativeImage *mask = drawGDIGlyph(font, glyph, 0, xform, mask_format); |
1083 | if (mask == 0) { |
1084 | if (m_fontEngineData->clearTypeEnabled) |
1085 | DeleteObject(font); |
1086 | return QImage(); |
1087 | } |
1088 | |
1089 | QImage alphaMap(mask->width(), mask->height(), QImage::Format_Alpha8); |
1090 | |
1091 | |
1092 | // Copy data... Cannot use QPainter here as GDI has messed up the |
1093 | // Alpha channel of the ni.image pixels... |
1094 | for (int y=0; y<mask->height(); ++y) { |
1095 | uchar *dest = alphaMap.scanLine(y); |
1096 | if (mask->image().format() == QImage::Format_RGB16) { |
1097 | const qint16 *src = reinterpret_cast<const qint16 *>(mask->image().constScanLine(y)); |
1098 | for (int x=0; x<mask->width(); ++x) |
1099 | dest[x] = 255 - qGray(src[x]); |
1100 | } else { |
1101 | const uint *src = reinterpret_cast<const uint *>(mask->image().constScanLine(y)); |
1102 | for (int x=0; x<mask->width(); ++x) { |
1103 | if (QWindowsNativeImage::systemFormat() == QImage::Format_RGB16) |
1104 | dest[x] = 255 - qGray(src[x]); |
1105 | else |
1106 | dest[x] = 255 - (m_fontEngineData->pow_gamma[qGray(src[x])] * 255. / 2047.); |
1107 | } |
1108 | } |
1109 | } |
1110 | |
1111 | // Cleanup... |
1112 | delete mask; |
1113 | if (clearTypeTemporarilyDisabled) { |
1114 | DeleteObject(font); |
1115 | } |
1116 | |
1117 | return alphaMap; |
1118 | } |
1119 | |
1120 | #define SPI_GETFONTSMOOTHINGCONTRAST 0x200C |
1121 | #define SPI_SETFONTSMOOTHINGCONTRAST 0x200D |
1122 | |
1123 | QImage QWindowsFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed, const QTransform &t) |
1124 | { |
1125 | HFONT font = hfont; |
1126 | |
1127 | UINT contrast; |
1128 | SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &contrast, 0); |
1129 | SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, reinterpret_cast<void *>(quintptr(1000)), 0); |
1130 | |
1131 | int margin = glyphMargin(QFontEngine::Format_A32); |
1132 | QWindowsNativeImage *mask = drawGDIGlyph(font, glyph, margin, t, QImage::Format_RGB32); |
1133 | SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, reinterpret_cast<void *>(quintptr(contrast)), 0); |
1134 | |
1135 | if (mask == 0) |
1136 | return QImage(); |
1137 | |
1138 | // Gracefully handle the odd case when the display is 16-bit |
1139 | const QImage source = mask->image().depth() == 32 |
1140 | ? mask->image() |
1141 | : mask->image().convertToFormat(QImage::Format_RGB32); |
1142 | |
1143 | QImage rgbMask(mask->width(), mask->height(), QImage::Format_RGB32); |
1144 | for (int y=0; y<mask->height(); ++y) { |
1145 | auto dest = reinterpret_cast<uint *>(rgbMask.scanLine(y)); |
1146 | const uint *src = reinterpret_cast<const uint *>(source.constScanLine(y)); |
1147 | for (int x=0; x<mask->width(); ++x) { |
1148 | dest[x] = 0xffffffff - (0x00ffffff & src[x]); |
1149 | } |
1150 | } |
1151 | |
1152 | delete mask; |
1153 | |
1154 | return rgbMask; |
1155 | } |
1156 | |
1157 | QFontEngine *QWindowsFontEngine::cloneWithSize(qreal pixelSize) const |
1158 | { |
1159 | QFontDef request = fontDef; |
1160 | QString actualFontName = request.family; |
1161 | if (!uniqueFamilyName.isEmpty()) |
1162 | request.family = uniqueFamilyName; |
1163 | request.pixelSize = pixelSize; |
1164 | const QString faceName = QString::fromWCharArray(m_logfont.lfFaceName); |
1165 | |
1166 | QFontEngine *fontEngine = |
1167 | QWindowsFontDatabase::createEngine(request, faceName, |
1168 | QWindowsFontDatabase::defaultVerticalDPI(), |
1169 | m_fontEngineData); |
1170 | if (fontEngine) { |
1171 | fontEngine->fontDef.family = actualFontName; |
1172 | if (!uniqueFamilyName.isEmpty()) { |
1173 | static_cast<QWindowsFontEngine *>(fontEngine)->setUniqueFamilyName(uniqueFamilyName); |
1174 | if (QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration()) { |
1175 | QPlatformFontDatabase *pfdb = pi->fontDatabase(); |
1176 | static_cast<QWindowsFontDatabase *>(pfdb)->refUniqueFont(uniqueFamilyName); |
1177 | } |
1178 | } |
1179 | } |
1180 | return fontEngine; |
1181 | } |
1182 | |
1183 | Qt::HANDLE QWindowsFontEngine::handle() const |
1184 | { |
1185 | return hfont; |
1186 | } |
1187 | |
1188 | void QWindowsFontEngine::initFontInfo(const QFontDef &request, |
1189 | int dpi) |
1190 | { |
1191 | fontDef = request; // most settings are equal |
1192 | HDC dc = m_fontEngineData->hdc; |
1193 | SelectObject(dc, hfont); |
1194 | wchar_t n[64]; |
1195 | GetTextFace(dc, 64, n); |
1196 | fontDef.family = QString::fromWCharArray(n); |
1197 | fontDef.fixedPitch = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); |
1198 | if (fontDef.pointSize < 0) { |
1199 | fontDef.pointSize = fontDef.pixelSize * 72. / dpi; |
1200 | } else if (fontDef.pixelSize == -1) { |
1201 | fontDef.pixelSize = qRound(fontDef.pointSize * dpi / 72.); |
1202 | } |
1203 | } |
1204 | |
1205 | bool QWindowsFontEngine::supportsTransformation(const QTransform &transform) const |
1206 | { |
1207 | // Support all transformations for ttf files, and translations for raster fonts |
1208 | return ttf || transform.type() <= QTransform::TxTranslate; |
1209 | } |
1210 | |
1211 | QT_END_NAMESPACE |
1212 |
Warning: That file was not part of the compilation database. It may have many parsing errors.