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