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