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 "qdir.h"
41#include "qmetatype.h"
42#include "qtextstream.h"
43#include "qvariant.h"
44#include "qfontengine_ft_p.h"
45#include "private/qimage_p.h"
46#include <private/qstringiterator_p.h>
47#include <qguiapplication.h>
48#include <qscreen.h>
49#include <qpa/qplatformscreen.h>
50#include <QtCore/QUuid>
51#include <QtGui/QPainterPath>
52
53#ifndef QT_NO_FREETYPE
54
55#include "qfile.h"
56#include "qfileinfo.h"
57#include <qscopedvaluerollback.h>
58#include "qthreadstorage.h"
59#include <qmath.h>
60#include <qendian.h>
61
62#include <ft2build.h>
63#include FT_FREETYPE_H
64#include FT_OUTLINE_H
65#include FT_SYNTHESIS_H
66#include FT_TRUETYPE_TABLES_H
67#include FT_TYPE1_TABLES_H
68#include FT_GLYPH_H
69#include FT_MODULE_H
70#include FT_LCD_FILTER_H
71
72#if defined(FT_CONFIG_OPTIONS_H)
73#include FT_CONFIG_OPTIONS_H
74#endif
75
76#if defined(FT_FONT_FORMATS_H)
77#include FT_FONT_FORMATS_H
78#endif
79
80#ifdef QT_LINUXBASE
81#include FT_ERRORS_H
82#endif
83
84#if !defined(QT_MAX_CACHED_GLYPH_SIZE)
85# define QT_MAX_CACHED_GLYPH_SIZE 64
86#endif
87
88QT_BEGIN_NAMESPACE
89
90#define FLOOR(x) ((x) & -64)
91#define CEIL(x) (((x)+63) & -64)
92#define TRUNC(x) ((x) >> 6)
93#define ROUND(x) (((x)+32) & -64)
94
95static bool ft_getSfntTable(void *user_data, uint tag, uchar *buffer, uint *length)
96{
97 FT_Face face = (FT_Face)user_data;
98
99 bool result = false;
100 if (FT_IS_SFNT(face)) {
101 FT_ULong len = *length;
102 result = FT_Load_Sfnt_Table(face, tag, offset: 0, buffer, length: &len) == FT_Err_Ok;
103 *length = len;
104 Q_ASSERT(!result || int(*length) > 0);
105 }
106
107 return result;
108}
109
110static QFontEngineFT::Glyph emptyGlyph;
111
112static const QFontEngine::HintStyle ftInitialDefaultHintStyle =
113#ifdef Q_OS_WIN
114 QFontEngineFT::HintFull;
115#else
116 QFontEngineFT::HintNone;
117#endif
118
119// -------------------------- Freetype support ------------------------------
120
121class QtFreetypeData
122{
123public:
124 QtFreetypeData()
125 : library(nullptr)
126 { }
127 ~QtFreetypeData();
128
129 FT_Library library;
130 QHash<QFontEngine::FaceId, QFreetypeFace *> faces;
131};
132
133QtFreetypeData::~QtFreetypeData()
134{
135 for (QHash<QFontEngine::FaceId, QFreetypeFace *>::ConstIterator iter = faces.cbegin(); iter != faces.cend(); ++iter)
136 iter.value()->cleanup();
137 faces.clear();
138 FT_Done_FreeType(library);
139 library = nullptr;
140}
141
142Q_GLOBAL_STATIC(QThreadStorage<QtFreetypeData *>, theFreetypeData)
143
144QtFreetypeData *qt_getFreetypeData()
145{
146 QtFreetypeData *&freetypeData = theFreetypeData()->localData();
147 if (!freetypeData)
148 freetypeData = new QtFreetypeData;
149 if (!freetypeData->library) {
150 FT_Init_FreeType(alibrary: &freetypeData->library);
151#if defined(FT_FONT_FORMATS_H)
152 // Freetype defaults to disabling stem-darkening on CFF, we re-enable it.
153 FT_Bool no_darkening = false;
154 FT_Property_Set(library: freetypeData->library, module_name: "cff", property_name: "no-stem-darkening", value: &no_darkening);
155#endif
156 }
157 return freetypeData;
158}
159
160FT_Library qt_getFreetype()
161{
162 QtFreetypeData *freetypeData = qt_getFreetypeData();
163 Q_ASSERT(freetypeData->library);
164 return freetypeData->library;
165}
166
167int QFreetypeFace::fsType() const
168{
169 int fsType = 0;
170 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
171 if (os2)
172 fsType = os2->fsType;
173 return fsType;
174}
175
176int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
177{
178 if (int error = FT_Load_Glyph(face, glyph_index: glyph, load_flags: flags))
179 return error;
180
181 if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
182 return Err_Invalid_SubTable;
183
184 *nPoints = face->glyph->outline.n_points;
185 if (!(*nPoints))
186 return Err_Ok;
187
188 if (point > *nPoints)
189 return Err_Invalid_SubTable;
190
191 *xpos = QFixed::fromFixed(fixed: face->glyph->outline.points[point].x);
192 *ypos = QFixed::fromFixed(fixed: face->glyph->outline.points[point].y);
193
194 return Err_Ok;
195}
196
197bool QFreetypeFace::isScalableBitmap() const
198{
199#ifdef FT_HAS_COLOR
200 return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face);
201#else
202 return false;
203#endif
204}
205
206extern QByteArray qt_fontdata_from_index(int);
207
208/*
209 * One font file can contain more than one font (bold/italic for example)
210 * find the right one and return it.
211 *
212 * Returns the freetype face or 0 in case of an empty file or any other problems
213 * (like not being able to open the file)
214 */
215QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
216 const QByteArray &fontData)
217{
218 if (face_id.filename.isEmpty() && fontData.isEmpty())
219 return nullptr;
220
221 QtFreetypeData *freetypeData = qt_getFreetypeData();
222
223 QFreetypeFace *freetype = freetypeData->faces.value(akey: face_id, adefaultValue: 0);
224 if (freetype) {
225 freetype->ref.ref();
226 } else {
227 QScopedPointer<QFreetypeFace> newFreetype(new QFreetypeFace);
228 FT_Face face;
229 if (!face_id.filename.isEmpty()) {
230 QString fileName = QFile::decodeName(localFileName: face_id.filename);
231 if (face_id.filename.startsWith(c: ":qmemoryfonts/")) {
232 // from qfontdatabase.cpp
233 QByteArray idx = face_id.filename;
234 idx.remove(index: 0, len: 14); // remove ':qmemoryfonts/'
235 bool ok = false;
236 newFreetype->fontData = qt_fontdata_from_index(idx.toInt(ok: &ok));
237 if (!ok)
238 newFreetype->fontData = QByteArray();
239 } else if (!QFileInfo(fileName).isNativePath()) {
240 QFile file(fileName);
241 if (!file.open(flags: QIODevice::ReadOnly)) {
242 return nullptr;
243 }
244 newFreetype->fontData = file.readAll();
245 }
246 } else {
247 newFreetype->fontData = fontData;
248 }
249 if (!newFreetype->fontData.isEmpty()) {
250 if (FT_New_Memory_Face(library: freetypeData->library, file_base: (const FT_Byte *)newFreetype->fontData.constData(), file_size: newFreetype->fontData.size(), face_index: face_id.index, aface: &face)) {
251 return nullptr;
252 }
253 } else if (FT_New_Face(library: freetypeData->library, filepathname: face_id.filename, face_index: face_id.index, aface: &face)) {
254 return nullptr;
255 }
256 newFreetype->face = face;
257
258 newFreetype->ref.storeRelaxed(newValue: 1);
259 newFreetype->xsize = 0;
260 newFreetype->ysize = 0;
261 newFreetype->matrix.xx = 0x10000;
262 newFreetype->matrix.yy = 0x10000;
263 newFreetype->matrix.xy = 0;
264 newFreetype->matrix.yx = 0;
265 newFreetype->unicode_map = nullptr;
266 newFreetype->symbol_map = nullptr;
267
268 memset(s: newFreetype->cmapCache, c: 0, n: sizeof(newFreetype->cmapCache));
269
270 for (int i = 0; i < newFreetype->face->num_charmaps; ++i) {
271 FT_CharMap cm = newFreetype->face->charmaps[i];
272 switch(cm->encoding) {
273 case FT_ENCODING_UNICODE:
274 newFreetype->unicode_map = cm;
275 break;
276 case FT_ENCODING_APPLE_ROMAN:
277 case FT_ENCODING_ADOBE_LATIN_1:
278 if (!newFreetype->unicode_map || newFreetype->unicode_map->encoding != FT_ENCODING_UNICODE)
279 newFreetype->unicode_map = cm;
280 break;
281 case FT_ENCODING_ADOBE_CUSTOM:
282 case FT_ENCODING_MS_SYMBOL:
283 if (!newFreetype->symbol_map)
284 newFreetype->symbol_map = cm;
285 break;
286 default:
287 break;
288 }
289 }
290
291 if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1)
292 FT_Set_Char_Size(face, char_width: newFreetype->face->available_sizes[0].x_ppem, char_height: newFreetype->face->available_sizes[0].y_ppem, horz_resolution: 0, vert_resolution: 0);
293
294 FT_Set_Charmap(face: newFreetype->face, charmap: newFreetype->unicode_map);
295 QT_TRY {
296 freetypeData->faces.insert(akey: face_id, avalue: newFreetype.data());
297 } QT_CATCH(...) {
298 newFreetype.take()->release(face_id);
299 // we could return null in principle instead of throwing
300 QT_RETHROW;
301 }
302 freetype = newFreetype.take();
303 }
304 return freetype;
305}
306
307void QFreetypeFace::cleanup()
308{
309 hbFace.reset();
310 FT_Done_Face(face);
311 face = nullptr;
312}
313
314void QFreetypeFace::release(const QFontEngine::FaceId &face_id)
315{
316 if (!ref.deref()) {
317 if (face) {
318 QtFreetypeData *freetypeData = qt_getFreetypeData();
319
320 cleanup();
321
322 auto it = freetypeData->faces.constFind(akey: face_id);
323 if (it != freetypeData->faces.constEnd())
324 freetypeData->faces.erase(it);
325
326 if (freetypeData->faces.isEmpty()) {
327 FT_Done_FreeType(library: freetypeData->library);
328 freetypeData->library = nullptr;
329 }
330 }
331
332 delete this;
333 }
334}
335
336
337void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor)
338{
339 *ysize = qRound(d: fontDef.pixelSize * 64);
340 *xsize = *ysize * fontDef.stretch / 100;
341 *scalableBitmapScaleFactor = 1;
342 *outline_drawing = false;
343
344 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) {
345 int best = 0;
346 if (!isScalableBitmap()) {
347 /*
348 * Bitmap only faces must match exactly, so find the closest
349 * one (height dominant search)
350 */
351 for (int i = 1; i < face->num_fixed_sizes; i++) {
352 if (qAbs(t: *ysize - face->available_sizes[i].y_ppem) <
353 qAbs(t: *ysize - face->available_sizes[best].y_ppem) ||
354 (qAbs(t: *ysize - face->available_sizes[i].y_ppem) ==
355 qAbs(t: *ysize - face->available_sizes[best].y_ppem) &&
356 qAbs(t: *xsize - face->available_sizes[i].x_ppem) <
357 qAbs(t: *xsize - face->available_sizes[best].x_ppem))) {
358 best = i;
359 }
360 }
361 } else {
362 // Select the shortest bitmap strike whose height is larger than the desired height
363 for (int i = 1; i < face->num_fixed_sizes; i++) {
364 if (face->available_sizes[i].y_ppem < *ysize) {
365 if (face->available_sizes[i].y_ppem > face->available_sizes[best].y_ppem)
366 best = i;
367 } else if (face->available_sizes[best].y_ppem < *ysize) {
368 best = i;
369 } else if (face->available_sizes[i].y_ppem < face->available_sizes[best].y_ppem) {
370 best = i;
371 }
372 }
373 }
374
375 // According to freetype documentation we must use FT_Select_Size
376 // to make sure we can select the desired bitmap strike index
377 if (FT_Select_Size(face, strike_index: best) == 0) {
378 if (isScalableBitmap())
379 *scalableBitmapScaleFactor = QFixed::fromReal(r: (qreal)fontDef.pixelSize / face->available_sizes[best].height);
380 *xsize = face->available_sizes[best].x_ppem;
381 *ysize = face->available_sizes[best].y_ppem;
382 } else {
383 *xsize = *ysize = 0;
384 }
385 } else {
386 *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6));
387 }
388}
389
390QFontEngine::Properties QFreetypeFace::properties() const
391{
392 QFontEngine::Properties p;
393 p.postscriptName = FT_Get_Postscript_Name(face);
394 PS_FontInfoRec font_info;
395 if (FT_Get_PS_Font_Info(face, afont_info: &font_info) == 0)
396 p.copyright = font_info.notice;
397 if (FT_IS_SCALABLE(face)) {
398 p.ascent = face->ascender;
399 p.descent = -face->descender;
400 p.leading = face->height - face->ascender + face->descender;
401 p.emSquare = face->units_per_EM;
402 p.boundingBox = QRectF(face->bbox.xMin, -face->bbox.yMax,
403 face->bbox.xMax - face->bbox.xMin,
404 face->bbox.yMax - face->bbox.yMin);
405 } else {
406 p.ascent = QFixed::fromFixed(fixed: face->size->metrics.ascender);
407 p.descent = QFixed::fromFixed(fixed: -face->size->metrics.descender);
408 p.leading = QFixed::fromFixed(fixed: face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender);
409 p.emSquare = face->size->metrics.y_ppem;
410// p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.);
411 p.boundingBox = QRectF(0, -p.ascent.toReal(),
412 face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() );
413 }
414 p.italicAngle = 0;
415 p.capHeight = p.ascent;
416 p.lineWidth = face->underline_thickness;
417
418 return p;
419}
420
421bool QFreetypeFace::getSfntTable(uint tag, uchar *buffer, uint *length) const
422{
423 return ft_getSfntTable(user_data: face, tag, buffer, length);
424}
425
426/* Some fonts (such as MingLiu rely on hinting to scale different
427 components to their correct sizes. While this is really broken (it
428 should be done in the component glyph itself, not the hinter) we
429 will have to live with it.
430
431 This means we can not use FT_LOAD_NO_HINTING to get the glyph
432 outline. All we can do is to load the unscaled glyph and scale it
433 down manually when required.
434*/
435static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale)
436{
437 x_scale = FT_MulDiv(a: x_scale, b: 1 << 10, c: face->units_per_EM);
438 y_scale = FT_MulDiv(a: y_scale, b: 1 << 10, c: face->units_per_EM);
439 FT_Vector *p = g->outline.points;
440 const FT_Vector *e = p + g->outline.n_points;
441 while (p < e) {
442 p->x = FT_MulFix(a: p->x, b: x_scale);
443 p->y = FT_MulFix(a: p->y, b: y_scale);
444 ++p;
445 }
446}
447
448#define GLYPH2PATH_DEBUG QT_NO_QDEBUG_MACRO // qDebug
449void QFreetypeFace::addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale)
450{
451 const qreal factor = 1/64.;
452 scaleOutline(face, g, x_scale, y_scale);
453
454 QPointF cp = point.toPointF();
455
456 // convert the outline to a painter path
457 int i = 0;
458 for (int j = 0; j < g->outline.n_contours; ++j) {
459 int last_point = g->outline.contours[j];
460 GLYPH2PATH_DEBUG() << "contour:" << i << "to" << last_point;
461 QPointF start = QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
462 if (!(g->outline.tags[i] & 1)) { // start point is not on curve:
463 if (!(g->outline.tags[last_point] & 1)) { // end point is not on curve:
464 GLYPH2PATH_DEBUG() << " start and end point are not on curve";
465 start = (QPointF(g->outline.points[last_point].x*factor,
466 -g->outline.points[last_point].y*factor) + start) / 2.0;
467 } else {
468 GLYPH2PATH_DEBUG() << " end point is on curve, start is not";
469 start = QPointF(g->outline.points[last_point].x*factor,
470 -g->outline.points[last_point].y*factor);
471 }
472 --i; // to use original start point as control point below
473 }
474 start += cp;
475 GLYPH2PATH_DEBUG() << " start at" << start;
476
477 path->moveTo(p: start);
478 QPointF c[4];
479 c[0] = start;
480 int n = 1;
481 while (i < last_point) {
482 ++i;
483 c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
484 GLYPH2PATH_DEBUG() << " " << i << c[n] << "tag =" << (int)g->outline.tags[i]
485 << ": on curve =" << (bool)(g->outline.tags[i] & 1);
486 ++n;
487 switch (g->outline.tags[i] & 3) {
488 case 2:
489 // cubic bezier element
490 if (n < 4)
491 continue;
492 c[3] = (c[3] + c[2])/2;
493 --i;
494 break;
495 case 0:
496 // quadratic bezier element
497 if (n < 3)
498 continue;
499 c[3] = (c[1] + c[2])/2;
500 c[2] = (2*c[1] + c[3])/3;
501 c[1] = (2*c[1] + c[0])/3;
502 --i;
503 break;
504 case 1:
505 case 3:
506 if (n == 2) {
507 GLYPH2PATH_DEBUG() << " lineTo" << c[1];
508 path->lineTo(p: c[1]);
509 c[0] = c[1];
510 n = 1;
511 continue;
512 } else if (n == 3) {
513 c[3] = c[2];
514 c[2] = (2*c[1] + c[3])/3;
515 c[1] = (2*c[1] + c[0])/3;
516 }
517 break;
518 }
519 GLYPH2PATH_DEBUG() << " cubicTo" << c[1] << c[2] << c[3];
520 path->cubicTo(ctrlPt1: c[1], ctrlPt2: c[2], endPt: c[3]);
521 c[0] = c[3];
522 n = 1;
523 }
524
525 if (n == 1) {
526 GLYPH2PATH_DEBUG() << " closeSubpath";
527 path->closeSubpath();
528 } else {
529 c[3] = start;
530 if (n == 2) {
531 c[2] = (2*c[1] + c[3])/3;
532 c[1] = (2*c[1] + c[0])/3;
533 }
534 GLYPH2PATH_DEBUG() << " close cubicTo" << c[1] << c[2] << c[3];
535 path->cubicTo(ctrlPt1: c[1], ctrlPt2: c[2], endPt: c[3]);
536 }
537 ++i;
538 }
539}
540
541extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path);
542
543void QFreetypeFace::addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path)
544{
545 if (slot->format != FT_GLYPH_FORMAT_BITMAP
546 || slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO)
547 return;
548
549 QPointF cp = point.toPointF();
550 qt_addBitmapToPath(x0: cp.x() + TRUNC(slot->metrics.horiBearingX), y0: cp.y() - TRUNC(slot->metrics.horiBearingY),
551 image_data: slot->bitmap.buffer, bpl: slot->bitmap.pitch, w: slot->bitmap.width, h: slot->bitmap.rows, path);
552}
553
554static inline void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
555{
556 const int offs = bgr ? -1 : 1;
557 const int w = width * 3;
558 while (height--) {
559 uint *dd = dst;
560 for (int x = 0; x < w; x += 3) {
561 uchar red = src[x + 1 - offs];
562 uchar green = src[x + 1];
563 uchar blue = src[x + 1 + offs];
564 *dd++ = (0xFFU << 24) | (red << 16) | (green << 8) | blue;
565 }
566 dst += width;
567 src += src_pitch;
568 }
569}
570
571static inline void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
572{
573 const int offs = bgr ? -src_pitch : src_pitch;
574 while (height--) {
575 for (int x = 0; x < width; x++) {
576 uchar red = src[x + src_pitch - offs];
577 uchar green = src[x + src_pitch];
578 uchar blue = src[x + src_pitch + offs];
579 *dst++ = (0XFFU << 24) | (red << 16) | (green << 8) | blue;
580 }
581 src += 3*src_pitch;
582 }
583}
584
585static QFontEngine::SubpixelAntialiasingType subpixelAntialiasingTypeHint()
586{
587 static int type = -1;
588 if (type == -1) {
589 if (QScreen *screen = QGuiApplication::primaryScreen())
590 type = screen->handle()->subpixelAntialiasingTypeHint();
591 }
592 return static_cast<QFontEngine::SubpixelAntialiasingType>(type);
593}
594
595QFontEngineFT *QFontEngineFT::create(const QFontDef &fontDef, FaceId faceId, const QByteArray &fontData)
596{
597 QScopedPointer<QFontEngineFT> engine(new QFontEngineFT(fontDef));
598
599 QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_Mono;
600 const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
601
602 if (antialias) {
603 QFontEngine::SubpixelAntialiasingType subpixelType = subpixelAntialiasingTypeHint();
604 if (subpixelType == QFontEngine::Subpixel_None || (fontDef.styleStrategy & QFont::NoSubpixelAntialias)) {
605 format = QFontEngineFT::Format_A8;
606 engine->subpixelType = QFontEngine::Subpixel_None;
607 } else {
608 format = QFontEngineFT::Format_A32;
609 engine->subpixelType = subpixelType;
610 }
611 }
612
613 if (!engine->init(faceId, antiaalias: antialias, defaultFormat: format, fontData) || engine->invalid()) {
614 qWarning(msg: "QFontEngineFT: Failed to create FreeType font engine");
615 return nullptr;
616 }
617
618 engine->setQtDefaultHintStyle(static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
619 return engine.take();
620}
621
622namespace {
623 class QFontEngineFTRawData: public QFontEngineFT
624 {
625 public:
626 QFontEngineFTRawData(const QFontDef &fontDef) : QFontEngineFT(fontDef)
627 {
628 }
629
630 void updateFamilyNameAndStyle()
631 {
632 fontDef.family = QString::fromLatin1(str: freetype->face->family_name);
633
634 if (freetype->face->style_flags & FT_STYLE_FLAG_ITALIC)
635 fontDef.style = QFont::StyleItalic;
636
637 if (freetype->face->style_flags & FT_STYLE_FLAG_BOLD)
638 fontDef.weight = QFont::Bold;
639 }
640
641 bool initFromData(const QByteArray &fontData)
642 {
643 FaceId faceId;
644 faceId.filename = "";
645 faceId.index = 0;
646 faceId.uuid = QUuid::createUuid().toByteArray();
647
648 return init(faceId, antiaalias: true, defaultFormat: Format_None, fontData);
649 }
650 };
651}
652
653QFontEngineFT *QFontEngineFT::create(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
654{
655 QFontDef fontDef;
656 fontDef.pixelSize = pixelSize;
657 fontDef.stretch = QFont::Unstretched;
658 fontDef.hintingPreference = hintingPreference;
659
660 QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef);
661 if (!fe->initFromData(fontData)) {
662 delete fe;
663 return nullptr;
664 }
665
666 fe->updateFamilyNameAndStyle();
667 fe->setQtDefaultHintStyle(static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
668
669 return fe;
670}
671
672QFontEngineFT::QFontEngineFT(const QFontDef &fd)
673 : QFontEngine(Freetype)
674{
675 fontDef = fd;
676 matrix.xx = 0x10000;
677 matrix.yy = 0x10000;
678 matrix.xy = 0;
679 matrix.yx = 0;
680 cache_cost = 100 * 1024;
681 kerning_pairs_loaded = false;
682 transform = false;
683 embolden = false;
684 obliquen = false;
685 antialias = true;
686 freetype = nullptr;
687 default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
688 default_hint_style = ftInitialDefaultHintStyle;
689 subpixelType = Subpixel_None;
690 lcdFilterType = (int)((quintptr) FT_LCD_FILTER_DEFAULT);
691 defaultFormat = Format_None;
692 embeddedbitmap = false;
693 const QByteArray env = qgetenv(varName: "QT_NO_FT_CACHE");
694 cacheEnabled = env.isEmpty() || env.toInt() == 0;
695 m_subPixelPositionCount = 4;
696 forceAutoHint = false;
697 stemDarkeningDriver = false;
698}
699
700QFontEngineFT::~QFontEngineFT()
701{
702 if (freetype)
703 freetype->release(face_id);
704}
705
706bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
707 const QByteArray &fontData)
708{
709 return init(faceId, antialias, format, freetypeFace: QFreetypeFace::getFace(face_id: faceId, fontData));
710}
711
712static void dont_delete(void*) {}
713
714bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
715 QFreetypeFace *freetypeFace)
716{
717 freetype = freetypeFace;
718 if (!freetype) {
719 xsize = 0;
720 ysize = 0;
721 return false;
722 }
723 defaultFormat = format;
724 this->antialias = antialias;
725
726 if (!antialias)
727 glyphFormat = QFontEngine::Format_Mono;
728 else
729 glyphFormat = defaultFormat;
730
731 face_id = faceId;
732
733 symbol = freetype->symbol_map != nullptr;
734 PS_FontInfoRec psrec;
735 // don't assume that type1 fonts are symbol fonts by default
736 if (FT_Get_PS_Font_Info(face: freetype->face, afont_info: &psrec) == FT_Err_Ok) {
737 symbol = bool(fontDef.family.contains(s: QLatin1String("symbol"), cs: Qt::CaseInsensitive));
738 }
739
740 freetype->computeSize(fontDef, xsize: &xsize, ysize: &ysize, outline_drawing: &defaultGlyphSet.outline_drawing, scalableBitmapScaleFactor: &scalableBitmapScaleFactor);
741
742 FT_Face face = lockFace();
743
744 if (FT_IS_SCALABLE(face)) {
745 bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !(face->style_flags & FT_STYLE_FLAG_ITALIC) && !qEnvironmentVariableIsSet(varName: "QT_NO_SYNTHESIZED_ITALIC");
746 if (fake_oblique)
747 obliquen = true;
748 FT_Set_Transform(face, matrix: &matrix, delta: nullptr);
749 freetype->matrix = matrix;
750 // fake bold
751 if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face) && !qEnvironmentVariableIsSet(varName: "QT_NO_SYNTHESIZED_BOLD")) {
752 if (const TT_OS2 *os2 = reinterpret_cast<const TT_OS2 *>(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) {
753 if (os2->usWeightClass < 700 &&
754 (fontDef.pixelSize < 64 || qEnvironmentVariableIsSet(varName: "QT_NO_SYNTHESIZED_BOLD_LIMIT"))) {
755 embolden = true;
756 }
757 }
758 }
759 // underline metrics
760 line_thickness = QFixed::fromFixed(fixed: FT_MulFix(a: face->underline_thickness, b: face->size->metrics.y_scale));
761 QFixed center_position = QFixed::fromFixed(fixed: -FT_MulFix(a: face->underline_position, b: face->size->metrics.y_scale));
762 underline_position = center_position - line_thickness / 2;
763 } else {
764 // ad hoc algorithm
765 int score = fontDef.weight * fontDef.pixelSize;
766 line_thickness = score / 700;
767 // looks better with thicker line for small pointsizes
768 if (line_thickness < 2 && score >= 1050)
769 line_thickness = 2;
770 underline_position = ((line_thickness * 2) + 3) / 6;
771
772 if (isScalableBitmap()) {
773 glyphFormat = defaultFormat = GlyphFormat::Format_ARGB;
774 cacheEnabled = false;
775 }
776 }
777 if (line_thickness < 1)
778 line_thickness = 1;
779
780 metrics = face->size->metrics;
781
782 /*
783 TrueType fonts with embedded bitmaps may have a bitmap font specific
784 ascent/descent in the EBLC table. There is no direct public API
785 to extract those values. The only way we've found is to trick freetype
786 into thinking that it's not a scalable font in FT_SelectSize so that
787 the metrics are retrieved from the bitmap strikes.
788 */
789 if (FT_IS_SCALABLE(face)) {
790 for (int i = 0; i < face->num_fixed_sizes; ++i) {
791 if (xsize == face->available_sizes[i].x_ppem && ysize == face->available_sizes[i].y_ppem) {
792 face->face_flags &= ~FT_FACE_FLAG_SCALABLE;
793
794 FT_Select_Size(face, strike_index: i);
795 if (face->size->metrics.ascender + face->size->metrics.descender > 0) {
796 FT_Pos leading = metrics.height - metrics.ascender + metrics.descender;
797 metrics.ascender = face->size->metrics.ascender;
798 metrics.descender = face->size->metrics.descender;
799 if (metrics.descender > 0
800 && QString::fromUtf8(str: face->family_name) == QLatin1String("Courier New")) {
801 metrics.descender *= -1;
802 }
803 metrics.height = metrics.ascender - metrics.descender + leading;
804 }
805 FT_Set_Char_Size(face, char_width: xsize, char_height: ysize, horz_resolution: 0, vert_resolution: 0);
806
807 face->face_flags |= FT_FACE_FLAG_SCALABLE;
808 break;
809 }
810 }
811 }
812#if defined(FT_FONT_FORMATS_H)
813 const char *fmt = FT_Get_Font_Format(face);
814 if (fmt && qstrncmp(str1: fmt, str2: "CFF", len: 4) == 0) {
815 FT_Bool no_stem_darkening = true;
816 FT_Error err = FT_Property_Get(library: qt_getFreetype(), module_name: "cff", property_name: "no-stem-darkening", value: &no_stem_darkening);
817 if (err == FT_Err_Ok)
818 stemDarkeningDriver = !no_stem_darkening;
819 else
820 stemDarkeningDriver = false;
821 }
822#endif
823
824 fontDef.styleName = QString::fromUtf8(str: face->style_name);
825
826 if (!freetype->hbFace) {
827 faceData.user_data = face;
828 faceData.get_font_table = ft_getSfntTable;
829 (void)harfbuzzFace(); // populates face_
830 freetype->hbFace = std::move(face_);
831 } else {
832 Q_ASSERT(!face_);
833 }
834 // we share the HB face in QFreeTypeFace, so do not let ~QFontEngine() destroy it
835 face_ = Holder(freetype->hbFace.get(), dont_delete);
836
837 unlockFace();
838
839 fsType = freetype->fsType();
840 return true;
841}
842
843void QFontEngineFT::setQtDefaultHintStyle(QFont::HintingPreference hintingPreference)
844{
845 switch (hintingPreference) {
846 case QFont::PreferNoHinting:
847 setDefaultHintStyle(HintNone);
848 break;
849 case QFont::PreferFullHinting:
850 setDefaultHintStyle(HintFull);
851 break;
852 case QFont::PreferVerticalHinting:
853 setDefaultHintStyle(HintLight);
854 break;
855 case QFont::PreferDefaultHinting:
856 setDefaultHintStyle(ftInitialDefaultHintStyle);
857 break;
858 }
859}
860
861void QFontEngineFT::setDefaultHintStyle(HintStyle style)
862{
863 default_hint_style = style;
864}
865
866bool QFontEngineFT::expectsGammaCorrectedBlending() const
867{
868 return stemDarkeningDriver;
869}
870
871int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags,
872 bool &hsubpixel, int &vfactor) const
873{
874 int load_flags = FT_LOAD_DEFAULT | default_load_flags;
875 int load_target = default_hint_style == HintLight
876 ? FT_LOAD_TARGET_LIGHT
877 : FT_LOAD_TARGET_NORMAL;
878
879 if (format == Format_Mono) {
880 load_target = FT_LOAD_TARGET_MONO;
881 } else if (format == Format_A32) {
882 if (subpixelType == Subpixel_RGB || subpixelType == Subpixel_BGR)
883 hsubpixel = true;
884 else if (subpixelType == Subpixel_VRGB || subpixelType == Subpixel_VBGR)
885 vfactor = 3;
886 } else if (format == Format_ARGB) {
887#ifdef FT_LOAD_COLOR
888 load_flags |= FT_LOAD_COLOR;
889#endif
890 }
891
892 if (set && set->outline_drawing)
893 load_flags |= FT_LOAD_NO_BITMAP;
894
895 if (default_hint_style == HintNone || (flags & DesignMetrics) || (set && set->outline_drawing))
896 load_flags |= FT_LOAD_NO_HINTING;
897 else
898 load_flags |= load_target;
899
900 if (forceAutoHint)
901 load_flags |= FT_LOAD_FORCE_AUTOHINT;
902
903 return load_flags;
904}
905
906static inline bool areMetricsTooLarge(const QFontEngineFT::GlyphInfo &info)
907{
908 // false if exceeds QFontEngineFT::Glyph metrics
909 return info.width > 0xFF || info.height > 0xFF || info.linearAdvance > 0x7FFF;
910}
911
912static inline void transformBoundingBox(int *left, int *top, int *right, int *bottom, FT_Matrix *matrix)
913{
914 int l, r, t, b;
915 FT_Vector vector;
916 vector.x = *left;
917 vector.y = *top;
918 FT_Vector_Transform(vector: &vector, matrix);
919 l = r = vector.x;
920 t = b = vector.y;
921 vector.x = *right;
922 vector.y = *top;
923 FT_Vector_Transform(vector: &vector, matrix);
924 if (l > vector.x) l = vector.x;
925 if (r < vector.x) r = vector.x;
926 if (t < vector.y) t = vector.y;
927 if (b > vector.y) b = vector.y;
928 vector.x = *right;
929 vector.y = *bottom;
930 FT_Vector_Transform(vector: &vector, matrix);
931 if (l > vector.x) l = vector.x;
932 if (r < vector.x) r = vector.x;
933 if (t < vector.y) t = vector.y;
934 if (b > vector.y) b = vector.y;
935 vector.x = *left;
936 vector.y = *bottom;
937 FT_Vector_Transform(vector: &vector, matrix);
938 if (l > vector.x) l = vector.x;
939 if (r < vector.x) r = vector.x;
940 if (t < vector.y) t = vector.y;
941 if (b > vector.y) b = vector.y;
942 *left = l;
943 *right = r;
944 *top = t;
945 *bottom = b;
946}
947
948QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
949 QFixed subPixelPosition,
950 GlyphFormat format,
951 bool fetchMetricsOnly,
952 bool disableOutlineDrawing) const
953{
954// Q_ASSERT(freetype->lock == 1);
955
956 if (format == Format_None)
957 format = defaultFormat != Format_None ? defaultFormat : Format_Mono;
958 Q_ASSERT(format != Format_None);
959
960 Glyph *g = set ? set->getGlyph(index: glyph, subPixelPosition) : nullptr;
961 if (g && g->format == format && (fetchMetricsOnly || g->data))
962 return g;
963
964 if (!g && set && set->isGlyphMissing(index: glyph))
965 return &emptyGlyph;
966
967
968 FT_Face face = freetype->face;
969
970 FT_Matrix matrix = freetype->matrix;
971
972 FT_Vector v;
973 v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.value());
974 v.y = 0;
975 FT_Set_Transform(face, matrix: &matrix, delta: &v);
976
977 bool hsubpixel = false;
978 int vfactor = 1;
979 int load_flags = loadFlags(set, format, flags: 0, hsubpixel, vfactor);
980
981 bool transform = matrix.xx != 0x10000
982 || matrix.yy != 0x10000
983 || matrix.xy != 0
984 || matrix.yx != 0;
985
986 if (transform || obliquen || (format != Format_Mono && !isScalableBitmap()))
987 load_flags |= FT_LOAD_NO_BITMAP;
988
989 FT_Error err = FT_Load_Glyph(face, glyph_index: glyph, load_flags);
990 if (err && (load_flags & FT_LOAD_NO_BITMAP)) {
991 load_flags &= ~FT_LOAD_NO_BITMAP;
992 err = FT_Load_Glyph(face, glyph_index: glyph, load_flags);
993 }
994 if (err == FT_Err_Too_Few_Arguments) {
995 // this is an error in the bytecode interpreter, just try to run without it
996 load_flags |= FT_LOAD_FORCE_AUTOHINT;
997 err = FT_Load_Glyph(face, glyph_index: glyph, load_flags);
998 } else if (err == FT_Err_Execution_Too_Long) {
999 // This is an error in the bytecode, probably a web font made by someone who
1000 // didn't test bytecode hinting at all so disable for it for all glyphs.
1001 qWarning(msg: "load glyph failed due to broken hinting bytecode in font, switching to auto hinting");
1002 default_load_flags |= FT_LOAD_FORCE_AUTOHINT;
1003 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1004 err = FT_Load_Glyph(face, glyph_index: glyph, load_flags);
1005 }
1006 if (err != FT_Err_Ok) {
1007 qWarning(msg: "load glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
1008 if (set)
1009 set->setGlyphMissing(glyph);
1010 return &emptyGlyph;
1011 }
1012
1013 FT_GlyphSlot slot = face->glyph;
1014
1015 if (embolden)
1016 FT_GlyphSlot_Embolden(slot);
1017 if (obliquen) {
1018 FT_GlyphSlot_Oblique(slot);
1019
1020 // While Embolden alters the metrics of the slot, oblique does not, so we need
1021 // to fix this ourselves.
1022 transform = true;
1023 FT_Matrix m;
1024 m.xx = 0x10000;
1025 m.yx = 0x0;
1026 m.xy = 0x6000;
1027 m.yy = 0x10000;
1028
1029 FT_Matrix_Multiply(a: &m, b: &matrix);
1030 }
1031
1032 GlyphInfo info;
1033 info.linearAdvance = slot->linearHoriAdvance >> 10;
1034 info.xOff = TRUNC(ROUND(slot->advance.x));
1035 info.yOff = 0;
1036
1037 if ((set && set->outline_drawing && !disableOutlineDrawing) || fetchMetricsOnly) {
1038 int left = slot->metrics.horiBearingX;
1039 int right = slot->metrics.horiBearingX + slot->metrics.width;
1040 int top = slot->metrics.horiBearingY;
1041 int bottom = slot->metrics.horiBearingY - slot->metrics.height;
1042
1043 if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP)
1044 transformBoundingBox(left: &left, top: &top, right: &right, bottom: &bottom, matrix: &matrix);
1045
1046 left = FLOOR(left);
1047 right = CEIL(right);
1048 bottom = FLOOR(bottom);
1049 top = CEIL(top);
1050
1051 info.x = TRUNC(left);
1052 info.y = TRUNC(top);
1053 info.width = TRUNC(right - left);
1054 info.height = TRUNC(top - bottom);
1055
1056 // If any of the metrics are too large to fit, don't cache them
1057 // Also, avoid integer overflow when linearAdvance is to large to fit in a signed short
1058 if (areMetricsTooLarge(info))
1059 return nullptr;
1060
1061 g = new Glyph;
1062 g->data = nullptr;
1063 g->linearAdvance = info.linearAdvance;
1064 g->width = info.width;
1065 g->height = info.height;
1066 g->x = info.x;
1067 g->y = info.y;
1068 g->advance = info.xOff;
1069 g->format = format;
1070
1071 if (set)
1072 set->setGlyph(index: glyph, spp: subPixelPosition, glyph: g);
1073
1074 return g;
1075 }
1076
1077 int glyph_buffer_size = 0;
1078 QScopedArrayPointer<uchar> glyph_buffer;
1079 FT_Render_Mode renderMode = (default_hint_style == HintLight) ? FT_RENDER_MODE_LIGHT : FT_RENDER_MODE_NORMAL;
1080 switch (format) {
1081 case Format_Mono:
1082 renderMode = FT_RENDER_MODE_MONO;
1083 break;
1084 case Format_A32:
1085 if (!hsubpixel && vfactor == 1) {
1086 qWarning(msg: "Format_A32 requested, but subpixel layout is unknown.");
1087 return nullptr;
1088 }
1089
1090 renderMode = hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V;
1091 break;
1092 case Format_A8:
1093 case Format_ARGB:
1094 break;
1095 default:
1096 Q_UNREACHABLE();
1097 }
1098 FT_Library_SetLcdFilter(library: slot->library, filter: (FT_LcdFilter)lcdFilterType);
1099
1100 err = FT_Render_Glyph(slot, render_mode: renderMode);
1101 if (err != FT_Err_Ok)
1102 qWarning(msg: "render glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
1103
1104 FT_Library_SetLcdFilter(library: slot->library, filter: FT_LCD_FILTER_NONE);
1105
1106 info.height = slot->bitmap.rows;
1107 info.width = slot->bitmap.width;
1108 info.x = slot->bitmap_left;
1109 info.y = slot->bitmap_top;
1110 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD)
1111 info.width = info.width / 3;
1112 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V)
1113 info.height = info.height / vfactor;
1114
1115 int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 :
1116 (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4));
1117
1118 glyph_buffer_size = info.height * pitch;
1119 glyph_buffer.reset(other: new uchar[glyph_buffer_size]);
1120
1121 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
1122 uchar *src = slot->bitmap.buffer;
1123 uchar *dst = glyph_buffer.data();
1124 int h = slot->bitmap.rows;
1125 // Some fonts return bitmaps even when we requested something else:
1126 if (format == Format_Mono) {
1127 int bytes = ((info.width + 7) & ~7) >> 3;
1128 while (h--) {
1129 memcpy (dest: dst, src: src, n: bytes);
1130 dst += pitch;
1131 src += slot->bitmap.pitch;
1132 }
1133 } else if (format == Format_A8) {
1134 while (h--) {
1135 for (int x = 0; x < int{info.width}; x++)
1136 dst[x] = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00);
1137 dst += pitch;
1138 src += slot->bitmap.pitch;
1139 }
1140 } else {
1141 while (h--) {
1142 uint *dd = reinterpret_cast<uint *>(dst);
1143 for (int x = 0; x < int{info.width}; x++)
1144 dd[x] = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffffff : 0x00000000);
1145 dst += pitch;
1146 src += slot->bitmap.pitch;
1147 }
1148 }
1149 } else if (slot->bitmap.pixel_mode == 7 /*FT_PIXEL_MODE_BGRA*/) {
1150 Q_ASSERT(format == Format_ARGB);
1151 uchar *src = slot->bitmap.buffer;
1152 uchar *dst = glyph_buffer.data();
1153 int h = slot->bitmap.rows;
1154 while (h--) {
1155#if Q_BYTE_ORDER == Q_BIG_ENDIAN
1156 const quint32 *srcPixel = (const quint32 *)src;
1157 quint32 *dstPixel = (quint32 *)dst;
1158 for (int x = 0; x < static_cast<int>(slot->bitmap.width); x++, srcPixel++, dstPixel++) {
1159 const quint32 pixel = *srcPixel;
1160 *dstPixel = qbswap(pixel);
1161 }
1162#else
1163 memcpy(dest: dst, src: src, n: slot->bitmap.width * 4);
1164#endif
1165 dst += slot->bitmap.pitch;
1166 src += slot->bitmap.pitch;
1167 }
1168 info.linearAdvance = info.xOff = slot->bitmap.width;
1169 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
1170 Q_ASSERT(format == Format_A8);
1171 uchar *src = slot->bitmap.buffer;
1172 uchar *dst = glyph_buffer.data();
1173 int h = slot->bitmap.rows;
1174 int bytes = info.width;
1175 while (h--) {
1176 memcpy (dest: dst, src: src, n: bytes);
1177 dst += pitch;
1178 src += slot->bitmap.pitch;
1179 }
1180 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
1181 Q_ASSERT(format == Format_A32);
1182 convertRGBToARGB(src: slot->bitmap.buffer, dst: (uint *)glyph_buffer.data(), width: info.width, height: info.height, src_pitch: slot->bitmap.pitch, bgr: subpixelType != Subpixel_RGB);
1183 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) {
1184 Q_ASSERT(format == Format_A32);
1185 convertRGBToARGB_V(src: slot->bitmap.buffer, dst: (uint *)glyph_buffer.data(), width: info.width, height: info.height, src_pitch: slot->bitmap.pitch, bgr: subpixelType != Subpixel_VRGB);
1186 } else {
1187 qWarning(msg: "QFontEngine: Glyph rendered in unknown pixel_mode=%d", slot->bitmap.pixel_mode);
1188 return nullptr;
1189 }
1190
1191 if (!g) {
1192 g = new Glyph;
1193 g->data = nullptr;
1194 }
1195
1196 g->linearAdvance = info.linearAdvance;
1197 g->width = info.width;
1198 g->height = info.height;
1199 g->x = info.x;
1200 g->y = info.y;
1201 g->advance = info.xOff;
1202 g->format = format;
1203 delete [] g->data;
1204 g->data = glyph_buffer.take();
1205
1206 if (set)
1207 set->setGlyph(index: glyph, spp: subPixelPosition, glyph: g);
1208
1209 return g;
1210}
1211
1212QFontEngine::FaceId QFontEngineFT::faceId() const
1213{
1214 return face_id;
1215}
1216
1217QFontEngine::Properties QFontEngineFT::properties() const
1218{
1219 Properties p = freetype->properties();
1220 if (p.postscriptName.isEmpty()) {
1221 p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontFamily: fontDef.family.toUtf8());
1222 }
1223
1224 return freetype->properties();
1225}
1226
1227QFixed QFontEngineFT::emSquareSize() const
1228{
1229 if (FT_IS_SCALABLE(freetype->face))
1230 return freetype->face->units_per_EM;
1231 else
1232 return freetype->face->size->metrics.y_ppem;
1233}
1234
1235bool QFontEngineFT::getSfntTableData(uint tag, uchar *buffer, uint *length) const
1236{
1237 return ft_getSfntTable(user_data: freetype->face, tag, buffer, length);
1238}
1239
1240int QFontEngineFT::synthesized() const
1241{
1242 int s = 0;
1243 if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC))
1244 s = SynthesizedItalic;
1245 if ((fontDef.weight >= QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD))
1246 s |= SynthesizedBold;
1247 if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face))
1248 s |= SynthesizedStretch;
1249 return s;
1250}
1251
1252QFixed QFontEngineFT::ascent() const
1253{
1254 QFixed v = QFixed::fromFixed(fixed: metrics.ascender);
1255 if (scalableBitmapScaleFactor != 1)
1256 v *= scalableBitmapScaleFactor;
1257 return v;
1258}
1259
1260QFixed QFontEngineFT::capHeight() const
1261{
1262 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face: freetype->face, ft_sfnt_os2);
1263 if (os2 && os2->version >= 2) {
1264 lockFace();
1265 QFixed answer = QFixed::fromFixed(fixed: FT_MulFix(a: os2->sCapHeight, b: freetype->face->size->metrics.y_scale));
1266 unlockFace();
1267 return answer;
1268 }
1269 return calculatedCapHeight();
1270}
1271
1272QFixed QFontEngineFT::descent() const
1273{
1274 QFixed v = QFixed::fromFixed(fixed: -metrics.descender);
1275 if (scalableBitmapScaleFactor != 1)
1276 v *= scalableBitmapScaleFactor;
1277 return v;
1278}
1279
1280QFixed QFontEngineFT::leading() const
1281{
1282 QFixed v = QFixed::fromFixed(fixed: metrics.height - metrics.ascender + metrics.descender);
1283 if (scalableBitmapScaleFactor != 1)
1284 v *= scalableBitmapScaleFactor;
1285 return v;
1286}
1287
1288QFixed QFontEngineFT::xHeight() const
1289{
1290 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face: freetype->face, ft_sfnt_os2);
1291 if (os2 && os2->sxHeight) {
1292 lockFace();
1293 QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize();
1294 unlockFace();
1295 return answer;
1296 }
1297
1298 return QFontEngine::xHeight();
1299}
1300
1301QFixed QFontEngineFT::averageCharWidth() const
1302{
1303 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face: freetype->face, ft_sfnt_os2);
1304 if (os2 && os2->xAvgCharWidth) {
1305 lockFace();
1306 QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize();
1307 unlockFace();
1308 return answer;
1309 }
1310
1311 return QFontEngine::averageCharWidth();
1312}
1313
1314qreal QFontEngineFT::maxCharWidth() const
1315{
1316 QFixed max_advance = QFixed::fromFixed(fixed: metrics.max_advance);
1317 if (scalableBitmapScaleFactor != 1)
1318 max_advance *= scalableBitmapScaleFactor;
1319 return max_advance.toReal();
1320}
1321
1322QFixed QFontEngineFT::lineThickness() const
1323{
1324 return line_thickness;
1325}
1326
1327QFixed QFontEngineFT::underlinePosition() const
1328{
1329 return underline_position;
1330}
1331
1332void QFontEngineFT::doKerning(QGlyphLayout *g, QFontEngine::ShaperFlags flags) const
1333{
1334 if (!kerning_pairs_loaded) {
1335 kerning_pairs_loaded = true;
1336 lockFace();
1337 if (freetype->face->size->metrics.x_ppem != 0) {
1338 QFixed scalingFactor = emSquareSize() / QFixed(freetype->face->size->metrics.x_ppem);
1339 unlockFace();
1340 const_cast<QFontEngineFT *>(this)->loadKerningPairs(scalingFactor);
1341 } else {
1342 unlockFace();
1343 }
1344 }
1345
1346QT_WARNING_PUSH
1347QT_WARNING_DISABLE_DEPRECATED
1348 if (shouldUseDesignMetrics(flags) && !(fontDef.styleStrategy & QFont::ForceIntegerMetrics))
1349QT_WARNING_POP
1350 flags |= DesignMetrics;
1351 else
1352 flags &= ~DesignMetrics;
1353
1354 QFontEngine::doKerning(g, flags);
1355}
1356
1357static inline FT_Matrix QTransformToFTMatrix(const QTransform &matrix)
1358{
1359 FT_Matrix m;
1360
1361 m.xx = FT_Fixed(matrix.m11() * 65536);
1362 m.xy = FT_Fixed(-matrix.m21() * 65536);
1363 m.yx = FT_Fixed(-matrix.m12() * 65536);
1364 m.yy = FT_Fixed(matrix.m22() * 65536);
1365
1366 return m;
1367}
1368
1369QFontEngineFT::QGlyphSet *QFontEngineFT::loadGlyphSet(const QTransform &matrix)
1370{
1371 if (matrix.type() > QTransform::TxShear || !cacheEnabled)
1372 return nullptr;
1373
1374 // FT_Set_Transform only supports scalable fonts
1375 if (!FT_IS_SCALABLE(freetype->face))
1376 return matrix.type() <= QTransform::TxTranslate ? &defaultGlyphSet : nullptr;
1377
1378 FT_Matrix m = QTransformToFTMatrix(matrix);
1379
1380 QGlyphSet *gs = nullptr;
1381
1382 for (int i = 0; i < transformedGlyphSets.count(); ++i) {
1383 const QGlyphSet &g = transformedGlyphSets.at(i);
1384 if (g.transformationMatrix.xx == m.xx
1385 && g.transformationMatrix.xy == m.xy
1386 && g.transformationMatrix.yx == m.yx
1387 && g.transformationMatrix.yy == m.yy) {
1388
1389 // found a match, move it to the front
1390 transformedGlyphSets.move(from: i, to: 0);
1391 gs = &transformedGlyphSets[0];
1392 break;
1393 }
1394 }
1395
1396 if (!gs) {
1397 // don't cache more than 10 transformations
1398 if (transformedGlyphSets.count() >= 10) {
1399 transformedGlyphSets.move(from: transformedGlyphSets.size() - 1, to: 0);
1400 } else {
1401 transformedGlyphSets.prepend(t: QGlyphSet());
1402 }
1403 gs = &transformedGlyphSets[0];
1404 gs->clear();
1405 gs->transformationMatrix = m;
1406 gs->outline_drawing = fontDef.pixelSize * fontDef.pixelSize * qAbs(t: matrix.determinant()) > QT_MAX_CACHED_GLYPH_SIZE * QT_MAX_CACHED_GLYPH_SIZE;
1407 }
1408 Q_ASSERT(gs != nullptr);
1409
1410 return gs;
1411}
1412
1413void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
1414{
1415 FT_Face face = lockFace(scale: Unscaled);
1416 FT_Set_Transform(face, matrix: nullptr, delta: nullptr);
1417 FT_Load_Glyph(face, glyph_index: glyph, FT_LOAD_NO_BITMAP);
1418
1419 int left = face->glyph->metrics.horiBearingX;
1420 int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width;
1421 int top = face->glyph->metrics.horiBearingY;
1422 int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
1423
1424 QFixedPoint p;
1425 p.x = 0;
1426 p.y = 0;
1427
1428 metrics->width = QFixed::fromFixed(fixed: right-left);
1429 metrics->height = QFixed::fromFixed(fixed: top-bottom);
1430 metrics->x = QFixed::fromFixed(fixed: left);
1431 metrics->y = QFixed::fromFixed(fixed: -top);
1432 metrics->xoff = QFixed::fromFixed(fixed: face->glyph->advance.x);
1433
1434 if (!FT_IS_SCALABLE(freetype->face))
1435 QFreetypeFace::addBitmapToPath(slot: face->glyph, point: p, path);
1436 else
1437 QFreetypeFace::addGlyphToPath(face, g: face->glyph, point: p, path, x_scale: face->units_per_EM << 6, y_scale: face->units_per_EM << 6);
1438
1439 FT_Set_Transform(face, matrix: &freetype->matrix, delta: nullptr);
1440 unlockFace();
1441}
1442
1443bool QFontEngineFT::supportsTransformation(const QTransform &transform) const
1444{
1445 return transform.type() <= QTransform::TxRotate;
1446}
1447
1448void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1449{
1450 if (!glyphs.numGlyphs)
1451 return;
1452
1453 if (FT_IS_SCALABLE(freetype->face)) {
1454 QFontEngine::addOutlineToPath(x, y, glyphs, path, flags);
1455 } else {
1456 QVarLengthArray<QFixedPoint> positions;
1457 QVarLengthArray<glyph_t> positioned_glyphs;
1458 QTransform matrix;
1459 matrix.translate(dx: x, dy: y);
1460 getGlyphPositions(glyphs, matrix, flags, glyphs_out&: positioned_glyphs, positions);
1461
1462 FT_Face face = lockFace(scale: Unscaled);
1463 for (int gl = 0; gl < glyphs.numGlyphs; gl++) {
1464 FT_UInt glyph = positioned_glyphs[gl];
1465 FT_Load_Glyph(face, glyph_index: glyph, FT_LOAD_TARGET_MONO);
1466 QFreetypeFace::addBitmapToPath(slot: face->glyph, point: positions[gl], path);
1467 }
1468 unlockFace();
1469 }
1470}
1471
1472void QFontEngineFT::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs,
1473 QPainterPath *path, QTextItem::RenderFlags)
1474{
1475 FT_Face face = lockFace(scale: Unscaled);
1476
1477 for (int gl = 0; gl < numGlyphs; gl++) {
1478 FT_UInt glyph = glyphs[gl];
1479
1480 FT_Load_Glyph(face, glyph_index: glyph, FT_LOAD_NO_BITMAP);
1481
1482 FT_GlyphSlot g = face->glyph;
1483 if (g->format != FT_GLYPH_FORMAT_OUTLINE)
1484 continue;
1485 if (embolden)
1486 FT_GlyphSlot_Embolden(slot: g);
1487 if (obliquen)
1488 FT_GlyphSlot_Oblique(slot: g);
1489 QFreetypeFace::addGlyphToPath(face, g, point: positions[gl], path, x_scale: xsize, y_scale: ysize);
1490 }
1491 unlockFace();
1492}
1493
1494glyph_t QFontEngineFT::glyphIndex(uint ucs4) const
1495{
1496 glyph_t glyph = ucs4 < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[ucs4] : 0;
1497 if (glyph == 0) {
1498 FT_Face face = freetype->face;
1499 glyph = FT_Get_Char_Index(face, charcode: ucs4);
1500 if (glyph == 0) {
1501 // Certain fonts don't have no-break space and tab,
1502 // while we usually want to render them as space
1503 if (ucs4 == QChar::Nbsp || ucs4 == QChar::Tabulation) {
1504 glyph = FT_Get_Char_Index(face, charcode: QChar::Space);
1505 } else if (freetype->symbol_map) {
1506 // Symbol fonts can have more than one CMAPs, FreeType should take the
1507 // correct one for us by default, so we always try FT_Get_Char_Index
1508 // first. If it didn't work (returns 0), we will explicitly set the
1509 // CMAP to symbol font one and try again. symbol_map is not always the
1510 // correct one because in certain fonts like Wingdings symbol_map only
1511 // contains PUA codepoints instead of the common ones.
1512 FT_Set_Charmap(face, charmap: freetype->symbol_map);
1513 glyph = FT_Get_Char_Index(face, charcode: ucs4);
1514 FT_Set_Charmap(face, charmap: freetype->unicode_map);
1515 if (!glyph && symbol && ucs4 < 0x100)
1516 glyph = FT_Get_Char_Index(face, charcode: ucs4 + 0xf000);
1517 }
1518 }
1519 if (ucs4 < QFreetypeFace::cmapCacheSize)
1520 freetype->cmapCache[ucs4] = glyph;
1521 }
1522
1523 return glyph;
1524}
1525
1526bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
1527 QFontEngine::ShaperFlags flags) const
1528{
1529 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
1530 if (*nglyphs < len) {
1531 *nglyphs = len;
1532 return false;
1533 }
1534
1535 int glyph_pos = 0;
1536 if (freetype->symbol_map) {
1537 FT_Face face = freetype->face;
1538 QStringIterator it(str, str + len);
1539 while (it.hasNext()) {
1540 uint uc = it.next();
1541 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
1542 if ( !glyphs->glyphs[glyph_pos] ) {
1543 // Symbol fonts can have more than one CMAPs, FreeType should take the
1544 // correct one for us by default, so we always try FT_Get_Char_Index
1545 // first. If it didn't work (returns 0), we will explicitly set the
1546 // CMAP to symbol font one and try again. symbol_map is not always the
1547 // correct one because in certain fonts like Wingdings symbol_map only
1548 // contains PUA codepoints instead of the common ones.
1549 glyph_t glyph = FT_Get_Char_Index(face, charcode: uc);
1550 // Certain symbol fonts don't have no-break space (0xa0) and tab (0x9),
1551 // while we usually want to render them as space
1552 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
1553 uc = 0x20;
1554 glyph = FT_Get_Char_Index(face, charcode: uc);
1555 }
1556 if (!glyph) {
1557 FT_Set_Charmap(face, charmap: freetype->symbol_map);
1558 glyph = FT_Get_Char_Index(face, charcode: uc);
1559 FT_Set_Charmap(face, charmap: freetype->unicode_map);
1560 if (!glyph && symbol && uc < 0x100)
1561 glyph = FT_Get_Char_Index(face, charcode: uc + 0xf000);
1562 }
1563 glyphs->glyphs[glyph_pos] = glyph;
1564 if (uc < QFreetypeFace::cmapCacheSize)
1565 freetype->cmapCache[uc] = glyph;
1566 }
1567 ++glyph_pos;
1568 }
1569 } else {
1570 FT_Face face = freetype->face;
1571 QStringIterator it(str, str + len);
1572 while (it.hasNext()) {
1573 uint uc = it.next();
1574 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
1575 if (!glyphs->glyphs[glyph_pos]) {
1576 {
1577 redo:
1578 glyph_t glyph = FT_Get_Char_Index(face, charcode: uc);
1579 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
1580 uc = 0x20;
1581 goto redo;
1582 }
1583 glyphs->glyphs[glyph_pos] = glyph;
1584 if (uc < QFreetypeFace::cmapCacheSize)
1585 freetype->cmapCache[uc] = glyph;
1586 }
1587 }
1588 ++glyph_pos;
1589 }
1590 }
1591
1592 *nglyphs = glyph_pos;
1593 glyphs->numGlyphs = glyph_pos;
1594
1595 if (!(flags & GlyphIndicesOnly))
1596 recalcAdvances(glyphs, flags);
1597
1598 return true;
1599}
1600
1601bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const
1602{
1603 if (!FT_IS_SCALABLE(freetype->face))
1604 return false;
1605
1606 return default_hint_style == HintNone || default_hint_style == HintLight || (flags & DesignMetrics);
1607}
1608
1609QFixed QFontEngineFT::scaledBitmapMetrics(QFixed m) const
1610{
1611 return m * scalableBitmapScaleFactor;
1612}
1613
1614glyph_metrics_t QFontEngineFT::scaledBitmapMetrics(const glyph_metrics_t &m, const QTransform &t) const
1615{
1616 QTransform trans;
1617 trans.setMatrix(m11: t.m11(), m12: t.m12(), m13: t.m13(),
1618 m21: t.m21(), m22: t.m22(), m23: t.m23(),
1619 m31: 0, m32: 0, m33: t.m33());
1620 const qreal scaleFactor = scalableBitmapScaleFactor.toReal();
1621 trans.scale(sx: scaleFactor, sy: scaleFactor);
1622
1623 QRectF rect(m.x.toReal(), m.y.toReal(), m.width.toReal(), m.height.toReal());
1624 QPointF offset(m.xoff.toReal(), m.yoff.toReal());
1625
1626 rect = trans.mapRect(rect);
1627 offset = trans.map(p: offset);
1628
1629 glyph_metrics_t metrics;
1630 metrics.x = QFixed::fromReal(r: rect.x());
1631 metrics.y = QFixed::fromReal(r: rect.y());
1632 metrics.width = QFixed::fromReal(r: rect.width());
1633 metrics.height = QFixed::fromReal(r: rect.height());
1634 metrics.xoff = QFixed::fromReal(r: offset.x());
1635 metrics.yoff = QFixed::fromReal(r: offset.y());
1636 return metrics;
1637}
1638
1639void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
1640{
1641 FT_Face face = nullptr;
1642 bool design = shouldUseDesignMetrics(flags);
1643 for (int i = 0; i < glyphs->numGlyphs; i++) {
1644 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(index: glyphs->glyphs[i]) : nullptr;
1645 // Since we are passing Format_None to loadGlyph, use same default format logic as loadGlyph
1646 GlyphFormat acceptableFormat = (defaultFormat != Format_None) ? defaultFormat : Format_Mono;
1647 if (g && g->format == acceptableFormat) {
1648 glyphs->advances[i] = design ? QFixed::fromFixed(fixed: g->linearAdvance) : QFixed(g->advance);
1649 } else {
1650 if (!face)
1651 face = lockFace();
1652 g = loadGlyph(set: cacheEnabled ? &defaultGlyphSet : nullptr, glyph: glyphs->glyphs[i], subPixelPosition: 0, format: Format_None, fetchMetricsOnly: true);
1653 if (g)
1654 glyphs->advances[i] = design ? QFixed::fromFixed(fixed: g->linearAdvance) : QFixed(g->advance);
1655 else
1656 glyphs->advances[i] = design ? QFixed::fromFixed(fixed: face->glyph->linearHoriAdvance >> 10)
1657 : QFixed::fromFixed(fixed: face->glyph->metrics.horiAdvance).round();
1658 if (!cacheEnabled && g != &emptyGlyph)
1659 delete g;
1660 }
1661
1662 if (scalableBitmapScaleFactor != 1)
1663 glyphs->advances[i] *= scalableBitmapScaleFactor;
1664 }
1665 if (face)
1666 unlockFace();
1667
1668QT_WARNING_PUSH
1669QT_WARNING_DISABLE_DEPRECATED
1670 if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
1671QT_WARNING_POP
1672 for (int i = 0; i < glyphs->numGlyphs; ++i)
1673 glyphs->advances[i] = glyphs->advances[i].round();
1674 }
1675}
1676
1677glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
1678{
1679 FT_Face face = nullptr;
1680
1681 glyph_metrics_t overall;
1682 // initialize with line height, we get the same behaviour on all platforms
1683 if (!isScalableBitmap()) {
1684 overall.y = -ascent();
1685 overall.height = ascent() + descent();
1686 } else {
1687 overall.y = QFixed::fromFixed(fixed: -metrics.ascender);
1688 overall.height = QFixed::fromFixed(fixed: metrics.ascender - metrics.descender);
1689 }
1690
1691 QFixed ymax = 0;
1692 QFixed xmax = 0;
1693 for (int i = 0; i < glyphs.numGlyphs; i++) {
1694 // If shaping has found this should be ignored, ignore it.
1695 if (!glyphs.advances[i] || glyphs.attributes[i].dontPrint)
1696 continue;
1697 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(index: glyphs.glyphs[i]) : nullptr;
1698 if (!g) {
1699 if (!face)
1700 face = lockFace();
1701 g = loadGlyph(set: cacheEnabled ? &defaultGlyphSet : nullptr, glyph: glyphs.glyphs[i], subPixelPosition: 0, format: Format_None, fetchMetricsOnly: true);
1702 }
1703 if (g) {
1704 QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
1705 QFixed y = overall.yoff + glyphs.offsets[i].y - g->y;
1706 overall.x = qMin(a: overall.x, b: x);
1707 overall.y = qMin(a: overall.y, b: y);
1708 xmax = qMax(a: xmax, b: x + g->width);
1709 ymax = qMax(a: ymax, b: y + g->height);
1710 overall.xoff += g->advance;
1711 if (!cacheEnabled && g != &emptyGlyph)
1712 delete g;
1713 } else {
1714 int left = FLOOR(face->glyph->metrics.horiBearingX);
1715 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1716 int top = CEIL(face->glyph->metrics.horiBearingY);
1717 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1718
1719 QFixed x = overall.xoff + glyphs.offsets[i].x - (-TRUNC(left));
1720 QFixed y = overall.yoff + glyphs.offsets[i].y - TRUNC(top);
1721 overall.x = qMin(a: overall.x, b: x);
1722 overall.y = qMin(a: overall.y, b: y);
1723 xmax = qMax(a: xmax, b: x + TRUNC(right - left));
1724 ymax = qMax(a: ymax, b: y + TRUNC(top - bottom));
1725 overall.xoff += int(TRUNC(ROUND(face->glyph->advance.x)));
1726 }
1727 }
1728 overall.height = qMax(a: overall.height, b: ymax - overall.y);
1729 overall.width = xmax - overall.x;
1730
1731 if (face)
1732 unlockFace();
1733
1734 if (isScalableBitmap())
1735 overall = scaledBitmapMetrics(m: overall, t: QTransform());
1736 return overall;
1737}
1738
1739glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph)
1740{
1741 FT_Face face = nullptr;
1742 glyph_metrics_t overall;
1743 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(index: glyph) : nullptr;
1744 if (!g) {
1745 face = lockFace();
1746 g = loadGlyph(set: cacheEnabled ? &defaultGlyphSet : nullptr, glyph, subPixelPosition: 0, format: Format_None, fetchMetricsOnly: true);
1747 }
1748 if (g) {
1749 overall.x = g->x;
1750 overall.y = -g->y;
1751 overall.width = g->width;
1752 overall.height = g->height;
1753 overall.xoff = g->advance;
1754QT_WARNING_PUSH
1755QT_WARNING_DISABLE_DEPRECATED
1756 if (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
1757QT_WARNING_POP
1758 overall.xoff = overall.xoff.round();
1759 if (!cacheEnabled && g != &emptyGlyph)
1760 delete g;
1761 } else {
1762 int left = FLOOR(face->glyph->metrics.horiBearingX);
1763 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1764 int top = CEIL(face->glyph->metrics.horiBearingY);
1765 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1766
1767 overall.width = TRUNC(right-left);
1768 overall.height = TRUNC(top-bottom);
1769 overall.x = TRUNC(left);
1770 overall.y = -TRUNC(top);
1771 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
1772 }
1773 if (face)
1774 unlockFace();
1775
1776 if (isScalableBitmap())
1777 overall = scaledBitmapMetrics(m: overall, t: QTransform());
1778 return overall;
1779}
1780
1781glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matrix)
1782{
1783 return alphaMapBoundingBox(glyph, subPixelPosition: 0, matrix, format: QFontEngine::Format_None);
1784}
1785
1786glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixelPosition, const QTransform &matrix, QFontEngine::GlyphFormat format)
1787{
1788 // When rendering glyphs into a cache via the alphaMap* functions, we disable
1789 // outline drawing. To ensure the bounding box matches the rendered glyph, we
1790 // need to do the same here.
1791 Glyph *g = loadGlyphFor(g: glyph, subPixelPosition, format, t: matrix, fetchBoundingBox: true, disableOutlineDrawing: true);
1792
1793 glyph_metrics_t overall;
1794 if (g) {
1795 overall.x = g->x;
1796 overall.y = -g->y;
1797 overall.width = g->width;
1798 overall.height = g->height;
1799 overall.xoff = g->advance;
1800 if (!cacheEnabled && g != &emptyGlyph)
1801 delete g;
1802 } else {
1803 FT_Face face = lockFace();
1804 int left = FLOOR(face->glyph->metrics.horiBearingX);
1805 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1806 int top = CEIL(face->glyph->metrics.horiBearingY);
1807 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1808
1809 overall.width = TRUNC(right-left);
1810 overall.height = TRUNC(top-bottom);
1811 overall.x = TRUNC(left);
1812 overall.y = -TRUNC(top);
1813 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
1814 unlockFace();
1815 }
1816
1817 if (isScalableBitmap())
1818 overall = scaledBitmapMetrics(m: overall, t: matrix);
1819 return overall;
1820}
1821
1822static inline QImage alphaMapFromGlyphData(QFontEngineFT::Glyph *glyph, QFontEngine::GlyphFormat glyphFormat)
1823{
1824 if (glyph == nullptr || glyph->height == 0 || glyph->width == 0)
1825 return QImage();
1826
1827 QImage::Format format = QImage::Format_Invalid;
1828 int bytesPerLine = -1;
1829 switch (glyphFormat) {
1830 case QFontEngine::Format_Mono:
1831 format = QImage::Format_Mono;
1832 bytesPerLine = ((glyph->width + 31) & ~31) >> 3;
1833 break;
1834 case QFontEngine::Format_A8:
1835 format = QImage::Format_Alpha8;
1836 bytesPerLine = (glyph->width + 3) & ~3;
1837 break;
1838 case QFontEngine::Format_A32:
1839 format = QImage::Format_RGB32;
1840 bytesPerLine = glyph->width * 4;
1841 break;
1842 default:
1843 Q_UNREACHABLE();
1844 };
1845
1846 QImage img(static_cast<const uchar *>(glyph->data), glyph->width, glyph->height, bytesPerLine, format);
1847 if (format == QImage::Format_Mono)
1848 img.setColor(i: 1, c: QColor(Qt::white).rgba()); // Expands color table to 2 items; item 0 set to transparent.
1849 return img;
1850}
1851
1852QFontEngine::Glyph *QFontEngineFT::glyphData(glyph_t glyphIndex, QFixed subPixelPosition,
1853 QFontEngine::GlyphFormat neededFormat, const QTransform &t)
1854{
1855 Q_ASSERT(cacheEnabled);
1856
1857 if (isBitmapFont())
1858 neededFormat = Format_Mono;
1859 else if (neededFormat == Format_None && defaultFormat != Format_None)
1860 neededFormat = defaultFormat;
1861 else if (neededFormat == Format_None)
1862 neededFormat = Format_A8;
1863
1864 Glyph *glyph = loadGlyphFor(g: glyphIndex, subPixelPosition, format: neededFormat, t);
1865 if (!glyph || !glyph->width || !glyph->height)
1866 return nullptr;
1867
1868 return glyph;
1869}
1870
1871static inline bool is2dRotation(const QTransform &t)
1872{
1873 return qFuzzyCompare(p1: t.m11(), p2: t.m22()) && qFuzzyCompare(p1: t.m12(), p2: -t.m21())
1874 && qFuzzyCompare(p1: t.m11()*t.m22() - t.m12()*t.m21(), p2: qreal(1.0));
1875}
1876
1877QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g,
1878 QFixed subPixelPosition,
1879 GlyphFormat format,
1880 const QTransform &t,
1881 bool fetchBoundingBox,
1882 bool disableOutlineDrawing)
1883{
1884 QGlyphSet *glyphSet = loadGlyphSet(matrix: t);
1885 if (glyphSet != nullptr && glyphSet->outline_drawing && !disableOutlineDrawing && !fetchBoundingBox)
1886 return nullptr;
1887
1888 Glyph *glyph = glyphSet != nullptr ? glyphSet->getGlyph(index: g, subPixelPosition) : nullptr;
1889 if (!glyph || glyph->format != format || (!fetchBoundingBox && !glyph->data)) {
1890 QScopedValueRollback<HintStyle> saved_default_hint_style(default_hint_style);
1891 if (t.type() >= QTransform::TxScale && !is2dRotation(t))
1892 default_hint_style = HintNone; // disable hinting if the glyphs are transformed
1893
1894 lockFace();
1895 FT_Matrix m = this->matrix;
1896 FT_Matrix ftMatrix = glyphSet != nullptr ? glyphSet->transformationMatrix : QTransformToFTMatrix(matrix: t);
1897 FT_Matrix_Multiply(a: &ftMatrix, b: &m);
1898 freetype->matrix = m;
1899 glyph = loadGlyph(set: glyphSet, glyph: g, subPixelPosition, format, fetchMetricsOnly: false, disableOutlineDrawing);
1900 unlockFace();
1901 }
1902
1903 return glyph;
1904}
1905
1906QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition)
1907{
1908 return alphaMapForGlyph(glyph: g, subPixelPosition, t: QTransform());
1909}
1910
1911QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t)
1912{
1913 const GlyphFormat neededFormat = antialias ? Format_A8 : Format_Mono;
1914
1915 Glyph *glyph = loadGlyphFor(g, subPixelPosition, format: neededFormat, t, fetchBoundingBox: false, disableOutlineDrawing: true);
1916
1917 QImage img = alphaMapFromGlyphData(glyph, glyphFormat: neededFormat);
1918 img = img.copy();
1919
1920 if (!cacheEnabled && glyph != &emptyGlyph)
1921 delete glyph;
1922
1923 return img;
1924}
1925
1926QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t)
1927{
1928 if (t.type() > QTransform::TxRotate)
1929 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
1930
1931 const GlyphFormat neededFormat = Format_A32;
1932
1933 Glyph *glyph = loadGlyphFor(g, subPixelPosition, format: neededFormat, t, fetchBoundingBox: false, disableOutlineDrawing: true);
1934
1935 QImage img = alphaMapFromGlyphData(glyph, glyphFormat: neededFormat);
1936 img = img.copy();
1937
1938 if (!cacheEnabled && glyph != &emptyGlyph)
1939 delete glyph;
1940
1941 if (!img.isNull())
1942 return img;
1943
1944 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
1945}
1946
1947QImage QFontEngineFT::bitmapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t, const QColor &color)
1948{
1949 Q_UNUSED(color);
1950
1951 Glyph *glyph = loadGlyphFor(g, subPixelPosition, format: defaultFormat, t);
1952 if (glyph == nullptr)
1953 return QImage();
1954
1955 QImage img;
1956 if (defaultFormat == GlyphFormat::Format_ARGB)
1957 img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_ARGB32_Premultiplied).copy();
1958 else if (defaultFormat == GlyphFormat::Format_Mono)
1959 img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_Mono).copy();
1960
1961 if (!img.isNull() && (!t.isIdentity() || scalableBitmapScaleFactor != 1)) {
1962 QTransform trans(t);
1963 const qreal scaleFactor = scalableBitmapScaleFactor.toReal();
1964 trans.scale(sx: scaleFactor, sy: scaleFactor);
1965 img = img.transformed(matrix: trans, mode: Qt::SmoothTransformation);
1966 }
1967
1968 if (!cacheEnabled && glyph != &emptyGlyph)
1969 delete glyph;
1970
1971 return img;
1972}
1973
1974void QFontEngineFT::removeGlyphFromCache(glyph_t glyph)
1975{
1976 defaultGlyphSet.removeGlyphFromCache(index: glyph, subPixelPosition: 0);
1977}
1978
1979int QFontEngineFT::glyphCount() const
1980{
1981 int count = 0;
1982 FT_Face face = lockFace();
1983 if (face) {
1984 count = face->num_glyphs;
1985 unlockFace();
1986 }
1987 return count;
1988}
1989
1990FT_Face QFontEngineFT::lockFace(Scaling scale) const
1991{
1992 freetype->lock();
1993 FT_Face face = freetype->face;
1994 if (scale == Unscaled) {
1995 if (FT_Set_Char_Size(face, char_width: face->units_per_EM << 6, char_height: face->units_per_EM << 6, horz_resolution: 0, vert_resolution: 0) == 0) {
1996 freetype->xsize = face->units_per_EM << 6;
1997 freetype->ysize = face->units_per_EM << 6;
1998 }
1999 } else if (freetype->xsize != xsize || freetype->ysize != ysize) {
2000 FT_Set_Char_Size(face, char_width: xsize, char_height: ysize, horz_resolution: 0, vert_resolution: 0);
2001 freetype->xsize = xsize;
2002 freetype->ysize = ysize;
2003 }
2004 if (freetype->matrix.xx != matrix.xx ||
2005 freetype->matrix.yy != matrix.yy ||
2006 freetype->matrix.xy != matrix.xy ||
2007 freetype->matrix.yx != matrix.yx) {
2008 freetype->matrix = matrix;
2009 FT_Set_Transform(face, matrix: &freetype->matrix, delta: nullptr);
2010 }
2011
2012 return face;
2013}
2014
2015void QFontEngineFT::unlockFace() const
2016{
2017 freetype->unlock();
2018}
2019
2020FT_Face QFontEngineFT::non_locked_face() const
2021{
2022 return freetype->face;
2023}
2024
2025
2026QFontEngineFT::QGlyphSet::QGlyphSet()
2027 : outline_drawing(false)
2028{
2029 transformationMatrix.xx = 0x10000;
2030 transformationMatrix.yy = 0x10000;
2031 transformationMatrix.xy = 0;
2032 transformationMatrix.yx = 0;
2033 memset(s: fast_glyph_data, c: 0, n: sizeof(fast_glyph_data));
2034 fast_glyph_count = 0;
2035}
2036
2037QFontEngineFT::QGlyphSet::~QGlyphSet()
2038{
2039 clear();
2040}
2041
2042void QFontEngineFT::QGlyphSet::clear()
2043{
2044 if (fast_glyph_count > 0) {
2045 for (int i = 0; i < 256; ++i) {
2046 if (fast_glyph_data[i]) {
2047 delete fast_glyph_data[i];
2048 fast_glyph_data[i] = nullptr;
2049 }
2050 }
2051 fast_glyph_count = 0;
2052 }
2053 qDeleteAll(c: glyph_data);
2054 glyph_data.clear();
2055}
2056
2057void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index, QFixed subPixelPosition)
2058{
2059 if (useFastGlyphData(index, subPixelPosition)) {
2060 if (fast_glyph_data[index]) {
2061 delete fast_glyph_data[index];
2062 fast_glyph_data[index] = nullptr;
2063 if (fast_glyph_count > 0)
2064 --fast_glyph_count;
2065 }
2066 } else {
2067 delete glyph_data.take(akey: GlyphAndSubPixelPosition(index, subPixelPosition));
2068 }
2069}
2070
2071void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index, QFixed subPixelPosition, Glyph *glyph)
2072{
2073 if (useFastGlyphData(index, subPixelPosition)) {
2074 if (!fast_glyph_data[index])
2075 ++fast_glyph_count;
2076 fast_glyph_data[index] = glyph;
2077 } else {
2078 glyph_data.insert(akey: GlyphAndSubPixelPosition(index, subPixelPosition), avalue: glyph);
2079 }
2080}
2081
2082int QFontEngineFT::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
2083{
2084 lockFace();
2085 bool hsubpixel = true;
2086 int vfactor = 1;
2087 int load_flags = loadFlags(set: nullptr, format: Format_A8, flags, hsubpixel, vfactor);
2088 int result = freetype->getPointInOutline(glyph, flags: load_flags, point, xpos, ypos, nPoints);
2089 unlockFace();
2090 return result;
2091}
2092
2093bool QFontEngineFT::initFromFontEngine(const QFontEngineFT *fe)
2094{
2095 if (!init(faceId: fe->faceId(), antialias: fe->antialias, format: fe->defaultFormat, freetypeFace: fe->freetype))
2096 return false;
2097
2098 // Increase the reference of this QFreetypeFace since one more QFontEngineFT
2099 // will be using it
2100 freetype->ref.ref();
2101
2102 default_load_flags = fe->default_load_flags;
2103 default_hint_style = fe->default_hint_style;
2104 antialias = fe->antialias;
2105 transform = fe->transform;
2106 embolden = fe->embolden;
2107 obliquen = fe->obliquen;
2108 subpixelType = fe->subpixelType;
2109 lcdFilterType = fe->lcdFilterType;
2110 embeddedbitmap = fe->embeddedbitmap;
2111
2112 return true;
2113}
2114
2115QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize) const
2116{
2117 QFontDef fontDef(this->fontDef);
2118 fontDef.pixelSize = pixelSize;
2119 QFontEngineFT *fe = new QFontEngineFT(fontDef);
2120 if (!fe->initFromFontEngine(fe: this)) {
2121 delete fe;
2122 return nullptr;
2123 } else {
2124 return fe;
2125 }
2126}
2127
2128Qt::HANDLE QFontEngineFT::handle() const
2129{
2130 return non_locked_face();
2131}
2132
2133QT_END_NAMESPACE
2134
2135#endif // QT_NO_FREETYPE
2136

source code of qtbase/src/platformsupport/fontdatabases/freetype/qfontengine_ft.cpp