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

source code of qtbase/src/gui/text/freetype/qfontengine_ft.cpp