Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qfontengine_ft.cpp
Go to the documentation of this file.
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// Qt-Security score:critical reason:data-parser
4
5#include "qdir.h"
6#include "qmetatype.h"
7#include "qtextstream.h"
8#include "qvariant.h"
10#include "private/qfontdatabase_p.h"
11#include "private/qimage_p.h"
12#include <private/qstringiterator_p.h>
13#include <qguiapplication.h>
14#include <qscreen.h>
15#include <qpa/qplatformscreen.h>
16#include <QtCore/QUuid>
17#include <QtCore/QLoggingCategory>
18#include <QtGui/QPainterPath>
19
20#ifndef QT_NO_FREETYPE
21
22#include "qfile.h"
23#include "qfileinfo.h"
24#include <qscopedvaluerollback.h>
25#include "qthreadstorage.h"
26#include <qmath.h>
27#include <qendian.h>
28#include <private/qcolrpaintgraphrenderer_p.h>
29
30#include <memory>
31
32#include <ft2build.h>
33#include FT_FREETYPE_H
34#include FT_OUTLINE_H
35#include FT_SYNTHESIS_H
36#include FT_TRUETYPE_TABLES_H
37#include FT_TYPE1_TABLES_H
38#include FT_GLYPH_H
39#include FT_MODULE_H
40#include FT_LCD_FILTER_H
41#include FT_MULTIPLE_MASTERS_H
42
43#if defined(FT_CONFIG_OPTIONS_H)
44#include FT_CONFIG_OPTIONS_H
45#endif
46
47#if defined(FT_FONT_FORMATS_H)
48#include FT_FONT_FORMATS_H
49#endif
50
51#ifdef QT_LINUXBASE
52#include FT_ERRORS_H
53#endif
54
56
57using namespace Qt::StringLiterals;
58
59#define FLOOR(x) ((x) & -64)
60#define CEIL(x) (((x)+63) & -64)
61#define TRUNC(x) ((x) >> 6)
62#define ROUND(x) (((x)+32) & -64)
63
64static bool ft_getSfntTable(void *user_data, uint tag, uchar *buffer, uint *length)
65{
66 FT_Face face = (FT_Face)user_data;
67
68 bool result = false;
69 if (FT_IS_SFNT(face)) {
70 FT_ULong len = *length;
71 result = FT_Load_Sfnt_Table(face, tag, 0, buffer, &len) == FT_Err_Ok
72 && len > 0;
73 *length = len;
74 }
75
76 return result;
77}
78
80
82#ifdef Q_OS_WIN
83 QFontEngineFT::HintFull;
84#else
85 QFontEngineFT::HintNone;
86#endif
87
88// -------------------------- Freetype support ------------------------------
89
91{
92public:
94 : library(nullptr)
95 { }
97
98 struct FaceStyle {
99 QString faceFileName;
100 QString styleName;
101
102 FaceStyle(QString faceFileName, QString styleName)
105 {}
106 };
107
112};
113
115{
116 for (auto iter = faces.cbegin(); iter != faces.cend(); ++iter) {
117 iter.value()->cleanup();
118 if (!iter.value()->ref.deref())
119 delete iter.value();
120 }
121 faces.clear();
122
123 for (auto iter = staleFaces.cbegin(); iter != staleFaces.cend(); ++iter) {
124 (*iter)->cleanup();
125 if (!(*iter)->ref.deref())
126 delete *iter;
127 }
128 staleFaces.clear();
129
130 FT_Done_FreeType(library);
131 library = nullptr;
132}
133
134inline bool operator==(const QtFreetypeData::FaceStyle &style1, const QtFreetypeData::FaceStyle &style2)
135{
136 return style1.faceFileName == style2.faceFileName && style1.styleName == style2.styleName;
137}
138
139inline size_t qHash(const QtFreetypeData::FaceStyle &style, size_t seed)
140{
141 return qHashMulti(seed, style.faceFileName, style.styleName);
142}
143
144Q_GLOBAL_STATIC(QThreadStorage<QtFreetypeData *>, theFreetypeData)
145
147{
148 QtFreetypeData *&freetypeData = theFreetypeData()->localData();
149 if (!freetypeData)
150 freetypeData = new QtFreetypeData;
151 if (!freetypeData->library) {
152 FT_Init_FreeType(&freetypeData->library);
153#if defined(FT_FONT_FORMATS_H)
154 // Freetype defaults to disabling stem-darkening on CFF, we re-enable it.
155 FT_Bool no_darkening = false;
156 FT_Property_Set(freetypeData->library, "cff", "no-stem-darkening", &no_darkening);
157#endif
158 }
159 return freetypeData;
160}
161
163{
164 QtFreetypeData *freetypeData = qt_getFreetypeData();
165 Q_ASSERT(freetypeData->library);
166 return freetypeData->library;
167}
168
169int QFreetypeFace::fsType() const
170{
171 int fsType = 0;
172 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
173 if (os2)
174 fsType = os2->fsType;
175 return fsType;
176}
177
178int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
179{
180 if (int error = FT_Load_Glyph(face, glyph, flags))
181 return error;
182
183 if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
184 return Err_Invalid_SubTable;
185
186 *nPoints = face->glyph->outline.n_points;
187 if (!(*nPoints))
188 return Err_Ok;
189
190 if (point >= *nPoints)
191 return Err_Invalid_SubTable;
192
193 *xpos = QFixed::fromFixed(face->glyph->outline.points[point].x);
194 *ypos = QFixed::fromFixed(face->glyph->outline.points[point].y);
195
196 return Err_Ok;
197}
198
199bool QFreetypeFace::isScalableBitmap() const
200{
201#ifdef FT_HAS_COLOR
202 return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face);
203#else
204 return false;
205#endif
206}
207
209
210/*
211 * One font file can contain more than one font (bold/italic for example)
212 * find the right one and return it.
213 *
214 * Returns the freetype face or 0 in case of an empty file or any other problems
215 * (like not being able to open the file)
216 */
217QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
218 const QByteArray &fontData)
219{
220 if (face_id.filename.isEmpty() && fontData.isEmpty())
221 return nullptr;
222
223 QtFreetypeData *freetypeData = qt_getFreetypeData();
224
225 // Purge any stale face that is now ready to be deleted
226 freetypeData->staleFaces.removeIf([](QFreetypeFace *face) {
227 if (face->ref.loadRelaxed() == 1) {
228 face->cleanup();
229 delete face;
230 return true;
231 }
232 return false;
233 });
234
235 QFreetypeFace *freetype = nullptr;
236 auto it = freetypeData->faces.find(face_id);
237 if (it != freetypeData->faces.end()) {
238 freetype = *it;
239
240 Q_ASSERT(freetype->ref.loadRelaxed() > 0);
241 if (freetype->ref.loadRelaxed() == 1) {
242 // If there is only one reference left to the face, it means it is only referenced by
243 // the cache itself, and thus it is in cleanup state (but the final outside reference
244 // was removed on a different thread so it could not be deleted right away). We then
245 // complete the cleanup and pretend we didn't find it, so that it can be re-created with
246 // the present state.
247 freetype->cleanup();
248 freetypeData->faces.erase(it);
249 delete freetype;
250 freetype = nullptr;
251 } else {
252 freetype->ref.ref();
253 }
254 }
255
256 if (!freetype) {
257 const auto deleter = [](QFreetypeFace *f) { delete f; };
258 std::unique_ptr<QFreetypeFace, decltype(deleter)> newFreetype(new QFreetypeFace, deleter);
259 FT_Face face;
260 FT_Face tmpFace;
261 if (!face_id.filename.isEmpty()) {
262 QString fileName = QFile::decodeName(face_id.filename);
263 if (const char *prefix = ":qmemoryfonts/"; face_id.filename.startsWith(prefix)) {
264 // from qfontdatabase.cpp
265 QByteArray idx = face_id.filename;
266 idx.remove(0, strlen(prefix)); // remove ':qmemoryfonts/'
267 bool ok = false;
268 newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok));
269 if (!ok)
270 newFreetype->fontData = QByteArray();
271 } else if (!QFileInfo(fileName).isNativePath()) {
272 QFile file(fileName);
273 if (!file.open(QIODevice::ReadOnly)) {
274 return nullptr;
275 }
276 newFreetype->fontData = file.readAll();
277 }
278 } else {
279 newFreetype->fontData = fontData;
280 }
281
282 FT_Int major, minor, patch;
283 FT_Library_Version(qt_getFreetype(), &major, &minor, &patch);
284 const bool goodVersion = major > 2 || (major == 2 && minor > 13) || (major == 2 && minor == 13 && patch > 2);
285
286 if (!newFreetype->fontData.isEmpty()) {
287 if (FT_New_Memory_Face(freetypeData->library,
288 (const FT_Byte *)newFreetype->fontData.constData(),
289 newFreetype->fontData.size(),
290 face_id.index,
291 &face)) {
292 return nullptr;
293 }
294
295 // On older Freetype versions, we create a temporary duplicate of the FT_Face to work
296 // around a bug, see further down.
297 if (goodVersion) {
298 tmpFace = face;
299 if (FT_Reference_Face(face))
300 tmpFace = nullptr;
301 } else if (!FT_HAS_MULTIPLE_MASTERS(face)
302 || FT_New_Memory_Face(freetypeData->library,
303 (const FT_Byte *)newFreetype->fontData.constData(),
304 newFreetype->fontData.size(),
305 face_id.index,
306 &tmpFace) != FT_Err_Ok) {
307 tmpFace = nullptr;
308 }
309 } else {
310 if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face))
311 return nullptr;
312
313 // On older Freetype versions, we create a temporary duplicate of the FT_Face to work
314 // around a bug, see further down.
315 if (goodVersion) {
316 tmpFace = face;
317 if (FT_Reference_Face(face))
318 tmpFace = nullptr;
319 } else if (!FT_HAS_MULTIPLE_MASTERS(face)
320 || FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &tmpFace) != FT_Err_Ok) {
321 tmpFace = nullptr;
322 }
323 }
324
325#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
326 if (face_id.instanceIndex >= 0) {
327 qCDebug(lcFontMatch)
328 << "Selecting named instance" << (face_id.instanceIndex)
329 << "in" << face_id.filename;
330 FT_Set_Named_Instance(face, face_id.instanceIndex + 1);
331 }
332#endif
333
334 // Due to a bug in Freetype 2.13.2 and earlier causing just a call to FT_Get_MM_Var() on
335 // specific fonts to corrupt the FT_Face so that loading glyphs will later fail, we use a
336 // temporary FT_Face here which can be thrown away after. The bug has been fixed in
337 // Freetype 2.13.3.
338 if (tmpFace != nullptr) {
339 FT_MM_Var *var;
340 if (FT_Get_MM_Var(tmpFace, &var) == FT_Err_Ok) {
341 for (FT_UInt i = 0; i < var->num_axis; ++i) {
342 FT_Var_Axis *axis = var->axis + i;
343
344 QFontVariableAxis fontVariableAxis;
345 if (const auto tag = QFont::Tag::fromValue(axis->tag)) {
346 fontVariableAxis.setTag(*tag);
347 } else {
348 qWarning() << "QFreetypeFace::getFace: Invalid variable axis tag encountered"
349 << axis->tag;
350 }
351
352 fontVariableAxis.setMinimumValue(axis->minimum / 65536.0);
353 fontVariableAxis.setMaximumValue(axis->maximum / 65536.0);
354 fontVariableAxis.setDefaultValue(axis->def / 65536.0);
355 fontVariableAxis.setName(QString::fromUtf8(axis->name));
356
357 newFreetype->variableAxisList.append(fontVariableAxis);
358 }
359
360 if (!face_id.variableAxes.isEmpty()) {
361 QVarLengthArray<FT_Fixed, 16> coords(var->num_axis);
362 FT_Get_Var_Design_Coordinates(face, var->num_axis, coords.data());
363 for (qsizetype i = 0; i < newFreetype->variableAxisList.size(); ++i) {
364 const QFontVariableAxis &axis = newFreetype->variableAxisList.at(i);
365 if (axis.tag().isValid()) {
366 const auto it = face_id.variableAxes.constFind(axis.tag());
367 if (it != face_id.variableAxes.constEnd())
368 coords[i] = FT_Fixed(*it * 65536);
369 }
370 }
371 FT_Set_Var_Design_Coordinates(face, var->num_axis, coords.data());
372 }
373
374 FT_Done_MM_Var(qt_getFreetype(), var);
375 }
376
377 FT_Done_Face(tmpFace);
378 }
379
380 newFreetype->face = face;
381 newFreetype->mm_var = nullptr;
382 if (FT_IS_NAMED_INSTANCE(newFreetype->face)) {
383 FT_Error ftresult;
384 ftresult = FT_Get_MM_Var(face, &newFreetype->mm_var);
385 if (ftresult != FT_Err_Ok)
386 newFreetype->mm_var = nullptr;
387 }
388
389 newFreetype->ref.storeRelaxed(1);
390 newFreetype->xsize = 0;
391 newFreetype->ysize = 0;
392 newFreetype->matrix.xx = 0x10000;
393 newFreetype->matrix.yy = 0x10000;
394 newFreetype->matrix.xy = 0;
395 newFreetype->matrix.yx = 0;
396 newFreetype->unicode_map = nullptr;
397 newFreetype->symbol_map = nullptr;
398
399 memset(newFreetype->cmapCache, 0, sizeof(newFreetype->cmapCache));
400
401 for (int i = 0; i < newFreetype->face->num_charmaps; ++i) {
402 FT_CharMap cm = newFreetype->face->charmaps[i];
403 switch(cm->encoding) {
404 case FT_ENCODING_UNICODE:
405 newFreetype->unicode_map = cm;
406 break;
407 case FT_ENCODING_APPLE_ROMAN:
408 case FT_ENCODING_ADOBE_LATIN_1:
409 if (!newFreetype->unicode_map || newFreetype->unicode_map->encoding != FT_ENCODING_UNICODE)
410 newFreetype->unicode_map = cm;
411 break;
412 case FT_ENCODING_ADOBE_CUSTOM:
413 case FT_ENCODING_MS_SYMBOL:
414 if (!newFreetype->symbol_map)
415 newFreetype->symbol_map = cm;
416 break;
417 default:
418 break;
419 }
420 }
421
422 if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1)
423 FT_Set_Char_Size(face, newFreetype->face->available_sizes[0].x_ppem, newFreetype->face->available_sizes[0].y_ppem, 0, 0);
424
425 FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map);
426
427 QT_TRY {
428 freetypeData->faces.insert(face_id, newFreetype.get());
429 } QT_CATCH(...) {
430 newFreetype.release()->release(face_id);
431 // we could return null in principle instead of throwing
432 QT_RETHROW;
433 }
434 freetype = newFreetype.release();
435 freetype->ref.ref();
436 }
437 return freetype;
438}
439
440void QFreetypeFace::cleanup()
441{
442 hbFace.reset();
443 if (mm_var)
444 FT_Done_MM_Var(qt_getFreetype(), mm_var);
445 mm_var = nullptr;
446 FT_Done_Face(face);
447 face = nullptr;
448}
449
450void QFreetypeFace::release(const QFontEngine::FaceId &face_id)
451{
452 Q_UNUSED(face_id);
453 bool deleteThis = !ref.deref();
454
455 // If the only reference left over is the cache's reference, we remove it from the cache,
456 // granted that we are on the correct thread. If not, we leave it there to be cleaned out
457 // later. While we are at it, we also purge all left over faces which are only referenced
458 // from the cache.
459 if (face && ref.loadRelaxed() == 1) {
460 QtFreetypeData *freetypeData = qt_getFreetypeData();
461
462 freetypeData->staleFaces.removeIf([&deleteThis, this](QFreetypeFace *face){
463 if (face->ref.loadRelaxed() == 1) {
464 face->cleanup();
465 if (face == this)
466 deleteThis = true;
467 else
468 delete face;
469 return true;
470 }
471 return false;
472 });
473
474 for (auto it = freetypeData->faces.constBegin();
475 it != freetypeData->faces.constEnd();
476 it = freetypeData->faces.erase(it)) {
477 if (it.value()->ref.loadRelaxed() == 1) {
478 it.value()->cleanup();
479 if (it.value() == this)
480 deleteThis = true; // This face, delete at end of function for safety
481 else
482 delete it.value();
483 } else {
484 freetypeData->staleFaces.append(it.value());
485 }
486 }
487
488 if (freetypeData->faces.isEmpty() && freetypeData->staleFaces.isEmpty()) {
489 FT_Done_FreeType(freetypeData->library);
490 freetypeData->library = nullptr;
491 }
492 }
493
494 if (deleteThis)
495 delete this;
496}
497
498static int computeFaceIndex(const QString &faceFileName, const QString &styleName)
499{
500 FT_Library library = qt_getFreetype();
501
502 int faceIndex = 0;
503 int numFaces = 0;
504
505 do {
506 FT_Face face;
507
508 FT_Error error = FT_New_Face(library, faceFileName.toUtf8().constData(), faceIndex, &face);
509 if (error != FT_Err_Ok) {
510 qDebug() << "FT_New_Face failed for face index" << faceIndex << ':' << Qt::hex << error;
511 break;
512 }
513
514 const bool found = QLatin1StringView(face->style_name) == styleName;
515 numFaces = face->num_faces;
516
517 FT_Done_Face(face);
518
519 if (found)
520 return faceIndex;
521 } while (++faceIndex < numFaces);
522
523 // Fall back to the first font face in the file
524 return 0;
525}
526
527int QFreetypeFace::getFaceIndexByStyleName(const QString &faceFileName, const QString &styleName)
528{
529 QtFreetypeData *freetypeData = qt_getFreetypeData();
530
531 // Try to get from cache
532 QtFreetypeData::FaceStyle faceStyle(faceFileName, styleName);
533 int faceIndex = freetypeData->faceIndices.value(faceStyle, -1);
534
535 if (faceIndex >= 0)
536 return faceIndex;
537
538 faceIndex = computeFaceIndex(faceFileName, styleName);
539
540 freetypeData->faceIndices.insert(faceStyle, faceIndex);
541
542 return faceIndex;
543}
544
545void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor)
546{
547 *ysize = qRound(fontDef.pixelSize * 64);
548 *xsize = *ysize * fontDef.stretch / 100;
549 *scalableBitmapScaleFactor = 1;
550 *outline_drawing = false;
551
552 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) {
553 int best = 0;
554 if (!isScalableBitmap()) {
555 /*
556 * Bitmap only faces must match exactly, so find the closest
557 * one (height dominant search)
558 */
559 for (int i = 1; i < face->num_fixed_sizes; i++) {
560 if (qAbs(*ysize - face->available_sizes[i].y_ppem) <
561 qAbs(*ysize - face->available_sizes[best].y_ppem) ||
562 (qAbs(*ysize - face->available_sizes[i].y_ppem) ==
563 qAbs(*ysize - face->available_sizes[best].y_ppem) &&
564 qAbs(*xsize - face->available_sizes[i].x_ppem) <
565 qAbs(*xsize - face->available_sizes[best].x_ppem))) {
566 best = i;
567 }
568 }
569 } else {
570 // Select the shortest bitmap strike whose height is larger than the desired height
571 for (int i = 1; i < face->num_fixed_sizes; i++) {
572 if (face->available_sizes[i].y_ppem < *ysize) {
573 if (face->available_sizes[i].y_ppem > face->available_sizes[best].y_ppem)
574 best = i;
575 } else if (face->available_sizes[best].y_ppem < *ysize) {
576 best = i;
577 } else if (face->available_sizes[i].y_ppem < face->available_sizes[best].y_ppem) {
578 best = i;
579 }
580 }
581 }
582
583 // According to freetype documentation we must use FT_Select_Size
584 // to make sure we can select the desired bitmap strike index
585 if (FT_Select_Size(face, best) == 0) {
586 const auto bestHeight = face->available_sizes[best].height;
587 if (isScalableBitmap() && bestHeight != 0)
588 *scalableBitmapScaleFactor = QFixed::fromReal((qreal)fontDef.pixelSize / bestHeight);
589 *xsize = face->available_sizes[best].x_ppem;
590 *ysize = face->available_sizes[best].y_ppem;
591 } else {
592 *xsize = *ysize = 0;
593 }
594 } else {
595#if defined FT_HAS_COLOR
596 if (FT_HAS_COLOR(face)) {
597 *outline_drawing = false;
598 } else
599#endif
600 {
601 int maxCachedGlyphSize = QFontEngine::maxCachedGlyphSize();
602 *outline_drawing = (*xsize > (maxCachedGlyphSize << 6) || *ysize > (maxCachedGlyphSize << 6));
603 }
604 }
605}
606
607QFontEngine::Properties QFreetypeFace::properties() const
608{
609 QFontEngine::Properties p;
610 p.postscriptName = FT_Get_Postscript_Name(face);
611 PS_FontInfoRec font_info;
612 if (FT_Get_PS_Font_Info(face, &font_info) == 0)
613 p.copyright = font_info.notice;
614 if (FT_IS_SCALABLE(face)
615#if defined(FT_HAS_COLOR)
616 && !FT_HAS_COLOR(face)
617#endif
618 ) {
619 p.ascent = face->ascender;
620 p.descent = -face->descender;
621 p.leading = face->height - face->ascender + face->descender;
622 p.emSquare = face->units_per_EM;
623 p.boundingBox = QRectF(face->bbox.xMin, -face->bbox.yMax,
624 face->bbox.xMax - face->bbox.xMin,
625 face->bbox.yMax - face->bbox.yMin);
626 } else {
627 p.ascent = QFixed::fromFixed(face->size->metrics.ascender);
628 p.descent = QFixed::fromFixed(-face->size->metrics.descender);
629 p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender);
630 p.emSquare = face->size->metrics.y_ppem;
631// p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.);
632 p.boundingBox = QRectF(0, -p.ascent.toReal(),
633 face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() );
634 }
635 p.italicAngle = 0;
636 p.capHeight = p.ascent;
637 p.lineWidth = face->underline_thickness;
638
639 return p;
640}
641
642bool QFreetypeFace::getSfntTable(uint tag, uchar *buffer, uint *length) const
643{
644 return ft_getSfntTable(face, tag, buffer, length);
645}
646
647/* Some fonts (such as MingLiu rely on hinting to scale different
648 components to their correct sizes. While this is really broken (it
649 should be done in the component glyph itself, not the hinter) we
650 will have to live with it.
651
652 This means we can not use FT_LOAD_NO_HINTING to get the glyph
653 outline. All we can do is to load the unscaled glyph and scale it
654 down manually when required.
655*/
656static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale)
657{
658 x_scale = FT_MulDiv(x_scale, 1 << 10, face->units_per_EM);
659 y_scale = FT_MulDiv(y_scale, 1 << 10, face->units_per_EM);
660 FT_Vector *p = g->outline.points;
661 const FT_Vector *e = p + g->outline.n_points;
662 while (p < e) {
663 p->x = FT_MulFix(p->x, x_scale);
664 p->y = FT_MulFix(p->y, y_scale);
665 ++p;
666 }
667}
668
669#define GLYPH2PATH_DEBUG QT_NO_QDEBUG_MACRO // qDebug
670void QFreetypeFace::addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale)
671{
672 const qreal factor = 1/64.;
673 scaleOutline(face, g, x_scale, y_scale);
674
675 QPointF cp = point.toPointF();
676
677 // convert the outline to a painter path
678 int i = 0;
679 for (int j = 0; j < g->outline.n_contours; ++j) {
680 int last_point = g->outline.contours[j];
681 GLYPH2PATH_DEBUG() << "contour:" << i << "to" << last_point;
682 QPointF start = QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
683 if (!(g->outline.tags[i] & 1)) { // start point is not on curve:
684 if (!(g->outline.tags[last_point] & 1)) { // end point is not on curve:
685 GLYPH2PATH_DEBUG() << " start and end point are not on curve";
686 start = (QPointF(g->outline.points[last_point].x*factor,
687 -g->outline.points[last_point].y*factor) + start) / 2.0;
688 } else {
689 GLYPH2PATH_DEBUG() << " end point is on curve, start is not";
690 start = QPointF(g->outline.points[last_point].x*factor,
691 -g->outline.points[last_point].y*factor);
692 }
693 --i; // to use original start point as control point below
694 }
695 start += cp;
696 GLYPH2PATH_DEBUG() << " start at" << start;
697
698 path->moveTo(start);
699 QPointF c[4];
700 c[0] = start;
701 int n = 1;
702 while (i < last_point) {
703 ++i;
704 c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
705 GLYPH2PATH_DEBUG() << " " << i << c[n] << "tag =" << (int)g->outline.tags[i]
706 << ": on curve =" << (bool)(g->outline.tags[i] & 1);
707 ++n;
708 switch (g->outline.tags[i] & 3) {
709 case 2:
710 // cubic bezier element
711 if (n < 4)
712 continue;
713 c[3] = (c[3] + c[2])/2;
714 --i;
715 break;
716 case 0:
717 // quadratic bezier element
718 if (n < 3)
719 continue;
720 c[3] = (c[1] + c[2])/2;
721 c[2] = (2*c[1] + c[3])/3;
722 c[1] = (2*c[1] + c[0])/3;
723 --i;
724 break;
725 case 1:
726 case 3:
727 if (n == 2) {
728 GLYPH2PATH_DEBUG() << " lineTo" << c[1];
729 path->lineTo(c[1]);
730 c[0] = c[1];
731 n = 1;
732 continue;
733 } else if (n == 3) {
734 c[3] = c[2];
735 c[2] = (2*c[1] + c[3])/3;
736 c[1] = (2*c[1] + c[0])/3;
737 }
738 break;
739 }
740 GLYPH2PATH_DEBUG() << " cubicTo" << c[1] << c[2] << c[3];
741 path->cubicTo(c[1], c[2], c[3]);
742 c[0] = c[3];
743 n = 1;
744 }
745
746 if (n == 1) {
747 GLYPH2PATH_DEBUG() << " closeSubpath";
748 path->closeSubpath();
749 } else {
750 c[3] = start;
751 if (n == 2) {
752 c[2] = (2*c[1] + c[3])/3;
753 c[1] = (2*c[1] + c[0])/3;
754 }
755 GLYPH2PATH_DEBUG() << " close cubicTo" << c[1] << c[2] << c[3];
756 path->cubicTo(c[1], c[2], c[3]);
757 }
758 ++i;
759 }
760}
761
762extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path);
763
764void QFreetypeFace::addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path)
765{
766 if (slot->format != FT_GLYPH_FORMAT_BITMAP
767 || slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO)
768 return;
769
770 QPointF cp = point.toPointF();
771 qt_addBitmapToPath(cp.x() + TRUNC(slot->metrics.horiBearingX), cp.y() - TRUNC(slot->metrics.horiBearingY),
772 slot->bitmap.buffer, slot->bitmap.pitch, slot->bitmap.width, slot->bitmap.rows, path);
773}
774
775static inline void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
776{
777 const int offs = bgr ? -1 : 1;
778 const int w = width * 3;
779 while (height--) {
780 uint *dd = dst;
781 for (int x = 0; x < w; x += 3) {
782 uchar red = src[x + 1 - offs];
783 uchar green = src[x + 1];
784 uchar blue = src[x + 1 + offs];
785 *dd++ = (0xFFU << 24) | (red << 16) | (green << 8) | blue;
786 }
787 dst += width;
788 src += src_pitch;
789 }
790}
791
792static inline void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
793{
794 const int offs = bgr ? -src_pitch : src_pitch;
795 while (height--) {
796 for (int x = 0; x < width; x++) {
797 uchar red = src[x + src_pitch - offs];
798 uchar green = src[x + src_pitch];
799 uchar blue = src[x + src_pitch + offs];
800 *dst++ = (0XFFU << 24) | (red << 16) | (green << 8) | blue;
801 }
802 src += 3*src_pitch;
803 }
804}
805
807{
808 static int type = -1;
809 if (type == -1) {
810 if (QScreen *screen = QGuiApplication::primaryScreen())
811 type = screen->handle()->subpixelAntialiasingTypeHint();
812 }
813 return static_cast<QFontEngine::SubpixelAntialiasingType>(type);
814}
815
816QFontEngineFT *QFontEngineFT::create(const QFontDef &fontDef, FaceId faceId, const QByteArray &fontData)
817{
818 auto engine = std::make_unique<QFontEngineFT>(fontDef);
819
820 QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_Mono;
821 const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
822
823 if (antialias) {
824 QFontEngine::SubpixelAntialiasingType subpixelType = subpixelAntialiasingTypeHint();
825 if (subpixelType == QFontEngine::Subpixel_None || (fontDef.styleStrategy & QFont::NoSubpixelAntialias)) {
826 format = QFontEngineFT::Format_A8;
827 engine->subpixelType = QFontEngine::Subpixel_None;
828 } else {
829 format = QFontEngineFT::Format_A32;
830 engine->subpixelType = subpixelType;
831 }
832 }
833
834 if (!engine->init(faceId, antialias, format, fontData) || engine->invalid()) {
835 qWarning("QFontEngineFT: Failed to create FreeType font engine");
836 return nullptr;
837 }
838
839 engine->setQtDefaultHintStyle(static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
840 return engine.release();
841}
842
843static FT_UShort calculateActualWeight(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId)
844{
845 FT_MM_Var *var = freetypeFace->mm_var;
846 if (var != nullptr && faceId.instanceIndex >= 0 && FT_UInt(faceId.instanceIndex) < var->num_namedstyles) {
847 for (FT_UInt axis = 0; axis < var->num_axis; ++axis) {
848 if (var->axis[axis].tag == QFont::Tag("wght").value()) {
849 return var->namedstyle[faceId.instanceIndex].coords[axis] >> 16;
850 }
851 }
852 }
853 if (const TT_OS2 *os2 = reinterpret_cast<const TT_OS2 *>(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) {
854 return os2->usWeightClass;
855 }
856
857 return 700;
858}
859
860namespace {
861 class QFontEngineFTRawData: public QFontEngineFT
862 {
863 public:
864 QFontEngineFTRawData(const QFontDef &fontDef) : QFontEngineFT(fontDef)
865 {
866 }
867
868 void updateFamilyNameAndStyle()
869 {
870 fontDef.families = QStringList(QString::fromLatin1(freetype->face->family_name));
871
872 if (freetype->face->style_flags & FT_STYLE_FLAG_ITALIC)
873 fontDef.style = QFont::StyleItalic;
874
875 if (freetype->face->style_flags & FT_STYLE_FLAG_BOLD)
876 fontDef.weight = QFont::Bold;
877 else
878 fontDef.weight = calculateActualWeight(freetype, freetype->face, faceId());
879 }
880
881 bool initFromData(const QByteArray &fontData,
882 const QMap<QFont::Tag, float> &variableAxisValues,
883 int instanceIndex)
884 {
885 FaceId faceId;
886 faceId.filename = "";
887 faceId.index = 0;
888 faceId.uuid = QUuid::createUuid().toByteArray();
889 faceId.variableAxes = variableAxisValues;
890 faceId.instanceIndex = instanceIndex;
891
892 return init(faceId, true, Format_None, fontData);
893 }
894 };
895}
896
897QFontEngineFT *QFontEngineFT::create(const QByteArray &fontData,
898 qreal pixelSize,
899 QFont::HintingPreference hintingPreference,
900 const QMap<QFont::Tag, float> &variableAxisValues,
901 int instanceIndex)
902{
903 QFontDef fontDef;
904 fontDef.pixelSize = pixelSize;
905 fontDef.stretch = QFont::Unstretched;
906 fontDef.hintingPreference = hintingPreference;
907 fontDef.variableAxisValues = variableAxisValues;
908
909 QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef);
910 if (!fe->initFromData(fontData, variableAxisValues, instanceIndex)) {
911 delete fe;
912 return nullptr;
913 }
914
915 fe->updateFamilyNameAndStyle();
916 fe->setQtDefaultHintStyle(static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
917
918 return fe;
919}
920
921QFontEngineFT::QFontEngineFT(const QFontDef &fd)
922 : QFontEngine(Freetype)
923{
924 fontDef = fd;
925 matrix.xx = 0x10000;
926 matrix.yy = 0x10000;
927 matrix.xy = 0;
928 matrix.yx = 0;
929 cache_cost = 100 * 1024;
930 kerning_pairs_loaded = false;
931 transform = false;
932 embolden = false;
933 obliquen = false;
934 antialias = true;
935 freetype = nullptr;
936 default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
937 default_hint_style = ftInitialDefaultHintStyle;
938 subpixelType = Subpixel_None;
939 lcdFilterType = (int)((quintptr) FT_LCD_FILTER_DEFAULT);
940 defaultFormat = Format_None;
941 embeddedbitmap = false;
942 const QByteArray env = qgetenv("QT_NO_FT_CACHE");
943 cacheEnabled = env.isEmpty() || env.toInt() == 0;
944 m_subPixelPositionCount = 4;
945 forceAutoHint = false;
946 stemDarkeningDriver = false;
947 verticalHintingOnly = false;
948}
949
950QFontEngineFT::~QFontEngineFT()
951{
952 if (freetype)
953 freetype->release(face_id);
954}
955
956bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
957 const QByteArray &fontData)
958{
959 return init(faceId, antialias, format, QFreetypeFace::getFace(faceId, fontData));
960}
961
962static void dont_delete(void*) {}
963
964static bool calculateActualItalic(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId)
965{
966 FT_MM_Var *var = freetypeFace->mm_var;
967 if (var != nullptr && faceId.instanceIndex >= 0 && FT_UInt(faceId.instanceIndex) < var->num_namedstyles) {
968 for (FT_UInt axis = 0; axis < var->num_axis; ++axis) {
969 if (var->axis[axis].tag == QFont::Tag("ital").value()) {
970 return (var->namedstyle[faceId.instanceIndex].coords[axis] >> 16) == 1;
971 }
972 }
973 }
974
975 return (face->style_flags & FT_STYLE_FLAG_ITALIC);
976}
977
978bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
979 QFreetypeFace *freetypeFace)
980{
981 freetype = freetypeFace;
982 if (!freetype) {
983 xsize = 0;
984 ysize = 0;
985 return false;
986 }
987 defaultFormat = format;
988 this->antialias = antialias;
989
990 if (!antialias)
991 glyphFormat = QFontEngine::Format_Mono;
992 else
993 glyphFormat = defaultFormat;
994
995 face_id = faceId;
996
997 symbol = freetype->symbol_map != nullptr;
998 PS_FontInfoRec psrec;
999 // don't assume that type1 fonts are symbol fonts by default
1000 if (FT_Get_PS_Font_Info(freetype->face, &psrec) == FT_Err_Ok) {
1001 symbol = !fontDef.families.isEmpty() && bool(fontDef.families.constFirst().contains("symbol"_L1, Qt::CaseInsensitive));
1002 }
1003
1004 freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing, &scalableBitmapScaleFactor);
1005
1006 FT_Face face = lockFace();
1007
1008 if (FT_IS_SCALABLE(face)
1009#if defined(FT_HAS_COLOR)
1010 && !FT_HAS_COLOR(face)
1011#endif
1012 ) {
1013 bool isItalic = calculateActualItalic(freetype, face, faceId);
1014 bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !isItalic && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_ITALIC");
1015 if (fake_oblique)
1016 obliquen = true;
1017 FT_Set_Transform(face, &matrix, nullptr);
1018 freetype->matrix = matrix;
1019 // fake bold
1020 if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD")) {
1021 FT_UShort actualWeight = calculateActualWeight(freetype, face, faceId);
1022 if (actualWeight < 700 &&
1023 (fontDef.pixelSize < 64 || qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD_LIMIT"))) {
1024 embolden = true;
1025 }
1026 }
1027 // underline metrics
1028 line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale));
1029 QFixed center_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale));
1030 underline_position = center_position - line_thickness / 2;
1031 } else {
1032 // ad hoc algorithm
1033 int score = fontDef.weight * fontDef.pixelSize;
1034 line_thickness = score / 7000;
1035 // looks better with thicker line for small pointsizes
1036 if (line_thickness < 2 && score >= 1050)
1037 line_thickness = 2;
1038 underline_position = ((line_thickness * 2) + 3) / 6;
1039
1040 cacheEnabled = false;
1041#if defined(FT_HAS_COLOR)
1042 if (FT_HAS_COLOR(face))
1043 glyphFormat = defaultFormat = GlyphFormat::Format_ARGB;
1044#endif
1045 }
1046 if (line_thickness < 1)
1047 line_thickness = 1;
1048
1049 metrics = face->size->metrics;
1050
1051 /*
1052 TrueType fonts with embedded bitmaps may have a bitmap font specific
1053 ascent/descent in the EBLC table. There is no direct public API
1054 to extract those values. The only way we've found is to trick freetype
1055 into thinking that it's not a scalable font in FT_Select_Size so that
1056 the metrics are retrieved from the bitmap strikes.
1057 */
1058 if (FT_IS_SCALABLE(face)) {
1059 for (int i = 0; i < face->num_fixed_sizes; ++i) {
1060 if (xsize == face->available_sizes[i].x_ppem && ysize == face->available_sizes[i].y_ppem) {
1061 face->face_flags &= ~FT_FACE_FLAG_SCALABLE;
1062
1063 FT_Select_Size(face, i);
1064 if (face->size->metrics.ascender + face->size->metrics.descender > 0) {
1065 FT_Pos leading = metrics.height - metrics.ascender + metrics.descender;
1066 metrics.ascender = face->size->metrics.ascender;
1067 metrics.descender = face->size->metrics.descender;
1068 if (metrics.descender > 0
1069 && QString::fromUtf8(face->family_name) == "Courier New"_L1) {
1070 metrics.descender *= -1;
1071 }
1072 metrics.height = metrics.ascender - metrics.descender + leading;
1073 }
1074 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
1075
1076 face->face_flags |= FT_FACE_FLAG_SCALABLE;
1077 break;
1078 }
1079 }
1080 }
1081#if defined(FT_FONT_FORMATS_H)
1082 const char *fmt = FT_Get_Font_Format(face);
1083 if (fmt && qstrncmp(fmt, "CFF", 4) == 0) {
1084 FT_Bool no_stem_darkening = true;
1085 FT_Error err = FT_Property_Get(qt_getFreetype(), "cff", "no-stem-darkening", &no_stem_darkening);
1086 if (err == FT_Err_Ok)
1087 stemDarkeningDriver = !no_stem_darkening;
1088 else
1089 stemDarkeningDriver = false;
1090 }
1091#endif
1092
1093 fontDef.styleName = QString::fromUtf8(face->style_name);
1094
1095 if (!freetype->hbFace) {
1096 faceData.user_data = face;
1097 faceData.get_font_table = ft_getSfntTable;
1098 (void)harfbuzzFace(); // populates face_
1099 freetype->hbFace = std::move(face_);
1100 } else {
1101 Q_ASSERT(!face_);
1102 }
1103 // we share the HB face in QFreeTypeFace, so do not let ~QFontEngine() destroy it
1104 face_ = Holder(freetype->hbFace.get(), dont_delete);
1105
1106 unlockFace();
1107
1108 fsType = freetype->fsType();
1109 return true;
1110}
1111
1112void QFontEngineFT::setQtDefaultHintStyle(QFont::HintingPreference hintingPreference)
1113{
1114 switch (hintingPreference) {
1115 case QFont::PreferNoHinting:
1116 setDefaultHintStyle(HintNone);
1117 break;
1118 case QFont::PreferFullHinting:
1119 setDefaultHintStyle(HintFull);
1120 break;
1121 case QFont::PreferVerticalHinting:
1122 setDefaultHintStyle(HintLight);
1123 break;
1124 case QFont::PreferDefaultHinting:
1125 setDefaultHintStyle(ftInitialDefaultHintStyle);
1126 break;
1127 }
1128}
1129
1130void QFontEngineFT::setDefaultHintStyle(HintStyle style)
1131{
1132 default_hint_style = style;
1133}
1134
1135// Since version 40 the TrueType interpreter moves points horizontally only for monochrome glyphs.
1136static bool hasTrueTypeInterpreterV40(FT_Library library)
1137{
1138#if defined(FT_FONT_FORMATS_H)
1139 FT_UInt version = 0;
1140 if (FT_Property_Get(library, "truetype", "interpreter-version", &version) != FT_Err_Ok)
1141 return true; // No TrueType driver, so no interpreter either
1142 return version == 40;
1143#else
1144 Q_UNUSED(library);
1145 return false;
1146#endif
1147}
1148
1149// Returns false if the hinting may still fit glyphs to the pixel grid horizontally.
1150bool QFontEngineFT::setVerticalHintingOnly()
1151{
1152 // Monochrome glyphs get fitted both ways by any hinter
1153 if (!antialias)
1154 return false;
1155 if ((default_hint_style == HintMedium || default_hint_style == HintFull)
1156 && !hasTrueTypeInterpreterV40(qt_getFreetype())) {
1157 return false;
1158 }
1159 verticalHintingOnly = true;
1160 return true;
1161}
1162
1163bool QFontEngineFT::expectsGammaCorrectedBlending(QFontEngine::GlyphFormat format) const
1164{
1165 Q_UNUSED(format);
1166 return stemDarkeningDriver;
1167}
1168
1169int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags,
1170 bool &hsubpixel, int &vfactor) const
1171{
1172 int load_flags = FT_LOAD_DEFAULT | default_load_flags;
1173 int load_target = default_hint_style == HintLight
1174 ? FT_LOAD_TARGET_LIGHT
1175 : verticalHintingOnly
1176 ? FT_LOAD_TARGET_LCD // Makes the auto-hinter work vertically only
1177 : FT_LOAD_TARGET_NORMAL;
1178
1179 if (format == Format_Mono) {
1180 load_target = FT_LOAD_TARGET_MONO;
1181 } else if (format == Format_A32) {
1182 if (subpixelType == Subpixel_RGB || subpixelType == Subpixel_BGR)
1183 hsubpixel = true;
1184 else if (subpixelType == Subpixel_VRGB || subpixelType == Subpixel_VBGR)
1185 vfactor = 3;
1186 } else if (format == Format_ARGB) {
1187#ifdef FT_LOAD_COLOR
1188 load_flags |= FT_LOAD_COLOR;
1189#endif
1190 }
1191
1192 if (set && set->outline_drawing)
1193 load_flags |= FT_LOAD_NO_BITMAP;
1194
1195 if (default_hint_style == HintNone || (flags & DesignMetrics) || (set && set->outline_drawing))
1196 load_flags |= FT_LOAD_NO_HINTING;
1197 else
1198 load_flags |= load_target;
1199
1200 if (forceAutoHint)
1201 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1202
1203 return load_flags;
1204}
1205
1206static inline bool areMetricsTooLarge(const QFontEngineFT::GlyphInfo &info)
1207{
1208 // false if exceeds QFontEngineFT::Glyph metrics
1209 return info.width > 0xFF || info.height > 0xFF || info.linearAdvance > 0x7FFF;
1210}
1211
1212static inline void transformBoundingBox(int *left, int *top, int *right, int *bottom, FT_Matrix *matrix)
1213{
1214 int l, r, t, b;
1215 FT_Vector vector;
1216 vector.x = *left;
1217 vector.y = *top;
1218 FT_Vector_Transform(&vector, matrix);
1219 l = r = vector.x;
1220 t = b = vector.y;
1221 vector.x = *right;
1222 vector.y = *top;
1223 FT_Vector_Transform(&vector, matrix);
1224 if (l > vector.x) l = vector.x;
1225 if (r < vector.x) r = vector.x;
1226 if (t < vector.y) t = vector.y;
1227 if (b > vector.y) b = vector.y;
1228 vector.x = *right;
1229 vector.y = *bottom;
1230 FT_Vector_Transform(&vector, matrix);
1231 if (l > vector.x) l = vector.x;
1232 if (r < vector.x) r = vector.x;
1233 if (t < vector.y) t = vector.y;
1234 if (b > vector.y) b = vector.y;
1235 vector.x = *left;
1236 vector.y = *bottom;
1237 FT_Vector_Transform(&vector, matrix);
1238 if (l > vector.x) l = vector.x;
1239 if (r < vector.x) r = vector.x;
1240 if (t < vector.y) t = vector.y;
1241 if (b > vector.y) b = vector.y;
1242 *left = l;
1243 *right = r;
1244 *top = t;
1245 *bottom = b;
1246}
1247
1248#if defined(QFONTENGINE_FT_SUPPORT_COLRV1)
1249#define FROM_FIXED_16_16(value) (value / 65536.0)
1250
1251static inline QTransform FTAffineToQTransform(const FT_Affine23 &matrix)
1252{
1253 qreal m11 = FROM_FIXED_16_16(matrix.xx);
1254 qreal m21 = -FROM_FIXED_16_16(matrix.xy);
1255 qreal m12 = -FROM_FIXED_16_16(matrix.yx);
1256 qreal m22 = FROM_FIXED_16_16(matrix.yy);
1257 qreal dx = FROM_FIXED_16_16(matrix.dx);
1258 qreal dy = -FROM_FIXED_16_16(matrix.dy);
1259
1260 return QTransform(m11, m12, m21, m22, dx, dy);
1261}
1262
1263bool QFontEngineFT::traverseColr1(FT_OpaquePaint opaquePaint,
1264 QSet<std::pair<FT_Byte *, FT_Bool> > *loops,
1265 QColor foregroundColor,
1266 FT_Color *palette,
1267 ushort paletteCount,
1268 QColrPaintGraphRenderer *paintGraphRenderer,
1269 int depth) const
1270{
1271 FT_Face face = freetype->face;
1272
1273 if (maxColrv1NestingDepth() > 0 && depth > maxColrv1NestingDepth()) {
1274 qCWarning(lcColrv1) << "COLRv1 graph nested deeper than" << maxColrv1NestingDepth()
1275 << "levels; aborting traversal";
1276 return false;
1277 }
1278
1279 auto key = std::pair{opaquePaint.p, opaquePaint.insert_root_transform};
1280 if (loops->contains(key)) {
1281 qCWarning(lcColrv1) << "Cycle detected in COLRv1 graph";
1282 return false;
1283 }
1284
1285 paintGraphRenderer->save();
1286 loops->insert(key);
1287 auto cleanup = qScopeGuard([&paintGraphRenderer, &key, &loops]() {
1288 loops->remove(key);
1289 paintGraphRenderer->restore();
1290 });
1291
1292 FT_COLR_Paint paint;
1293 if (!FT_Get_Paint(face, opaquePaint, &paint))
1294 return false;
1295
1296 if (paint.format == FT_COLR_PAINTFORMAT_COLR_LAYERS) {
1297 FT_OpaquePaint layerPaint;
1298 layerPaint.p = nullptr;
1299 while (FT_Get_Paint_Layers(face, &paint.u.colr_layers.layer_iterator, &layerPaint)) {
1300 if (!traverseColr1(layerPaint, loops, foregroundColor, palette, paletteCount, paintGraphRenderer, depth + 1))
1301 return false;
1302 }
1303 } else if (paint.format == FT_COLR_PAINTFORMAT_TRANSFORM
1304 || paint.format == FT_COLR_PAINTFORMAT_SCALE
1305 || paint.format == FT_COLR_PAINTFORMAT_TRANSLATE
1306 || paint.format == FT_COLR_PAINTFORMAT_ROTATE
1307 || paint.format == FT_COLR_PAINTFORMAT_SKEW) {
1308 QTransform xform;
1309
1310 FT_OpaquePaint nextPaint;
1311 switch (paint.format) {
1312 case FT_COLR_PAINTFORMAT_TRANSFORM:
1313 xform = FTAffineToQTransform(paint.u.transform.affine);
1314 nextPaint = paint.u.transform.paint;
1315 break;
1316 case FT_COLR_PAINTFORMAT_SCALE:
1317 {
1318 qreal centerX = FROM_FIXED_16_16(paint.u.scale.center_x);
1319 qreal centerY = -FROM_FIXED_16_16(paint.u.scale.center_y);
1320 qreal scaleX = FROM_FIXED_16_16(paint.u.scale.scale_x);
1321 qreal scaleY = FROM_FIXED_16_16(paint.u.scale.scale_y);
1322
1323 xform.translate(centerX, centerY);
1324 xform.scale(scaleX, scaleY);
1325 xform.translate(-centerX, -centerY);
1326
1327 nextPaint = paint.u.scale.paint;
1328 break;
1329 }
1330 case FT_COLR_PAINTFORMAT_ROTATE:
1331 {
1332 qreal centerX = FROM_FIXED_16_16(paint.u.rotate.center_x);
1333 qreal centerY = -FROM_FIXED_16_16(paint.u.rotate.center_y);
1334 qreal angle = -FROM_FIXED_16_16(paint.u.rotate.angle) * 180.0;
1335
1336 xform.translate(centerX, centerY);
1337 xform.rotate(angle);
1338 xform.translate(-centerX, -centerY);
1339
1340 nextPaint = paint.u.rotate.paint;
1341 break;
1342 }
1343
1344 case FT_COLR_PAINTFORMAT_SKEW:
1345 {
1346 qreal centerX = FROM_FIXED_16_16(paint.u.skew.center_x);
1347 qreal centerY = -FROM_FIXED_16_16(paint.u.skew.center_y);
1348 qreal angleX = FROM_FIXED_16_16(paint.u.skew.x_skew_angle) * M_PI;
1349 qreal angleY = -FROM_FIXED_16_16(paint.u.skew.y_skew_angle) * M_PI;
1350
1351 xform.translate(centerX, centerY);
1352 xform.shear(qTan(angleX), qTan(angleY));
1353 xform.translate(-centerX, -centerY);
1354
1355 nextPaint = paint.u.rotate.paint;
1356 break;
1357 }
1358 case FT_COLR_PAINTFORMAT_TRANSLATE:
1359 {
1360 qreal dx = FROM_FIXED_16_16(paint.u.translate.dx);
1361 qreal dy = -FROM_FIXED_16_16(paint.u.translate.dy);
1362
1363 xform.translate(dx, dy);
1364 nextPaint = paint.u.rotate.paint;
1365 break;
1366 }
1367 default:
1368 Q_UNREACHABLE();
1369 };
1370
1371 paintGraphRenderer->prependTransform(xform);
1372 if (!traverseColr1(nextPaint, loops, foregroundColor, palette, paletteCount, paintGraphRenderer, depth + 1))
1373 return false;
1374 } else if (paint.format == FT_COLR_PAINTFORMAT_LINEAR_GRADIENT
1375 || paint.format == FT_COLR_PAINTFORMAT_RADIAL_GRADIENT
1376 || paint.format == FT_COLR_PAINTFORMAT_SWEEP_GRADIENT
1377 || paint.format == FT_COLR_PAINTFORMAT_SOLID) {
1378 auto getPaletteColor = [&palette, &paletteCount, &foregroundColor](FT_UInt16 index,
1379 FT_F2Dot14 alpha) {
1380 QColor color;
1381 if (index < paletteCount) {
1382 const FT_Color &paletteColor = palette[index];
1383 color = qRgba(paletteColor.red,
1384 paletteColor.green,
1385 paletteColor.blue,
1386 paletteColor.alpha);
1387 } else if (index == 0xffff) {
1388 color = foregroundColor;
1389 }
1390
1391 if (color.isValid())
1392 color.setAlphaF(color.alphaF() * (alpha / 16384.0));
1393
1394 return color;
1395 };
1396
1397 auto gatherGradientStops = [&](FT_ColorStopIterator it) {
1398 QGradientStops ret;
1399 ret.resize(it.num_color_stops);
1400
1401 FT_ColorStop colorStop;
1402 while (FT_Get_Colorline_Stops(face, &colorStop, &it)) {
1403 uint index = it.current_color_stop - 1;
1404 if (qsizetype(index) < ret.size()) {
1405 QGradientStop &gradientStop = ret[index];
1406 gradientStop.first = FROM_FIXED_16_16(colorStop.stop_offset);
1407 gradientStop.second = getPaletteColor(colorStop.color.palette_index,
1408 colorStop.color.alpha);
1409 }
1410 }
1411
1412 return ret;
1413 };
1414
1415 auto extendToSpread = [](FT_PaintExtend extend) {
1416 switch (extend) {
1417 case FT_COLR_PAINT_EXTEND_REPEAT:
1418 return QGradient::RepeatSpread;
1419 case FT_COLR_PAINT_EXTEND_REFLECT:
1420 return QGradient::ReflectSpread;
1421 default:
1422 return QGradient::PadSpread;
1423 }
1424 };
1425
1426 if (paintGraphRenderer->isRendering()) {
1427 if (paint.format == FT_COLR_PAINTFORMAT_LINEAR_GRADIENT) {
1428 const qreal p0x = FROM_FIXED_16_16(paint.u.linear_gradient.p0.x);
1429 const qreal p0y = -FROM_FIXED_16_16(paint.u.linear_gradient.p0.y);
1430
1431 const qreal p1x = FROM_FIXED_16_16(paint.u.linear_gradient.p1.x);
1432 const qreal p1y = -FROM_FIXED_16_16(paint.u.linear_gradient.p1.y);
1433
1434 const qreal p2x = FROM_FIXED_16_16(paint.u.linear_gradient.p2.x);
1435 const qreal p2y = -FROM_FIXED_16_16(paint.u.linear_gradient.p2.y);
1436
1437 QPointF p0(p0x, p0y);
1438 QPointF p1(p1x, p1y);
1439 QPointF p2(p2x, p2y);
1440
1441 const QGradient::Spread spread =
1442 extendToSpread(paint.u.linear_gradient.colorline.extend);
1443 const QGradientStops stops =
1444 gatherGradientStops(paint.u.linear_gradient.colorline.color_stop_iterator);
1445 paintGraphRenderer->setLinearGradient(p0, p1, p2, spread, stops);
1446
1447 } else if (paint.format == FT_COLR_PAINTFORMAT_RADIAL_GRADIENT) {
1448 const qreal c0x = FROM_FIXED_16_16(paint.u.radial_gradient.c0.x);
1449 const qreal c0y = -FROM_FIXED_16_16(paint.u.radial_gradient.c0.y);
1450 const qreal r0 = FROM_FIXED_16_16(paint.u.radial_gradient.r0);
1451 const qreal c1x = FROM_FIXED_16_16(paint.u.radial_gradient.c1.x);
1452 const qreal c1y = -FROM_FIXED_16_16(paint.u.radial_gradient.c1.y);
1453 const qreal r1 = FROM_FIXED_16_16(paint.u.radial_gradient.r1);
1454
1455 const QPointF c0(c0x, c0y);
1456 const QPointF c1(c1x, c1y);
1457 const QGradient::Spread spread =
1458 extendToSpread(paint.u.radial_gradient.colorline.extend);
1459 const QGradientStops stops =
1460 gatherGradientStops(paint.u.radial_gradient.colorline.color_stop_iterator);
1461
1462 paintGraphRenderer->setRadialGradient(c0, r0, c1, r1, spread, stops);
1463 } else if (paint.format == FT_COLR_PAINTFORMAT_SWEEP_GRADIENT) {
1464 const qreal centerX = FROM_FIXED_16_16(paint.u.sweep_gradient.center.x);
1465 const qreal centerY = -FROM_FIXED_16_16(paint.u.sweep_gradient.center.y);
1466 const qreal startAngle = 180.0 * FROM_FIXED_16_16(paint.u.sweep_gradient.start_angle);
1467 const qreal endAngle = 180.0 * FROM_FIXED_16_16(paint.u.sweep_gradient.end_angle);
1468
1469 const QPointF center(centerX, centerY);
1470
1471 const QGradient::Spread spread = extendToSpread(paint.u.radial_gradient.colorline.extend);
1472 const QGradientStops stops = gatherGradientStops(paint.u.sweep_gradient.colorline.color_stop_iterator);
1473
1474 paintGraphRenderer->setConicalGradient(center, startAngle, endAngle, spread, stops);
1475
1476 } else if (paint.format == FT_COLR_PAINTFORMAT_SOLID) {
1477 QColor color = getPaletteColor(paint.u.solid.color.palette_index,
1478 paint.u.solid.color.alpha);
1479 if (!color.isValid()) {
1480 qCWarning(lcColrv1) << "Invalid palette index in COLRv1 graph:"
1481 << paint.u.solid.color.palette_index;
1482 return false;
1483 }
1484
1485 paintGraphRenderer->setSolidColor(color);
1486 }
1487 }
1488
1489 paintGraphRenderer->drawCurrentPath();
1490 } else if (paint.format == FT_COLR_PAINTFORMAT_COMPOSITE) {
1491 if (!paintGraphRenderer->isRendering()) {
1492 if (!traverseColr1(paint.u.composite.backdrop_paint,
1493 loops,
1494 foregroundColor,
1495 palette,
1496 paletteCount,
1497 paintGraphRenderer,
1498 depth + 1)) {
1499 return false;
1500 }
1501 if (!traverseColr1(paint.u.composite.source_paint,
1502 loops,
1503 foregroundColor,
1504 palette,
1505 paletteCount,
1506 paintGraphRenderer,
1507 depth + 1)) {
1508 return false;
1509 }
1510 } else {
1511 QPainter::CompositionMode compositionMode = QPainter::CompositionMode_SourceOver;
1512 switch (paint.u.composite.composite_mode) {
1513 case FT_COLR_COMPOSITE_CLEAR:
1514 compositionMode = QPainter::CompositionMode_Clear;
1515 break;
1516 case FT_COLR_COMPOSITE_SRC:
1517 compositionMode = QPainter::CompositionMode_Source;
1518 break;
1519 case FT_COLR_COMPOSITE_DEST:
1520 compositionMode = QPainter::CompositionMode_Destination;
1521 break;
1522 case FT_COLR_COMPOSITE_SRC_OVER:
1523 compositionMode = QPainter::CompositionMode_SourceOver;
1524 break;
1525 case FT_COLR_COMPOSITE_DEST_OVER:
1526 compositionMode = QPainter::CompositionMode_DestinationOver;
1527 break;
1528 case FT_COLR_COMPOSITE_SRC_IN:
1529 compositionMode = QPainter::CompositionMode_SourceIn;
1530 break;
1531 case FT_COLR_COMPOSITE_DEST_IN:
1532 compositionMode = QPainter::CompositionMode_DestinationIn;
1533 break;
1534 case FT_COLR_COMPOSITE_SRC_OUT:
1535 compositionMode = QPainter::CompositionMode_SourceOut;
1536 break;
1537 case FT_COLR_COMPOSITE_DEST_OUT:
1538 compositionMode = QPainter::CompositionMode_DestinationOut;
1539 break;
1540 case FT_COLR_COMPOSITE_SRC_ATOP:
1541 compositionMode = QPainter::CompositionMode_SourceAtop;
1542 break;
1543 case FT_COLR_COMPOSITE_DEST_ATOP:
1544 compositionMode = QPainter::CompositionMode_DestinationAtop;
1545 break;
1546 case FT_COLR_COMPOSITE_XOR:
1547 compositionMode = QPainter::CompositionMode_Xor;
1548 break;
1549 case FT_COLR_COMPOSITE_PLUS:
1550 compositionMode = QPainter::CompositionMode_Plus;
1551 break;
1552 case FT_COLR_COMPOSITE_SCREEN:
1553 compositionMode = QPainter::CompositionMode_Screen;
1554 break;
1555 case FT_COLR_COMPOSITE_OVERLAY:
1556 compositionMode = QPainter::CompositionMode_Overlay;
1557 break;
1558 case FT_COLR_COMPOSITE_DARKEN:
1559 compositionMode = QPainter::CompositionMode_Darken;
1560 break;
1561 case FT_COLR_COMPOSITE_LIGHTEN:
1562 compositionMode = QPainter::CompositionMode_Lighten;
1563 break;
1564 case FT_COLR_COMPOSITE_COLOR_DODGE:
1565 compositionMode = QPainter::CompositionMode_ColorDodge;
1566 break;
1567 case FT_COLR_COMPOSITE_COLOR_BURN:
1568 compositionMode = QPainter::CompositionMode_ColorBurn;
1569 break;
1570 case FT_COLR_COMPOSITE_HARD_LIGHT:
1571 compositionMode = QPainter::CompositionMode_HardLight;
1572 break;
1573 case FT_COLR_COMPOSITE_SOFT_LIGHT:
1574 compositionMode = QPainter::CompositionMode_SoftLight;
1575 break;
1576 case FT_COLR_COMPOSITE_DIFFERENCE:
1577 compositionMode = QPainter::CompositionMode_Difference;
1578 break;
1579 case FT_COLR_COMPOSITE_EXCLUSION:
1580 compositionMode = QPainter::CompositionMode_Exclusion;
1581 break;
1582 case FT_COLR_COMPOSITE_MULTIPLY:
1583 compositionMode = QPainter::CompositionMode_Multiply;
1584 break;
1585 default:
1586 qCWarning(lcColrv1) << "Unsupported COLRv1 composition mode" << paint.u.composite.composite_mode;
1587 break;
1588 };
1589
1590 QColrPaintGraphRenderer compositeRenderer;
1591 compositeRenderer.setBoundingRect(paintGraphRenderer->boundingRect());
1592 compositeRenderer.beginRender(fontDef.pixelSize / face->units_per_EM,
1593 paintGraphRenderer->currentTransform());
1594 if (!traverseColr1(paint.u.composite.backdrop_paint,
1595 loops,
1596 foregroundColor,
1597 palette,
1598 paletteCount,
1599 &compositeRenderer,
1600 depth + 1)) {
1601 return false;
1602 }
1603
1604 compositeRenderer.setCompositionMode(compositionMode);
1605 if (!traverseColr1(paint.u.composite.source_paint,
1606 loops,
1607 foregroundColor,
1608 palette,
1609 paletteCount,
1610 &compositeRenderer,
1611 depth + 1)) {
1612 return false;
1613 }
1614 paintGraphRenderer->drawImage(compositeRenderer.endRender());
1615 }
1616 } else if (paint.format == FT_COLR_PAINTFORMAT_GLYPH) {
1617 FT_Error error = FT_Load_Glyph(face,
1618 paint.u.glyph.glyphID,
1619 FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP | FT_LOAD_NO_SVG | FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_BITMAP_METRICS_ONLY);
1620 if (error) {
1621 qCWarning(lcColrv1) << "Failed to load glyph"
1622 << paint.u.glyph.glyphID
1623 << "in COLRv1 graph. Error: " << error;
1624 return false;
1625 }
1626
1627 QPainterPath path;
1628 QFreetypeFace::addGlyphToPath(face,
1629 face->glyph,
1630 QFixedPoint(0, 0),
1631 &path,
1632 face->units_per_EM << 6,
1633 face->units_per_EM << 6);
1634
1635 paintGraphRenderer->appendPath(path);
1636
1637 if (!traverseColr1(paint.u.glyph.paint, loops, foregroundColor, palette, paletteCount, paintGraphRenderer, depth + 1))
1638 return false;
1639 } else if (paint.format == FT_COLR_PAINTFORMAT_COLR_GLYPH) {
1640 FT_OpaquePaint otherOpaquePaint;
1641 otherOpaquePaint.p = nullptr;
1642 if (!FT_Get_Color_Glyph_Paint(face,
1643 paint.u.colr_glyph.glyphID,
1644 FT_COLOR_NO_ROOT_TRANSFORM,
1645 &otherOpaquePaint)) {
1646 qCWarning(lcColrv1) << "Failed to load color glyph"
1647 << paint.u.colr_glyph.glyphID
1648 << "in COLRv1 graph.";
1649 return false;
1650 }
1651
1652 if (!traverseColr1(otherOpaquePaint, loops, foregroundColor, palette, paletteCount, paintGraphRenderer, depth + 1))
1653 return false;
1654 }
1655
1656 return true;
1657}
1658
1659QFontEngineFT::Glyph *QFontEngineFT::loadColrv1Glyph(QGlyphSet *set,
1660 Glyph *g,
1661 uint glyph,
1662 const QColor &foregroundColor,
1663 bool fetchMetricsOnly) const
1664{
1665 FT_Face face = freetype->face;
1666
1667 GlyphInfo info;
1668 memset(&info, 0, sizeof(info));
1669
1670 // Load advance metrics for glyph. As documented, these should come from the base
1671 // glyph record.
1672 FT_Load_Glyph(face, glyph, FT_LOAD_DEFAULT
1673 | FT_LOAD_NO_BITMAP
1674 | FT_LOAD_NO_SVG
1675 | FT_LOAD_BITMAP_METRICS_ONLY);
1676 info.linearAdvance = int(face->glyph->linearHoriAdvance >> 10);
1677 info.xOff = short(TRUNC(ROUND(face->glyph->advance.x)));
1678
1679 FT_OpaquePaint opaquePaint;
1680 opaquePaint.p = nullptr;
1681 if (!FT_Get_Color_Glyph_Paint(face, glyph, FT_COLOR_INCLUDE_ROOT_TRANSFORM, &opaquePaint))
1682 return nullptr;
1683
1684 // The scene graph is in design coordinate system, so we need to also get glyphs in this
1685 // coordinate system. We then scale all painting to the requested pixel size
1686 FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0);
1687
1688 FT_Matrix matrix;
1689 FT_Vector delta;
1690 FT_Get_Transform(face, &matrix, &delta);
1691 QTransform originalXform(FROM_FIXED_16_16(matrix.xx), -FROM_FIXED_16_16(matrix.yx),
1692 -FROM_FIXED_16_16(matrix.xy), FROM_FIXED_16_16(matrix.yy),
1693 FROM_FIXED_16_16(delta.x), FROM_FIXED_16_16(delta.y));
1694
1695
1696 // Also clear transform to ensure we operate in design metrics
1697 FT_Set_Transform(face, nullptr, nullptr);
1698
1699 auto cleanup = qScopeGuard([&]() {
1700 // Reset stuff we changed
1701 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
1702 FT_Set_Transform(face, &matrix, &delta);
1703 });
1704
1705 qCDebug(lcColrv1).noquote() << "================== Start collecting COLRv1 metrics for" << glyph;
1706 QRect designCoordinateBounds;
1707
1708 // Getting metrics is done multiple times per glyph while entering it into the cache.
1709 // Since this may need to be calculated, we cache the last one for sequential calls.
1710 if (colrv1_bounds_cache_id == glyph) {
1711 designCoordinateBounds = colrv1_bounds_cache;
1712 } else {
1713 // COLRv1 fonts can optionally have a clip box for quicker retrieval of metrics. We try
1714 // to get this, and if there is none, we calculate the bounds by traversing the graph.
1715 FT_ClipBox clipBox;
1716 if (FT_Get_Color_Glyph_ClipBox(face, glyph, &clipBox)) {
1717 FT_Pos left = qMin(clipBox.bottom_left.x, qMin(clipBox.bottom_right.x, qMin(clipBox.top_left.x, clipBox.top_right.x)));
1718 FT_Pos right = qMax(clipBox.bottom_left.x, qMax(clipBox.bottom_right.x, qMax(clipBox.top_left.x, clipBox.top_right.x)));
1719
1720 FT_Pos top = qMin(-clipBox.bottom_left.y, qMin(-clipBox.bottom_right.y, qMin(-clipBox.top_left.y, -clipBox.top_right.y)));
1721 FT_Pos bottom = qMax(-clipBox.bottom_left.y, qMax(-clipBox.bottom_right.y, qMax(-clipBox.top_left.y, -clipBox.top_right.y)));
1722
1723 qreal scale = 1.0 / 64.0;
1724 designCoordinateBounds = QRect(QPoint(qFloor(left * scale), qFloor(top * scale)),
1725 QPoint(qCeil(right * scale), qCeil(bottom * scale)));
1726 } else {
1727 // Do a pass over the graph to find the bounds
1728 QColrPaintGraphRenderer boundingRectCalculator;
1729 boundingRectCalculator.beginCalculateBoundingBox();
1730 QSet<std::pair<FT_Byte *, FT_Bool> > loops;
1731 if (traverseColr1(opaquePaint,
1732 &loops,
1733 QColor{},
1734 nullptr,
1735 0,
1736 &boundingRectCalculator)) {
1737 designCoordinateBounds = boundingRectCalculator.boundingRect().toAlignedRect();
1738 }
1739 }
1740
1741 colrv1_bounds_cache_id = glyph;
1742 colrv1_bounds_cache = designCoordinateBounds;
1743 }
1744
1745 QTransform initialTransform;
1746 initialTransform.scale(fontDef.pixelSize / face->units_per_EM,
1747 fontDef.pixelSize / face->units_per_EM);
1748 QRect bounds = initialTransform.mapRect(designCoordinateBounds);
1749 bounds = originalXform.mapRect(bounds);
1750
1751 info.x = bounds.left();
1752 info.y = -bounds.top();
1753 info.width = bounds.width();
1754 info.height = bounds.height();
1755
1756 qCDebug(lcColrv1) << "Bounds of" << glyph << "==" << bounds;
1757
1758 // If requested, we now render the scene graph into an image using QPainter
1759 QImage destinationImage;
1760 if (!fetchMetricsOnly && !bounds.size().isEmpty()) {
1761 if (glyphBoundingBoxExceedsLimit(bounds.width(),
1762 bounds.height(),
1763 effectivePixelSizeSquared(originalXform))) {
1764 return nullptr;
1765 }
1766
1767 FT_Palette_Data paletteData;
1768 if (FT_Palette_Data_Get(face, &paletteData))
1769 return nullptr;
1770
1771 FT_Color *palette = nullptr;
1772 FT_Error error = FT_Palette_Select(face, 0, &palette);
1773 if (error) {
1774 qWarning("selecting palette for COLRv1 failed, err=%x face=%p, glyph=%d",
1775 error,
1776 face,
1777 glyph);
1778 }
1779
1780 if (palette == nullptr)
1781 return nullptr;
1782
1783 ushort paletteCount = paletteData.num_palette_entries;
1784
1785 QColrPaintGraphRenderer paintGraphRenderer;
1786 paintGraphRenderer.setBoundingRect(bounds);
1787 paintGraphRenderer.beginRender(fontDef.pixelSize / face->units_per_EM,
1788 originalXform);
1789
1790 // Render
1791 QSet<std::pair<FT_Byte *, FT_Bool> > loops;
1792 if (!traverseColr1(opaquePaint,
1793 &loops,
1794 foregroundColor,
1795 palette,
1796 paletteCount,
1797 &paintGraphRenderer)) {
1798 return nullptr;
1799 }
1800
1801 destinationImage = paintGraphRenderer.endRender();
1802 }
1803
1804 if (fetchMetricsOnly || !destinationImage.isNull()) {
1805 if (g == nullptr) {
1806 g = new Glyph;
1807 g->data = nullptr;
1808 if (set != nullptr)
1809 set->setGlyph(glyph, QFixedPoint{}, g);
1810 }
1811
1812 g->linearAdvance = info.linearAdvance;
1813 g->width = info.width;
1814 g->height = info.height;
1815 g->x = info.x;
1816 g->y = info.y;
1817 g->advance = info.xOff;
1818 g->format = Format_ARGB;
1819
1820 if (!fetchMetricsOnly && !destinationImage.isNull()) {
1821 g->data = new uchar[info.height * info.width * 4];
1822 memcpy(g->data, destinationImage.constBits(), info.height * info.width * 4);
1823 }
1824
1825 return g;
1826 }
1827
1828 return nullptr;
1829}
1830#endif // QFONTENGINE_FT_SUPPORT_COLRV1
1831
1832// The slant of FT_GlyphSlot_Oblique(), this has to be kept in sync with FreeType
1833static const FT_Fixed obliqueSlant = 0x0366A;
1834
1835QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
1836 const QFixedPoint &subPixelPosition,
1837 QColor color,
1838 GlyphFormat format,
1839 bool fetchMetricsOnly,
1840 bool disableOutlineDrawing) const
1841{
1842// Q_ASSERT(freetype->lock == 1);
1843
1844 if (format == Format_None)
1845 format = defaultFormat != Format_None ? defaultFormat : Format_Mono;
1846 Q_ASSERT(format != Format_None);
1847
1848 Glyph *g = set ? set->getGlyph(glyph, subPixelPosition) : nullptr;
1849 if (g && g->format == format && (fetchMetricsOnly || g->data))
1850 return g;
1851
1852 if (!g && set && set->isGlyphMissing(glyph))
1853 return &emptyGlyph;
1854
1855
1856 FT_Face face = freetype->face;
1857
1858 FT_Matrix matrix = freetype->matrix;
1859 bool transform = matrix.xx != 0x10000
1860 || matrix.yy != 0x10000
1861 || matrix.xy != 0
1862 || matrix.yx != 0;
1863 if (obliquen && transform) {
1864 // We have to apply the obliquen transformation before any
1865 // other transforms. This means we need to duplicate Freetype's
1866 // obliquen matrix here and this has to be kept in sync.
1867 FT_Matrix slant;
1868 slant.xx = 0x10000L;
1869 slant.yx = 0;
1870 slant.xy = obliqueSlant;
1871 slant.yy = 0x10000L;
1872
1873 FT_Matrix_Multiply(&matrix, &slant);
1874 matrix = slant;
1875 }
1876
1877 FT_Vector v;
1878 v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.x.value());
1879 v.y = format == Format_Mono ? 0 : FT_Pos(-subPixelPosition.y.value());
1880 FT_Set_Transform(face, &matrix, &v);
1881
1882 bool hsubpixel = false;
1883 int vfactor = 1;
1884 int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor);
1885
1886 if (transform || obliquen || (format != Format_Mono && !isScalableBitmap()))
1887 load_flags |= FT_LOAD_NO_BITMAP;
1888
1889#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20701
1890 // Only the advance/metrics are needed here, not the rendered bitmap
1891 if (fetchMetricsOnly)
1892 load_flags |= FT_LOAD_BITMAP_METRICS_ONLY;
1893#endif
1894
1895#if defined(QFONTENGINE_FT_SUPPORT_COLRV1)
1896 if (FT_IS_SCALABLE(freetype->face)
1897 && FT_HAS_COLOR(freetype->face)
1898 && (load_flags & FT_LOAD_COLOR)) {
1899 // Try loading COLRv1 glyph if possible.
1900 Glyph *ret = loadColrv1Glyph(set, g, glyph, color, fetchMetricsOnly);
1901 if (ret != nullptr)
1902 return ret;
1903 }
1904#else
1905 Q_UNUSED(color);
1906#endif
1907
1908 FT_Error err = FT_Load_Glyph(face, glyph, load_flags);
1909 if (err && (load_flags & FT_LOAD_NO_BITMAP)) {
1910 load_flags &= ~FT_LOAD_NO_BITMAP;
1911 err = FT_Load_Glyph(face, glyph, load_flags);
1912 }
1913 if (err == FT_Err_Too_Few_Arguments) {
1914 // this is an error in the bytecode interpreter, just try to run without it
1915 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1916 err = FT_Load_Glyph(face, glyph, load_flags);
1917 } else if (err == FT_Err_Execution_Too_Long) {
1918 // This is an error in the bytecode, probably a web font made by someone who
1919 // didn't test bytecode hinting at all so disable for it for all glyphs.
1920 qWarning("load glyph failed due to broken hinting bytecode in font, switching to auto hinting");
1921 default_load_flags |= FT_LOAD_FORCE_AUTOHINT;
1922 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1923 err = FT_Load_Glyph(face, glyph, load_flags);
1924 }
1925 if (err != FT_Err_Ok) {
1926 qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
1927 if (set)
1928 set->setGlyphMissing(glyph);
1929 return &emptyGlyph;
1930 }
1931
1932 FT_GlyphSlot slot = face->glyph;
1933
1934 if (embolden)
1935 FT_GlyphSlot_Embolden(slot);
1936 if (obliquen && !transform) {
1937 FT_GlyphSlot_Oblique(slot);
1938
1939 // While Embolden alters the metrics of the slot, oblique does not, so we need
1940 // to fix this ourselves.
1941 transform = true;
1942 FT_Matrix m;
1943 m.xx = 0x10000;
1944 m.yx = 0x0;
1945 m.xy = 0x6000;
1946 m.yy = 0x10000;
1947
1948 FT_Matrix_Multiply(&m, &matrix);
1949 }
1950
1951 GlyphInfo info;
1952 info.linearAdvance = slot->linearHoriAdvance >> 10;
1953 info.xOff = TRUNC(ROUND(slot->advance.x));
1954 info.yOff = 0;
1955
1956 if ((set && set->outline_drawing && !disableOutlineDrawing) || fetchMetricsOnly) {
1957 int left = slot->metrics.horiBearingX;
1958 int right = slot->metrics.horiBearingX + slot->metrics.width;
1959 int top = slot->metrics.horiBearingY;
1960 int bottom = slot->metrics.horiBearingY - slot->metrics.height;
1961
1962 if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP)
1963 transformBoundingBox(&left, &top, &right, &bottom, &matrix);
1964
1965 left = FLOOR(left);
1966 right = CEIL(right);
1967 bottom = FLOOR(bottom);
1968 top = CEIL(top);
1969
1970 info.x = TRUNC(left);
1971 info.y = TRUNC(top);
1972 info.width = TRUNC(right - left);
1973 info.height = TRUNC(top - bottom);
1974
1975 // If any of the metrics are too large to fit, don't cache them
1976 // Also, avoid integer overflow when linearAdvance is to large to fit in a signed short
1977 if (areMetricsTooLarge(info))
1978 return nullptr;
1979
1980 g = new Glyph;
1981 g->data = nullptr;
1982 g->linearAdvance = info.linearAdvance;
1983 g->width = info.width;
1984 g->height = info.height;
1985 g->x = info.x;
1986 g->y = info.y;
1987 g->advance = info.xOff;
1988 g->format = format;
1989
1990 if (set)
1991 set->setGlyph(glyph, subPixelPosition, g);
1992
1993 return g;
1994 }
1995
1996 if (slot->format == FT_GLYPH_FORMAT_OUTLINE) {
1997 const QTransform xform(matrix.xx / 65536.0, matrix.xy / 65536.0,
1998 matrix.yx / 65536.0, matrix.yy / 65536.0, 0, 0);
1999 FT_BBox cbox;
2000 FT_Outline_Get_CBox(&slot->outline, &cbox);
2001 const qreal glyphWidth = qreal((cbox.xMax - cbox.xMin) >> 6);
2002 const qreal glyphHeight = qreal((cbox.yMax - cbox.yMin) >> 6);
2003 if (glyphBoundingBoxExceedsLimit(glyphWidth, glyphHeight, effectivePixelSizeSquared(xform))) {
2004 if (set)
2005 set->setGlyphMissing(glyph);
2006 return &emptyGlyph;
2007 }
2008 }
2009
2010 int glyph_buffer_size = 0;
2011 std::unique_ptr<uchar[]> glyph_buffer;
2012 FT_Render_Mode renderMode = (default_hint_style == HintLight) ? FT_RENDER_MODE_LIGHT : FT_RENDER_MODE_NORMAL;
2013 switch (format) {
2014 case Format_Mono:
2015 renderMode = FT_RENDER_MODE_MONO;
2016 break;
2017 case Format_A32:
2018 if (!hsubpixel && vfactor == 1) {
2019 qWarning("Format_A32 requested, but subpixel layout is unknown.");
2020 return nullptr;
2021 }
2022
2023 renderMode = hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V;
2024 break;
2025 case Format_A8:
2026 case Format_ARGB:
2027 break;
2028 default:
2029 Q_UNREACHABLE();
2030 }
2031 FT_Library_SetLcdFilter(slot->library, (FT_LcdFilter)lcdFilterType);
2032
2033 err = FT_Render_Glyph(slot, renderMode);
2034 FT_Library_SetLcdFilter(slot->library, FT_LCD_FILTER_NONE);
2035
2036 if (err != FT_Err_Ok) {
2037 qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
2038 return nullptr;
2039 }
2040
2041 info.height = slot->bitmap.rows;
2042 info.width = slot->bitmap.width;
2043 info.x = slot->bitmap_left;
2044 info.y = slot->bitmap_top;
2045 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD)
2046 info.width = info.width / 3;
2047 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V)
2048 info.height = info.height / vfactor;
2049
2050 int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 :
2051 (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4));
2052
2053 glyph_buffer_size = info.height * pitch;
2054 glyph_buffer.reset(new uchar[glyph_buffer_size]);
2055
2056 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
2057 uchar *src = slot->bitmap.buffer;
2058 uchar *dst = glyph_buffer.get();
2059 int h = slot->bitmap.rows;
2060 // Some fonts return bitmaps even when we requested something else:
2061 if (format == Format_Mono) {
2062 int bytes = ((info.width + 7) & ~7) >> 3;
2063 while (h--) {
2064 memcpy (dst, src, bytes);
2065 dst += pitch;
2066 src += slot->bitmap.pitch;
2067 }
2068 } else if (format == Format_A8) {
2069 while (h--) {
2070 for (int x = 0; x < int{info.width}; x++)
2071 dst[x] = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00);
2072 dst += pitch;
2073 src += slot->bitmap.pitch;
2074 }
2075 } else {
2076 while (h--) {
2077 uint *dd = reinterpret_cast<uint *>(dst);
2078 for (int x = 0; x < int{info.width}; x++)
2079 dd[x] = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffffff : 0x00000000);
2080 dst += pitch;
2081 src += slot->bitmap.pitch;
2082 }
2083 }
2084 } else if (slot->bitmap.pixel_mode == 7 /*FT_PIXEL_MODE_BGRA*/) {
2085 Q_ASSERT(format == Format_ARGB);
2086 uchar *src = slot->bitmap.buffer;
2087 uchar *dst = glyph_buffer.get();
2088 int h = slot->bitmap.rows;
2089 while (h--) {
2090#if Q_BYTE_ORDER == Q_BIG_ENDIAN
2091 const quint32 *srcPixel = (const quint32 *)src;
2092 quint32 *dstPixel = (quint32 *)dst;
2093 for (int x = 0; x < static_cast<int>(slot->bitmap.width); x++, srcPixel++, dstPixel++) {
2094 const quint32 pixel = *srcPixel;
2095 *dstPixel = qbswap(pixel);
2096 }
2097#else
2098 memcpy(dst, src, slot->bitmap.width * 4);
2099#endif
2100 dst += slot->bitmap.pitch;
2101 src += slot->bitmap.pitch;
2102 }
2103 info.linearAdvance = info.xOff = slot->bitmap.width;
2104 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
2105 if (format == Format_A8) {
2106 uchar *src = slot->bitmap.buffer;
2107 uchar *dst = glyph_buffer.get();
2108 int h = slot->bitmap.rows;
2109 int bytes = info.width;
2110 while (h--) {
2111 memcpy (dst, src, bytes);
2112 dst += pitch;
2113 src += slot->bitmap.pitch;
2114 }
2115 } else if (format == Format_ARGB) {
2116 uchar *src = slot->bitmap.buffer;
2117 quint32 *dstPixel = reinterpret_cast<quint32 *>(glyph_buffer.get());
2118 int h = slot->bitmap.rows;
2119 while (h--) {
2120 for (int x = 0; x < static_cast<int>(slot->bitmap.width); ++x) {
2121 uchar alpha = src[x];
2122 float alphaF = alpha / 255.0;
2123 dstPixel[x] = qRgba(qRound(alphaF * color.red()),
2124 qRound(alphaF * color.green()),
2125 qRound(alphaF * color.blue()),
2126 alpha);
2127 }
2128 src += slot->bitmap.pitch;
2129 dstPixel += info.width;
2130 }
2131 }
2132 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
2133 Q_ASSERT(format == Format_A32);
2134 convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB);
2135 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) {
2136 Q_ASSERT(format == Format_A32);
2137 convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB);
2138 } else {
2139 qWarning("QFontEngine: Glyph rendered in unknown pixel_mode=%d", slot->bitmap.pixel_mode);
2140 return nullptr;
2141 }
2142
2143 if (!g) {
2144 g = new Glyph;
2145 g->data = nullptr;
2146 }
2147
2148 g->linearAdvance = info.linearAdvance;
2149 g->width = info.width;
2150 g->height = info.height;
2151 g->x = info.x;
2152 g->y = info.y;
2153 g->advance = info.xOff;
2154 g->format = format;
2155 delete [] g->data;
2156 g->data = glyph_buffer.release();
2157
2158 if (set)
2159 set->setGlyph(glyph, subPixelPosition, g);
2160
2161 return g;
2162}
2163
2164QFontEngine::FaceId QFontEngineFT::faceId() const
2165{
2166 return face_id;
2167}
2168
2169QFontEngine::Properties QFontEngineFT::properties() const
2170{
2171 Properties p = freetype->properties();
2172 if (p.postscriptName.isEmpty()) {
2173 p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family().toUtf8());
2174 }
2175
2176 return p;
2177}
2178
2179QFixed QFontEngineFT::emSquareSize() const
2180{
2181 if (FT_IS_SCALABLE(freetype->face))
2182 return freetype->face->units_per_EM;
2183 else
2184 return freetype->face->size->metrics.y_ppem;
2185}
2186
2187bool QFontEngineFT::getSfntTableData(uint tag, uchar *buffer, uint *length) const
2188{
2189 return ft_getSfntTable(freetype->face, tag, buffer, length);
2190}
2191
2192int QFontEngineFT::synthesized() const
2193{
2194 int s = 0;
2195 if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC))
2196 s = SynthesizedItalic;
2197 if ((fontDef.weight >= QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD))
2198 s |= SynthesizedBold;
2199 if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face))
2200 s |= SynthesizedStretch;
2201 return s;
2202}
2203
2204void QFontEngineFT::initializeHeightMetrics() const
2205{
2206 m_ascent = QFixed::fromFixed(metrics.ascender);
2207 m_descent = QFixed::fromFixed(-metrics.descender);
2208 m_leading = QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender);
2209
2210 QFontEngine::initializeHeightMetrics();
2211
2212 if (scalableBitmapScaleFactor != 1) {
2213 m_ascent *= scalableBitmapScaleFactor;
2214 m_descent *= scalableBitmapScaleFactor;
2215 m_leading *= scalableBitmapScaleFactor;
2216 }
2217}
2218
2219QFixed QFontEngineFT::capHeight() const
2220{
2221 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
2222 if (os2 && os2->version >= 2) {
2223 lockFace();
2224 QFixed answer = QFixed::fromFixed(FT_MulFix(os2->sCapHeight, freetype->face->size->metrics.y_scale));
2225 unlockFace();
2226 return answer;
2227 }
2228 return calculatedCapHeight();
2229}
2230
2231QFixed QFontEngineFT::xHeight() const
2232{
2233 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
2234 if (os2 && os2->sxHeight) {
2235 lockFace();
2236 QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize();
2237 unlockFace();
2238 return answer;
2239 }
2240
2241 return QFontEngine::xHeight();
2242}
2243
2244QFixed QFontEngineFT::averageCharWidth() const
2245{
2246 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
2247 if (os2 && os2->xAvgCharWidth) {
2248 lockFace();
2249 QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize();
2250 unlockFace();
2251 return answer;
2252 }
2253
2254 return QFontEngine::averageCharWidth();
2255}
2256
2257qreal QFontEngineFT::maxCharWidth() const
2258{
2259 QFixed max_advance = QFixed::fromFixed(metrics.max_advance);
2260 if (scalableBitmapScaleFactor != 1)
2261 max_advance *= scalableBitmapScaleFactor;
2262 return max_advance.toReal();
2263}
2264
2265QFixed QFontEngineFT::lineThickness() const
2266{
2267 return line_thickness;
2268}
2269
2270QFixed QFontEngineFT::underlinePosition() const
2271{
2272 return underline_position;
2273}
2274
2275void QFontEngineFT::doKerning(QGlyphLayout *g, QFontEngine::ShaperFlags flags) const
2276{
2277 if (!kerning_pairs_loaded) {
2278 kerning_pairs_loaded = true;
2279 lockFace();
2280 if (freetype->face->size->metrics.x_ppem != 0) {
2281 QFixed scalingFactor = emSquareSize() / QFixed(freetype->face->size->metrics.x_ppem);
2282 unlockFace();
2283 const_cast<QFontEngineFT *>(this)->loadKerningPairs(scalingFactor);
2284 } else {
2285 unlockFace();
2286 }
2287 }
2288
2289 if (shouldUseDesignMetrics(flags))
2290 flags |= DesignMetrics;
2291 else
2292 flags &= ~DesignMetrics;
2293
2294 QFontEngine::doKerning(g, flags);
2295}
2296
2297static inline FT_Matrix QTransformToFTMatrix(const QTransform &matrix)
2298{
2299 FT_Matrix m;
2300
2301 m.xx = FT_Fixed(matrix.m11() * 65536);
2302 m.xy = FT_Fixed(-matrix.m21() * 65536);
2303 m.yx = FT_Fixed(-matrix.m12() * 65536);
2304 m.yy = FT_Fixed(matrix.m22() * 65536);
2305
2306 return m;
2307}
2308
2309QFontEngineFT::QGlyphSet *QFontEngineFT::TransformedGlyphSets::findSet(const QTransform &matrix, const QFontDef &fontDef)
2310{
2311 FT_Matrix m = QTransformToFTMatrix(matrix);
2312
2313 int i = 0;
2314 for (; i < nSets; ++i) {
2315 QGlyphSet *g = sets[i];
2316 if (!g)
2317 break;
2318 if (g->transformationMatrix.xx == m.xx
2319 && g->transformationMatrix.xy == m.xy
2320 && g->transformationMatrix.yx == m.yx
2321 && g->transformationMatrix.yy == m.yy) {
2322
2323 // found a match, move it to the front
2324 moveToFront(i);
2325 return g;
2326 }
2327 }
2328
2329 // don't cache more than nSets transformations
2330 if (i == nSets)
2331 // reuse the last set
2332 --i;
2333 moveToFront(nSets - 1);
2334 if (!sets[0])
2335 sets[0] = new QGlyphSet;
2336 QGlyphSet *gs = sets[0];
2337 Q_ASSERT(gs != nullptr);
2338
2339 gs->clear();
2340 gs->transformationMatrix = m;
2341 const int maxCachedSize = maxCachedGlyphSize();
2342 gs->outline_drawing = fontDef.pixelSize * fontDef.pixelSize * qAbs(matrix.determinant()) > maxCachedSize * maxCachedSize;
2343
2344 return gs;
2345}
2346
2347void QFontEngineFT::TransformedGlyphSets::moveToFront(int i)
2348{
2349 QGlyphSet *g = sets[i];
2350 while (i > 0) {
2351 sets[i] = sets[i - 1];
2352 --i;
2353 }
2354 sets[0] = g;
2355}
2356
2357
2358QFontEngineFT::QGlyphSet *QFontEngineFT::loadGlyphSet(const QTransform &matrix)
2359{
2360 if (matrix.type() > QTransform::TxShear || !cacheEnabled)
2361 return nullptr;
2362
2363 // FT_Set_Transform only supports scalable fonts
2364 if (!FT_IS_SCALABLE(freetype->face))
2365 return matrix.type() <= QTransform::TxTranslate ? &defaultGlyphSet : nullptr;
2366
2367 return transformedGlyphSets.findSet(matrix, fontDef);
2368}
2369
2370void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
2371{
2372 FT_Face face = lockFace(Unscaled);
2373 FT_Set_Transform(face, nullptr, nullptr);
2374 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
2375
2376 int left = face->glyph->metrics.horiBearingX;
2377 int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width;
2378 int top = face->glyph->metrics.horiBearingY;
2379 int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
2380
2381 QFixedPoint p;
2382 p.x = 0;
2383 p.y = 0;
2384
2385 metrics->width = QFixed::fromFixed(right-left);
2386 metrics->height = QFixed::fromFixed(top-bottom);
2387 metrics->x = QFixed::fromFixed(left);
2388 metrics->y = QFixed::fromFixed(-top);
2389 metrics->xoff = QFixed::fromFixed(face->glyph->advance.x);
2390
2391 if (!FT_IS_SCALABLE(freetype->face))
2392 QFreetypeFace::addBitmapToPath(face->glyph, p, path);
2393 else
2394 QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6);
2395
2396 FT_Set_Transform(face, &freetype->matrix, nullptr);
2397 unlockFace();
2398}
2399
2400bool QFontEngineFT::supportsTransformation(const QTransform &transform) const
2401{
2402 return transform.type() <= QTransform::TxRotate;
2403}
2404
2405void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
2406{
2407 if (!glyphs.numGlyphs)
2408 return;
2409
2410 if (FT_IS_SCALABLE(freetype->face)) {
2411 QFontEngine::addOutlineToPath(x, y, glyphs, path, flags);
2412 } else {
2413 QVarLengthArray<QFixedPoint> positions;
2414 QVarLengthArray<glyph_t> positioned_glyphs;
2415 QTransform matrix;
2416 matrix.translate(x, y);
2417 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
2418
2419 FT_Face face = lockFace(Unscaled);
2420 for (int gl = 0; gl < glyphs.numGlyphs; gl++) {
2421 FT_UInt glyph = positioned_glyphs[gl];
2422 FT_Load_Glyph(face, glyph, FT_LOAD_TARGET_MONO);
2423 QFreetypeFace::addBitmapToPath(face->glyph, positions[gl], path);
2424 }
2425 unlockFace();
2426 }
2427}
2428
2429void QFontEngineFT::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs,
2430 QPainterPath *path, QTextItem::RenderFlags)
2431{
2432 FT_Face face = lockFace(Unscaled);
2433
2434 for (int gl = 0; gl < numGlyphs; gl++) {
2435 FT_UInt glyph = glyphs[gl];
2436
2437 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
2438
2439 FT_GlyphSlot g = face->glyph;
2440 if (g->format != FT_GLYPH_FORMAT_OUTLINE)
2441 continue;
2442 if (embolden)
2443 FT_GlyphSlot_Embolden(g);
2444 if (obliquen)
2445 FT_GlyphSlot_Oblique(g);
2446 QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize);
2447 }
2448 unlockFace();
2449}
2450
2451glyph_t QFontEngineFT::glyphIndex(uint ucs4) const
2452{
2453 glyph_t glyph = ucs4 < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[ucs4] : 0;
2454 if (glyph == 0) {
2455 FT_Face face = freetype->face;
2456 glyph = FT_Get_Char_Index(face, ucs4);
2457 if (glyph == 0) {
2458 // Certain fonts don't have no-break space and tab,
2459 // while we usually want to render them as space
2460 if (ucs4 == QChar::Nbsp || ucs4 == QChar::Tabulation) {
2461 glyph = FT_Get_Char_Index(face, QChar::Space);
2462 } else if (freetype->symbol_map) {
2463 // Symbol fonts can have more than one CMAPs, FreeType should take the
2464 // correct one for us by default, so we always try FT_Get_Char_Index
2465 // first. If it didn't work (returns 0), we will explicitly set the
2466 // CMAP to symbol font one and try again. symbol_map is not always the
2467 // correct one because in certain fonts like Wingdings symbol_map only
2468 // contains PUA codepoints instead of the common ones.
2469 FT_Set_Charmap(face, freetype->symbol_map);
2470 glyph = FT_Get_Char_Index(face, ucs4);
2471 FT_Set_Charmap(face, freetype->unicode_map);
2472 if (!glyph && symbol && ucs4 < 0x100)
2473 glyph = FT_Get_Char_Index(face, ucs4 + 0xf000);
2474 }
2475 }
2476 if (ucs4 < QFreetypeFace::cmapCacheSize)
2477 freetype->cmapCache[ucs4] = glyph;
2478 }
2479
2480 return glyph;
2481}
2482
2483QString QFontEngineFT::glyphName(glyph_t index) const
2484{
2485 QString result;
2486 if (index >= glyph_t(glyphCount()))
2487 return result;
2488
2489 FT_Face face = freetype->face;
2490 if (face->face_flags & FT_FACE_FLAG_GLYPH_NAMES) {
2491 char glyphName[128] = {};
2492 if (FT_Get_Glyph_Name(face, index, glyphName, sizeof(glyphName)) == 0)
2493 result = QString::fromUtf8(glyphName);
2494 }
2495
2496 return result.isEmpty() ? QFontEngine::glyphName(index) : result;
2497}
2498
2499int QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
2500 QFontEngine::ShaperFlags flags) const
2501{
2502 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
2503 if (*nglyphs < len) {
2504 *nglyphs = len;
2505 return -1;
2506 }
2507
2508 int mappedGlyphs = 0;
2509 int glyph_pos = 0;
2510 if (freetype->symbol_map) {
2511 FT_Face face = freetype->face;
2512 QStringIterator it(str, str + len);
2513 while (it.hasNext()) {
2514 uint uc = it.next();
2515 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
2516 if ( !glyphs->glyphs[glyph_pos] ) {
2517 // Symbol fonts can have more than one CMAPs, FreeType should take the
2518 // correct one for us by default, so we always try FT_Get_Char_Index
2519 // first. If it didn't work (returns 0), we will explicitly set the
2520 // CMAP to symbol font one and try again. symbol_map is not always the
2521 // correct one because in certain fonts like Wingdings symbol_map only
2522 // contains PUA codepoints instead of the common ones.
2523 glyph_t glyph = FT_Get_Char_Index(face, uc);
2524 // Certain symbol fonts don't have no-break space (0xa0) and tab (0x9),
2525 // while we usually want to render them as space
2526 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
2527 uc = 0x20;
2528 glyph = FT_Get_Char_Index(face, uc);
2529 }
2530 if (!glyph) {
2531 FT_Set_Charmap(face, freetype->symbol_map);
2532 glyph = FT_Get_Char_Index(face, uc);
2533 FT_Set_Charmap(face, freetype->unicode_map);
2534 if (!glyph && symbol && uc < 0x100)
2535 glyph = FT_Get_Char_Index(face, uc + 0xf000);
2536 }
2537 glyphs->glyphs[glyph_pos] = glyph;
2538 if (uc < QFreetypeFace::cmapCacheSize)
2539 freetype->cmapCache[uc] = glyph;
2540 }
2541 if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
2542 mappedGlyphs++;
2543 ++glyph_pos;
2544 }
2545 } else {
2546 FT_Face face = freetype->face;
2547 QStringIterator it(str, str + len);
2548 while (it.hasNext()) {
2549 uint uc = it.next();
2550 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
2551 if (!glyphs->glyphs[glyph_pos]) {
2552 {
2553 redo:
2554 glyph_t glyph = FT_Get_Char_Index(face, uc);
2555 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
2556 uc = 0x20;
2557 goto redo;
2558 }
2559 glyphs->glyphs[glyph_pos] = glyph;
2560 if (uc < QFreetypeFace::cmapCacheSize)
2561 freetype->cmapCache[uc] = glyph;
2562 }
2563 }
2564 if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
2565 mappedGlyphs++;
2566 ++glyph_pos;
2567 }
2568 }
2569
2570 *nglyphs = glyph_pos;
2571 glyphs->numGlyphs = glyph_pos;
2572
2573 if (!(flags & GlyphIndicesOnly))
2574 recalcAdvances(glyphs, flags);
2575
2576 return mappedGlyphs;
2577}
2578
2579bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const
2580{
2581 if (!FT_IS_SCALABLE(freetype->face))
2582 return false;
2583
2584 return supportsHorizontalSubPixelPositions() || (flags & DesignMetrics);
2585}
2586
2587QFixed QFontEngineFT::scaledBitmapMetrics(QFixed m) const
2588{
2589 return m * scalableBitmapScaleFactor;
2590}
2591
2592glyph_metrics_t QFontEngineFT::scaledBitmapMetrics(const glyph_metrics_t &m, const QTransform &t) const
2593{
2594 QTransform trans;
2595 trans.setMatrix(t.m11(), t.m12(), t.m13(),
2596 t.m21(), t.m22(), t.m23(),
2597 0, 0, t.m33());
2598 const qreal scaleFactor = scalableBitmapScaleFactor.toReal();
2599 trans.scale(scaleFactor, scaleFactor);
2600
2601 QRectF rect(m.x.toReal(), m.y.toReal(), m.width.toReal(), m.height.toReal());
2602 QPointF offset(m.xoff.toReal(), m.yoff.toReal());
2603
2604 rect = trans.mapRect(rect);
2605 offset = trans.map(offset);
2606
2607 glyph_metrics_t metrics;
2608 metrics.x = QFixed::fromReal(rect.x());
2609 metrics.y = QFixed::fromReal(rect.y());
2610 metrics.width = QFixed::fromReal(rect.width());
2611 metrics.height = QFixed::fromReal(rect.height());
2612 metrics.xoff = QFixed::fromReal(offset.x());
2613 metrics.yoff = QFixed::fromReal(offset.y());
2614 return metrics;
2615}
2616
2617void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2618{
2619 FT_Face face = nullptr;
2620 bool design = shouldUseDesignMetrics(flags);
2621 for (int i = 0; i < glyphs->numGlyphs; i++) {
2622 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs->glyphs[i]) : nullptr;
2623 // Since we are passing Format_None to loadGlyph, use same default format logic as loadGlyph
2624 GlyphFormat acceptableFormat = (defaultFormat != Format_None) ? defaultFormat : Format_Mono;
2625 if (g && g->format == acceptableFormat) {
2626 glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
2627 } else {
2628 if (!face)
2629 face = lockFace();
2630 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
2631 glyphs->glyphs[i],
2632 QFixedPoint(),
2633 QColor(),
2634 Format_None,
2635 true);
2636 if (g)
2637 glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
2638 else
2639 glyphs->advances[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10)
2640 : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round();
2641 if (!cacheEnabled && g != &emptyGlyph)
2642 delete g;
2643 }
2644
2645 if (scalableBitmapScaleFactor != 1)
2646 glyphs->advances[i] *= scalableBitmapScaleFactor;
2647 }
2648 if (face)
2649 unlockFace();
2650}
2651
2652glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
2653{
2654 FT_Face face = nullptr;
2655
2656 glyph_metrics_t overall;
2657 // initialize with line height, we get the same behaviour on all platforms
2658 if (!isScalableBitmap()) {
2659 overall.y = -ascent();
2660 overall.height = ascent() + descent();
2661 } else {
2662 overall.y = QFixed::fromFixed(-metrics.ascender);
2663 overall.height = QFixed::fromFixed(metrics.ascender - metrics.descender);
2664 }
2665
2666 QFixed ymax = 0;
2667 QFixed xmax = 0;
2668 for (int i = 0; i < glyphs.numGlyphs; i++) {
2669 // If shaping has found this should be ignored, ignore it.
2670 if (!glyphs.advances[i] || glyphs.attributes[i].dontPrint)
2671 continue;
2672 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs.glyphs[i]) : nullptr;
2673 if (!g) {
2674 if (!face)
2675 face = lockFace();
2676 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
2677 glyphs.glyphs[i],
2678 QFixedPoint(),
2679 QColor(),
2680 Format_None,
2681 true);
2682 }
2683 if (g) {
2684 QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
2685 QFixed y = overall.yoff + glyphs.offsets[i].y - g->y;
2686 overall.x = qMin(overall.x, x);
2687 overall.y = qMin(overall.y, y);
2688 xmax = qMax(xmax, x.ceil() + g->width);
2689 ymax = qMax(ymax, y.ceil() + g->height);
2690 if (!cacheEnabled && g != &emptyGlyph)
2691 delete g;
2692 } else {
2693 int left = FLOOR(face->glyph->metrics.horiBearingX);
2694 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
2695 int top = CEIL(face->glyph->metrics.horiBearingY);
2696 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
2697
2698 QFixed x = overall.xoff + glyphs.offsets[i].x - (-TRUNC(left));
2699 QFixed y = overall.yoff + glyphs.offsets[i].y - TRUNC(top);
2700 overall.x = qMin(overall.x, x);
2701 overall.y = qMin(overall.y, y);
2702 xmax = qMax(xmax, x + TRUNC(right - left));
2703 ymax = qMax(ymax, y + TRUNC(top - bottom));
2704 }
2705 overall.xoff += glyphs.effectiveAdvance(i);
2706 }
2707 overall.height = qMax(overall.height, ymax - overall.y);
2708 overall.width = xmax - overall.x;
2709
2710 if (face)
2711 unlockFace();
2712
2713 if (isScalableBitmap())
2714 overall = scaledBitmapMetrics(overall, QTransform());
2715 return overall;
2716}
2717
2718glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph)
2719{
2720 FT_Face face = nullptr;
2721 glyph_metrics_t overall;
2722 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) : nullptr;
2723 if (!g) {
2724 face = lockFace();
2725 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
2726 glyph,
2727 QFixedPoint(),
2728 QColor(),
2729 Format_None,
2730 true);
2731 }
2732 if (g) {
2733 overall.x = g->x;
2734 overall.y = -g->y;
2735 overall.width = g->width;
2736 overall.height = g->height;
2737 overall.xoff = g->advance;
2738 if (!cacheEnabled && g != &emptyGlyph)
2739 delete g;
2740 } else {
2741 int left = FLOOR(face->glyph->metrics.horiBearingX);
2742 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
2743 int top = CEIL(face->glyph->metrics.horiBearingY);
2744 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
2745
2746 overall.width = TRUNC(right-left);
2747 overall.height = TRUNC(top-bottom);
2748 overall.x = TRUNC(left);
2749 overall.y = -TRUNC(top);
2750 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
2751 }
2752 if (face)
2753 unlockFace();
2754
2755 if (isScalableBitmap())
2756 overall = scaledBitmapMetrics(overall, QTransform());
2757 return overall;
2758}
2759
2760glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matrix)
2761{
2762 return alphaMapBoundingBox(glyph, QFixedPoint(), matrix, QFontEngine::Format_None);
2763}
2764
2765glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph,
2766 const QFixedPoint &subPixelPosition,
2767 const QTransform &matrix,
2768 QFontEngine::GlyphFormat format)
2769{
2770 // When rendering glyphs into a cache via the alphaMap* functions, we disable
2771 // outline drawing. To ensure the bounding box matches the rendered glyph, we
2772 // need to do the same here.
2773
2774 const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
2775 && matrix.type() > QTransform::TxTranslate;
2776 if (needsImageTransform && format == QFontEngine::Format_Mono)
2777 format = QFontEngine::Format_A8;
2778 Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, QColor(), true, true);
2779
2780 glyph_metrics_t overall;
2781 if (g) {
2782 overall.x = g->x;
2783 overall.y = -g->y;
2784 overall.width = g->width;
2785 overall.height = g->height;
2786 overall.xoff = g->advance;
2787 if (!cacheEnabled && g != &emptyGlyph)
2788 delete g;
2789 } else {
2790 FT_Face face = lockFace();
2791 int left = FLOOR(face->glyph->metrics.horiBearingX);
2792 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
2793 int top = CEIL(face->glyph->metrics.horiBearingY);
2794 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
2795
2796 overall.width = TRUNC(right-left);
2797 overall.height = TRUNC(top-bottom);
2798 overall.x = TRUNC(left);
2799 overall.y = -TRUNC(top);
2800 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
2801 unlockFace();
2802 }
2803
2804 if (isScalableBitmap() || needsImageTransform)
2805 overall = scaledBitmapMetrics(overall, matrix);
2806 return overall;
2807}
2808
2809static inline QImage alphaMapFromGlyphData(QFontEngineFT::Glyph *glyph, QFontEngine::GlyphFormat glyphFormat)
2810{
2811 if (glyph == nullptr || glyph->height == 0 || glyph->width == 0)
2812 return QImage();
2813
2814 QImage::Format format = QImage::Format_Invalid;
2815 int bytesPerLine = -1;
2816 switch (glyphFormat) {
2817 case QFontEngine::Format_Mono:
2818 format = QImage::Format_Mono;
2819 bytesPerLine = ((glyph->width + 31) & ~31) >> 3;
2820 break;
2821 case QFontEngine::Format_A8:
2822 format = QImage::Format_Alpha8;
2823 bytesPerLine = (glyph->width + 3) & ~3;
2824 break;
2825 case QFontEngine::Format_A32:
2826 format = QImage::Format_RGB32;
2827 bytesPerLine = glyph->width * 4;
2828 break;
2829 default:
2830 Q_UNREACHABLE();
2831 };
2832
2833 QImage img(static_cast<const uchar *>(glyph->data), glyph->width, glyph->height, bytesPerLine, format);
2834 if (format == QImage::Format_Mono)
2835 img.setColor(1, QColor(Qt::white).rgba()); // Expands color table to 2 items; item 0 set to transparent.
2836 return img;
2837}
2838
2839QFontEngine::Glyph *QFontEngineFT::glyphData(glyph_t glyphIndex,
2840 const QFixedPoint &subPixelPosition,
2841 QFontEngine::GlyphFormat neededFormat,
2842 const QTransform &t)
2843{
2844 Q_ASSERT(cacheEnabled);
2845
2846 if (isBitmapFont())
2847 neededFormat = Format_Mono;
2848 else if (neededFormat == Format_None && defaultFormat != Format_None)
2849 neededFormat = defaultFormat;
2850 else if (neededFormat == Format_None)
2851 neededFormat = Format_A8;
2852
2853 Glyph *glyph = loadGlyphFor(glyphIndex, subPixelPosition, neededFormat, t, QColor());
2854 if (!glyph || !glyph->width || !glyph->height)
2855 return nullptr;
2856
2857 return glyph;
2858}
2859
2860static inline bool is2dRotation(const QTransform &t)
2861{
2862 return qFuzzyCompare(t.m11(), t.m22()) && qFuzzyCompare(t.m12(), -t.m21())
2863 && qFuzzyCompare(t.m11()*t.m22() - t.m12()*t.m21(), qreal(1.0));
2864}
2865
2866QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g,
2867 const QFixedPoint &subPixelPosition,
2868 GlyphFormat format,
2869 const QTransform &t,
2870 QColor color,
2871 bool fetchBoundingBox,
2872 bool disableOutlineDrawing)
2873{
2874 QGlyphSet *glyphSet = loadGlyphSet(t);
2875 if (glyphSet != nullptr && glyphSet->outline_drawing && !disableOutlineDrawing && !fetchBoundingBox)
2876 return nullptr;
2877
2878 Glyph *glyph = glyphSet != nullptr ? glyphSet->getGlyph(g, subPixelPosition) : nullptr;
2879 if (!glyph || glyph->format != format || (!fetchBoundingBox && !glyph->data)) {
2880 QScopedValueRollback<HintStyle> saved_default_hint_style(default_hint_style);
2881
2882 // A scaled glyph can be hinted at the size it has on the device, if it's a pixel at least
2883 const qreal scaleX = qAbs(t.m11());
2884 const qreal scaleY = qAbs(t.m22());
2885 const bool hintAtDeviceSize = default_hint_style != HintNone
2886 && format != Format_ARGB // COLRv1 is drawn at the size of the engine
2887 && FT_IS_SCALABLE(freetype->face)
2888 && t.type() <= QTransform::TxScale
2889 && xsize * scaleX >= 64 && ysize * scaleY >= 64;
2890
2891 FT_Matrix m = this->matrix;
2892 if (hintAtDeviceSize) {
2893 // The size is scaled instead of the outline, lockFace() sets it back when needed
2894 freetype->lock();
2895 setFaceSize(qRound(xsize * scaleX), qRound(ysize * scaleY));
2896
2897 // The synthesized slant is applied to the outline at its new size, so the matrix
2898 // has to make up for a scale that is not uniform
2899 if (obliquen)
2900 m.xy = qRound(obliqueSlant * (scaleX / scaleY - 1));
2901
2902 // A size can't mirror the outline, that is left to the matrix too
2903 if (t.m11() < 0) {
2904 m.xx = -m.xx;
2905 m.xy = -m.xy;
2906 }
2907 if (t.m22() < 0) {
2908 m.yx = -m.yx;
2909 m.yy = -m.yy;
2910 }
2911 } else {
2912 if (t.type() >= QTransform::TxScale && !is2dRotation(t))
2913 default_hint_style = HintNone; // disable hinting if the glyphs are transformed
2914
2915 lockFace();
2916 FT_Matrix ftMatrix = glyphSet != nullptr ? glyphSet->transformationMatrix : QTransformToFTMatrix(t);
2917 FT_Matrix_Multiply(&ftMatrix, &m);
2918 }
2919 freetype->matrix = m;
2920 glyph = loadGlyph(glyphSet, g, subPixelPosition, color, format, false, disableOutlineDrawing);
2921 unlockFace();
2922 }
2923
2924 return glyph;
2925}
2926
2927QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, const QFixedPoint &subPixelPosition)
2928{
2929 return alphaMapForGlyph(g, subPixelPosition, QTransform());
2930}
2931
2932QImage QFontEngineFT::alphaMapForGlyph(glyph_t g,
2933 const QFixedPoint &subPixelPosition,
2934 const QTransform &t)
2935{
2936 const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
2937 && t.type() > QTransform::TxTranslate;
2938 const GlyphFormat neededFormat = antialias || needsImageTransform ? Format_A8 : Format_Mono;
2939
2940 Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, QColor(), false, true);
2941
2942 QImage img = alphaMapFromGlyphData(glyph, neededFormat);
2943 if (needsImageTransform)
2944 img = img.transformed(t, Qt::FastTransformation);
2945 else
2946 img = img.copy();
2947
2948 if (!cacheEnabled && glyph != &emptyGlyph)
2949 delete glyph;
2950
2951 return img;
2952}
2953
2954QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g,
2955 const QFixedPoint &subPixelPosition,
2956 const QTransform &t)
2957{
2958 if (t.type() > QTransform::TxRotate)
2959 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
2960
2961 const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
2962 && t.type() > QTransform::TxTranslate;
2963
2964
2965 const GlyphFormat neededFormat = Format_A32;
2966
2967 Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, QColor(), false, true);
2968
2969 QImage img = alphaMapFromGlyphData(glyph, neededFormat);
2970 if (needsImageTransform)
2971 img = img.transformed(t, Qt::FastTransformation);
2972 else
2973 img = img.copy();
2974
2975 if (!cacheEnabled && glyph != &emptyGlyph)
2976 delete glyph;
2977
2978 if (!img.isNull())
2979 return img;
2980
2981 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
2982}
2983
2984QImage QFontEngineFT::bitmapForGlyph(glyph_t g,
2985 const QFixedPoint &subPixelPosition,
2986 const QTransform &t,
2987 const QColor &color)
2988{
2989 Glyph *glyph = loadGlyphFor(g, subPixelPosition, defaultFormat, t, color);
2990 if (glyph == nullptr)
2991 return QImage();
2992
2993 QImage img;
2994 if (defaultFormat == GlyphFormat::Format_ARGB)
2995 img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_ARGB32_Premultiplied).copy();
2996 else if (defaultFormat == GlyphFormat::Format_Mono)
2997 img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_Mono).copy();
2998
2999 if (!img.isNull() && (scalableBitmapScaleFactor != 1 || (!t.isIdentity() && !isSmoothlyScalable))) {
3000 QTransform trans(t);
3001 const qreal scaleFactor = scalableBitmapScaleFactor.toReal();
3002 trans.scale(scaleFactor, scaleFactor);
3003 img = img.transformed(trans, Qt::SmoothTransformation);
3004 }
3005
3006 if (!cacheEnabled && glyph != &emptyGlyph)
3007 delete glyph;
3008
3009 return img;
3010}
3011
3012void QFontEngineFT::removeGlyphFromCache(glyph_t glyph)
3013{
3014 defaultGlyphSet.removeGlyphFromCache(glyph, QFixedPoint());
3015}
3016
3017int QFontEngineFT::glyphCount() const
3018{
3019 int count = 0;
3020 FT_Face face = lockFace();
3021 if (face) {
3022 count = int(std::clamp(face->num_glyphs, FT_Long(0), FT_Long(INT_MAX)));
3023 unlockFace();
3024 }
3025 return count;
3026}
3027
3028FT_Face QFontEngineFT::lockFace(Scaling scale) const
3029{
3030 freetype->lock();
3031 FT_Face face = freetype->face;
3032 if (scale == Unscaled) {
3033 if (FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0) == 0) {
3034 freetype->xsize = face->units_per_EM << 6;
3035 freetype->ysize = face->units_per_EM << 6;
3036 }
3037 } else {
3038 setFaceSize(xsize, ysize);
3039 }
3040 if (freetype->matrix.xx != matrix.xx ||
3041 freetype->matrix.yy != matrix.yy ||
3042 freetype->matrix.xy != matrix.xy ||
3043 freetype->matrix.yx != matrix.yx) {
3044 freetype->matrix = matrix;
3045 FT_Set_Transform(face, &freetype->matrix, nullptr);
3046 }
3047
3048 return face;
3049}
3050
3051void QFontEngineFT::setFaceSize(int xsize, int ysize) const
3052{
3053 if (freetype->xsize != xsize || freetype->ysize != ysize) {
3054 FT_Set_Char_Size(freetype->face, xsize, ysize, 0, 0);
3055 freetype->xsize = xsize;
3056 freetype->ysize = ysize;
3057 }
3058}
3059
3060void QFontEngineFT::unlockFace() const
3061{
3062 freetype->unlock();
3063}
3064
3065FT_Face QFontEngineFT::non_locked_face() const
3066{
3067 return freetype->face;
3068}
3069
3070
3071QFontEngineFT::QGlyphSet::QGlyphSet()
3072 : outline_drawing(false)
3073{
3074 transformationMatrix.xx = 0x10000;
3075 transformationMatrix.yy = 0x10000;
3076 transformationMatrix.xy = 0;
3077 transformationMatrix.yx = 0;
3078 memset(fast_glyph_data, 0, sizeof(fast_glyph_data));
3079 fast_glyph_count = 0;
3080}
3081
3082QFontEngineFT::QGlyphSet::~QGlyphSet()
3083{
3084 clear();
3085}
3086
3087void QFontEngineFT::QGlyphSet::clear()
3088{
3089 if (fast_glyph_count > 0) {
3090 for (int i = 0; i < 256; ++i) {
3091 if (fast_glyph_data[i]) {
3092 delete fast_glyph_data[i];
3093 fast_glyph_data[i] = nullptr;
3094 }
3095 }
3096 fast_glyph_count = 0;
3097 }
3098 qDeleteAll(glyph_data);
3099 glyph_data.clear();
3100}
3101
3102void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index,
3103 const QFixedPoint &subPixelPosition)
3104{
3105 if (useFastGlyphData(index, subPixelPosition)) {
3106 if (fast_glyph_data[index]) {
3107 delete fast_glyph_data[index];
3108 fast_glyph_data[index] = nullptr;
3109 if (fast_glyph_count > 0)
3110 --fast_glyph_count;
3111 }
3112 } else {
3113 delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition));
3114 }
3115}
3116
3117void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index,
3118 const QFixedPoint &subPixelPosition,
3119 Glyph *glyph)
3120{
3121 if (useFastGlyphData(index, subPixelPosition)) {
3122 if (!fast_glyph_data[index])
3123 ++fast_glyph_count;
3124 fast_glyph_data[index] = glyph;
3125 } else {
3126 glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph);
3127 }
3128}
3129
3130int QFontEngineFT::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
3131{
3132 lockFace();
3133 bool hsubpixel = true;
3134 int vfactor = 1;
3135 int load_flags = loadFlags(nullptr, Format_A8, flags, hsubpixel, vfactor);
3136 int result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints);
3137 unlockFace();
3138 return result;
3139}
3140
3141bool QFontEngineFT::initFromFontEngine(const QFontEngineFT *fe)
3142{
3143 if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype))
3144 return false;
3145
3146 // Increase the reference of this QFreetypeFace since one more QFontEngineFT
3147 // will be using it
3148 freetype->ref.ref();
3149
3150 default_load_flags = fe->default_load_flags;
3151 default_hint_style = fe->default_hint_style;
3152 antialias = fe->antialias;
3153 transform = fe->transform;
3154 embolden = fe->embolden;
3155 obliquen = fe->obliquen;
3156 subpixelType = fe->subpixelType;
3157 lcdFilterType = fe->lcdFilterType;
3158 embeddedbitmap = fe->embeddedbitmap;
3159 verticalHintingOnly = fe->verticalHintingOnly;
3160
3161 return true;
3162}
3163
3164QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize) const
3165{
3166 QFontDef fontDef(this->fontDef);
3167 fontDef.pixelSize = pixelSize;
3168 QFontEngineFT *fe = new QFontEngineFT(fontDef);
3169 if (!fe->initFromFontEngine(this)) {
3170 delete fe;
3171 return nullptr;
3172 } else {
3173 return fe;
3174 }
3175}
3176
3177Qt::HANDLE QFontEngineFT::handle() const
3178{
3179 return non_locked_face();
3180}
3181
3182QList<QFontVariableAxis> QFontEngineFT::variableAxes() const
3183{
3184 return freetype->variableAxes();
3185}
3186
3187QT_END_NAMESPACE
3188
3189#endif // QT_NO_FREETYPE
\inmodule QtCore
QHash< QFontEngine::FaceId, QFreetypeFace * > faces
QList< QFreetypeFace * > staleFaces
QHash< FaceStyle, int > faceIndices
Combined button and popup list for selecting options.
static bool hasTrueTypeInterpreterV40(FT_Library library)
static const QFontEngine::HintStyle ftInitialDefaultHintStyle
#define FLOOR(x)
static QFontEngine::SubpixelAntialiasingType subpixelAntialiasingTypeHint()
static FT_Matrix QTransformToFTMatrix(const QTransform &matrix)
static void transformBoundingBox(int *left, int *top, int *right, int *bottom, FT_Matrix *matrix)
static void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
QtFreetypeData * qt_getFreetypeData()
static bool calculateActualItalic(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId)
#define CEIL(x)
static bool is2dRotation(const QTransform &t)
static QImage alphaMapFromGlyphData(QFontEngineFT::Glyph *glyph, QFontEngine::GlyphFormat glyphFormat)
static QFontEngineFT::Glyph emptyGlyph
QByteArray qt_fontdata_from_index(int)
size_t qHash(const QtFreetypeData::FaceStyle &style, size_t seed)
static bool ft_getSfntTable(void *user_data, uint tag, uchar *buffer, uint *length)
void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path)
static int computeFaceIndex(const QString &faceFileName, const QString &styleName)
bool operator==(const QtFreetypeData::FaceStyle &style1, const QtFreetypeData::FaceStyle &style2)
FT_Library qt_getFreetype()
static FT_UShort calculateActualWeight(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId)
#define GLYPH2PATH_DEBUG
static void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale)
static const FT_Fixed obliqueSlant
static void dont_delete(void *)
#define ROUND(x)
static bool areMetricsTooLarge(const QFontEngineFT::GlyphInfo &info)
#define TRUNC(x)
FaceStyle(QString faceFileName, QString styleName)