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 (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20701
1838 // Only the advance/metrics are needed here, not the rendered bitmap
1839 if (fetchMetricsOnly)
1840 load_flags |= FT_LOAD_BITMAP_METRICS_ONLY;
1841#endif
1842
1843#if defined(QFONTENGINE_FT_SUPPORT_COLRV1)
1844 if (FT_IS_SCALABLE(freetype->face)
1845 && FT_HAS_COLOR(freetype->face)
1846 && (load_flags & FT_LOAD_COLOR)) {
1847 // Try loading COLRv1 glyph if possible.
1848 Glyph *ret = loadColrv1Glyph(set, g, glyph, color, fetchMetricsOnly);
1849 if (ret != nullptr)
1850 return ret;
1851 }
1852#else
1853 Q_UNUSED(color);
1854#endif
1855
1856 FT_Error err = FT_Load_Glyph(face, glyph, load_flags);
1857 if (err && (load_flags & FT_LOAD_NO_BITMAP)) {
1858 load_flags &= ~FT_LOAD_NO_BITMAP;
1859 err = FT_Load_Glyph(face, glyph, load_flags);
1860 }
1861 if (err == FT_Err_Too_Few_Arguments) {
1862 // this is an error in the bytecode interpreter, just try to run without it
1863 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1864 err = FT_Load_Glyph(face, glyph, load_flags);
1865 } else if (err == FT_Err_Execution_Too_Long) {
1866 // This is an error in the bytecode, probably a web font made by someone who
1867 // didn't test bytecode hinting at all so disable for it for all glyphs.
1868 qWarning("load glyph failed due to broken hinting bytecode in font, switching to auto hinting");
1869 default_load_flags |= FT_LOAD_FORCE_AUTOHINT;
1870 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1871 err = FT_Load_Glyph(face, glyph, load_flags);
1872 }
1873 if (err != FT_Err_Ok) {
1874 qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
1875 if (set)
1876 set->setGlyphMissing(glyph);
1877 return &emptyGlyph;
1878 }
1879
1880 FT_GlyphSlot slot = face->glyph;
1881
1882 if (embolden)
1883 FT_GlyphSlot_Embolden(slot);
1884 if (obliquen && !transform) {
1885 FT_GlyphSlot_Oblique(slot);
1886
1887 // While Embolden alters the metrics of the slot, oblique does not, so we need
1888 // to fix this ourselves.
1889 transform = true;
1890 FT_Matrix m;
1891 m.xx = 0x10000;
1892 m.yx = 0x0;
1893 m.xy = 0x6000;
1894 m.yy = 0x10000;
1895
1896 FT_Matrix_Multiply(&m, &matrix);
1897 }
1898
1899 GlyphInfo info;
1900 info.linearAdvance = slot->linearHoriAdvance >> 10;
1901 info.xOff = TRUNC(ROUND(slot->advance.x));
1902 info.yOff = 0;
1903
1904 if ((set && set->outline_drawing && !disableOutlineDrawing) || fetchMetricsOnly) {
1905 int left = slot->metrics.horiBearingX;
1906 int right = slot->metrics.horiBearingX + slot->metrics.width;
1907 int top = slot->metrics.horiBearingY;
1908 int bottom = slot->metrics.horiBearingY - slot->metrics.height;
1909
1910 if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP)
1911 transformBoundingBox(&left, &top, &right, &bottom, &matrix);
1912
1913 left = FLOOR(left);
1914 right = CEIL(right);
1915 bottom = FLOOR(bottom);
1916 top = CEIL(top);
1917
1918 info.x = TRUNC(left);
1919 info.y = TRUNC(top);
1920 info.width = TRUNC(right - left);
1921 info.height = TRUNC(top - bottom);
1922
1923 // If any of the metrics are too large to fit, don't cache them
1924 // Also, avoid integer overflow when linearAdvance is to large to fit in a signed short
1925 if (areMetricsTooLarge(info))
1926 return nullptr;
1927
1928 g = new Glyph;
1929 g->data = nullptr;
1930 g->linearAdvance = info.linearAdvance;
1931 g->width = info.width;
1932 g->height = info.height;
1933 g->x = info.x;
1934 g->y = info.y;
1935 g->advance = info.xOff;
1936 g->format = format;
1937
1938 if (set)
1939 set->setGlyph(glyph, subPixelPosition, g);
1940
1941 return g;
1942 }
1943
1944 if (slot->format == FT_GLYPH_FORMAT_OUTLINE) {
1945 const QTransform xform(matrix.xx / 65536.0, matrix.xy / 65536.0,
1946 matrix.yx / 65536.0, matrix.yy / 65536.0, 0, 0);
1947 FT_BBox cbox;
1948 FT_Outline_Get_CBox(&slot->outline, &cbox);
1949 const qreal glyphWidth = qreal((cbox.xMax - cbox.xMin) >> 6);
1950 const qreal glyphHeight = qreal((cbox.yMax - cbox.yMin) >> 6);
1951 if (glyphBoundingBoxExceedsLimit(glyphWidth, glyphHeight, effectivePixelSizeSquared(xform))) {
1952 if (set)
1953 set->setGlyphMissing(glyph);
1954 return &emptyGlyph;
1955 }
1956 }
1957
1958 int glyph_buffer_size = 0;
1959 std::unique_ptr<uchar[]> glyph_buffer;
1960 FT_Render_Mode renderMode = (default_hint_style == HintLight) ? FT_RENDER_MODE_LIGHT : FT_RENDER_MODE_NORMAL;
1961 switch (format) {
1962 case Format_Mono:
1963 renderMode = FT_RENDER_MODE_MONO;
1964 break;
1965 case Format_A32:
1966 if (!hsubpixel && vfactor == 1) {
1967 qWarning("Format_A32 requested, but subpixel layout is unknown.");
1968 return nullptr;
1969 }
1970
1971 renderMode = hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V;
1972 break;
1973 case Format_A8:
1974 case Format_ARGB:
1975 break;
1976 default:
1977 Q_UNREACHABLE();
1978 }
1979 FT_Library_SetLcdFilter(slot->library, (FT_LcdFilter)lcdFilterType);
1980
1981 err = FT_Render_Glyph(slot, renderMode);
1982 FT_Library_SetLcdFilter(slot->library, FT_LCD_FILTER_NONE);
1983
1984 if (err != FT_Err_Ok) {
1985 qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
1986 return nullptr;
1987 }
1988
1989 info.height = slot->bitmap.rows;
1990 info.width = slot->bitmap.width;
1991 info.x = slot->bitmap_left;
1992 info.y = slot->bitmap_top;
1993 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD)
1994 info.width = info.width / 3;
1995 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V)
1996 info.height = info.height / vfactor;
1997
1998 int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 :
1999 (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4));
2000
2001 glyph_buffer_size = info.height * pitch;
2002 glyph_buffer.reset(new uchar[glyph_buffer_size]);
2003
2004 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
2005 uchar *src = slot->bitmap.buffer;
2006 uchar *dst = glyph_buffer.get();
2007 int h = slot->bitmap.rows;
2008 // Some fonts return bitmaps even when we requested something else:
2009 if (format == Format_Mono) {
2010 int bytes = ((info.width + 7) & ~7) >> 3;
2011 while (h--) {
2012 memcpy (dst, src, bytes);
2013 dst += pitch;
2014 src += slot->bitmap.pitch;
2015 }
2016 } else if (format == Format_A8) {
2017 while (h--) {
2018 for (int x = 0; x < int{info.width}; x++)
2019 dst[x] = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00);
2020 dst += pitch;
2021 src += slot->bitmap.pitch;
2022 }
2023 } else {
2024 while (h--) {
2025 uint *dd = reinterpret_cast<uint *>(dst);
2026 for (int x = 0; x < int{info.width}; x++)
2027 dd[x] = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffffff : 0x00000000);
2028 dst += pitch;
2029 src += slot->bitmap.pitch;
2030 }
2031 }
2032 } else if (slot->bitmap.pixel_mode == 7 /*FT_PIXEL_MODE_BGRA*/) {
2033 Q_ASSERT(format == Format_ARGB);
2034 uchar *src = slot->bitmap.buffer;
2035 uchar *dst = glyph_buffer.get();
2036 int h = slot->bitmap.rows;
2037 while (h--) {
2038#if Q_BYTE_ORDER == Q_BIG_ENDIAN
2039 const quint32 *srcPixel = (const quint32 *)src;
2040 quint32 *dstPixel = (quint32 *)dst;
2041 for (int x = 0; x < static_cast<int>(slot->bitmap.width); x++, srcPixel++, dstPixel++) {
2042 const quint32 pixel = *srcPixel;
2043 *dstPixel = qbswap(pixel);
2044 }
2045#else
2046 memcpy(dst, src, slot->bitmap.width * 4);
2047#endif
2048 dst += slot->bitmap.pitch;
2049 src += slot->bitmap.pitch;
2050 }
2051 info.linearAdvance = info.xOff = slot->bitmap.width;
2052 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
2053 if (format == Format_A8) {
2054 uchar *src = slot->bitmap.buffer;
2055 uchar *dst = glyph_buffer.get();
2056 int h = slot->bitmap.rows;
2057 int bytes = info.width;
2058 while (h--) {
2059 memcpy (dst, src, bytes);
2060 dst += pitch;
2061 src += slot->bitmap.pitch;
2062 }
2063 } else if (format == Format_ARGB) {
2064 uchar *src = slot->bitmap.buffer;
2065 quint32 *dstPixel = reinterpret_cast<quint32 *>(glyph_buffer.get());
2066 int h = slot->bitmap.rows;
2067 while (h--) {
2068 for (int x = 0; x < static_cast<int>(slot->bitmap.width); ++x) {
2069 uchar alpha = src[x];
2070 float alphaF = alpha / 255.0;
2071 dstPixel[x] = qRgba(qRound(alphaF * color.red()),
2072 qRound(alphaF * color.green()),
2073 qRound(alphaF * color.blue()),
2074 alpha);
2075 }
2076 src += slot->bitmap.pitch;
2077 dstPixel += info.width;
2078 }
2079 }
2080 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
2081 Q_ASSERT(format == Format_A32);
2082 convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB);
2083 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) {
2084 Q_ASSERT(format == Format_A32);
2085 convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB);
2086 } else {
2087 qWarning("QFontEngine: Glyph rendered in unknown pixel_mode=%d", slot->bitmap.pixel_mode);
2088 return nullptr;
2089 }
2090
2091 if (!g) {
2092 g = new Glyph;
2093 g->data = nullptr;
2094 }
2095
2096 g->linearAdvance = info.linearAdvance;
2097 g->width = info.width;
2098 g->height = info.height;
2099 g->x = info.x;
2100 g->y = info.y;
2101 g->advance = info.xOff;
2102 g->format = format;
2103 delete [] g->data;
2104 g->data = glyph_buffer.release();
2105
2106 if (set)
2107 set->setGlyph(glyph, subPixelPosition, g);
2108
2109 return g;
2110}
2111
2112QFontEngine::FaceId QFontEngineFT::faceId() const
2113{
2114 return face_id;
2115}
2116
2117QFontEngine::Properties QFontEngineFT::properties() const
2118{
2119 Properties p = freetype->properties();
2120 if (p.postscriptName.isEmpty()) {
2121 p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family().toUtf8());
2122 }
2123
2124 return freetype->properties();
2125}
2126
2127QFixed QFontEngineFT::emSquareSize() const
2128{
2129 if (FT_IS_SCALABLE(freetype->face))
2130 return freetype->face->units_per_EM;
2131 else
2132 return freetype->face->size->metrics.y_ppem;
2133}
2134
2135bool QFontEngineFT::getSfntTableData(uint tag, uchar *buffer, uint *length) const
2136{
2137 return ft_getSfntTable(freetype->face, tag, buffer, length);
2138}
2139
2140int QFontEngineFT::synthesized() const
2141{
2142 int s = 0;
2143 if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC))
2144 s = SynthesizedItalic;
2145 if ((fontDef.weight >= QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD))
2146 s |= SynthesizedBold;
2147 if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face))
2148 s |= SynthesizedStretch;
2149 return s;
2150}
2151
2152void QFontEngineFT::initializeHeightMetrics() const
2153{
2154 m_ascent = QFixed::fromFixed(metrics.ascender);
2155 m_descent = QFixed::fromFixed(-metrics.descender);
2156 m_leading = QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender);
2157
2158 QFontEngine::initializeHeightMetrics();
2159
2160 if (scalableBitmapScaleFactor != 1) {
2161 m_ascent *= scalableBitmapScaleFactor;
2162 m_descent *= scalableBitmapScaleFactor;
2163 m_leading *= scalableBitmapScaleFactor;
2164 }
2165}
2166
2167QFixed QFontEngineFT::capHeight() const
2168{
2169 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
2170 if (os2 && os2->version >= 2) {
2171 lockFace();
2172 QFixed answer = QFixed::fromFixed(FT_MulFix(os2->sCapHeight, freetype->face->size->metrics.y_scale));
2173 unlockFace();
2174 return answer;
2175 }
2176 return calculatedCapHeight();
2177}
2178
2179QFixed QFontEngineFT::xHeight() const
2180{
2181 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
2182 if (os2 && os2->sxHeight) {
2183 lockFace();
2184 QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize();
2185 unlockFace();
2186 return answer;
2187 }
2188
2189 return QFontEngine::xHeight();
2190}
2191
2192QFixed QFontEngineFT::averageCharWidth() const
2193{
2194 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
2195 if (os2 && os2->xAvgCharWidth) {
2196 lockFace();
2197 QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize();
2198 unlockFace();
2199 return answer;
2200 }
2201
2202 return QFontEngine::averageCharWidth();
2203}
2204
2205qreal QFontEngineFT::maxCharWidth() const
2206{
2207 QFixed max_advance = QFixed::fromFixed(metrics.max_advance);
2208 if (scalableBitmapScaleFactor != 1)
2209 max_advance *= scalableBitmapScaleFactor;
2210 return max_advance.toReal();
2211}
2212
2213QFixed QFontEngineFT::lineThickness() const
2214{
2215 return line_thickness;
2216}
2217
2218QFixed QFontEngineFT::underlinePosition() const
2219{
2220 return underline_position;
2221}
2222
2223void QFontEngineFT::doKerning(QGlyphLayout *g, QFontEngine::ShaperFlags flags) const
2224{
2225 if (!kerning_pairs_loaded) {
2226 kerning_pairs_loaded = true;
2227 lockFace();
2228 if (freetype->face->size->metrics.x_ppem != 0) {
2229 QFixed scalingFactor = emSquareSize() / QFixed(freetype->face->size->metrics.x_ppem);
2230 unlockFace();
2231 const_cast<QFontEngineFT *>(this)->loadKerningPairs(scalingFactor);
2232 } else {
2233 unlockFace();
2234 }
2235 }
2236
2237 if (shouldUseDesignMetrics(flags))
2238 flags |= DesignMetrics;
2239 else
2240 flags &= ~DesignMetrics;
2241
2242 QFontEngine::doKerning(g, flags);
2243}
2244
2245static inline FT_Matrix QTransformToFTMatrix(const QTransform &matrix)
2246{
2247 FT_Matrix m;
2248
2249 m.xx = FT_Fixed(matrix.m11() * 65536);
2250 m.xy = FT_Fixed(-matrix.m21() * 65536);
2251 m.yx = FT_Fixed(-matrix.m12() * 65536);
2252 m.yy = FT_Fixed(matrix.m22() * 65536);
2253
2254 return m;
2255}
2256
2257QFontEngineFT::QGlyphSet *QFontEngineFT::TransformedGlyphSets::findSet(const QTransform &matrix, const QFontDef &fontDef)
2258{
2259 FT_Matrix m = QTransformToFTMatrix(matrix);
2260
2261 int i = 0;
2262 for (; i < nSets; ++i) {
2263 QGlyphSet *g = sets[i];
2264 if (!g)
2265 break;
2266 if (g->transformationMatrix.xx == m.xx
2267 && g->transformationMatrix.xy == m.xy
2268 && g->transformationMatrix.yx == m.yx
2269 && g->transformationMatrix.yy == m.yy) {
2270
2271 // found a match, move it to the front
2272 moveToFront(i);
2273 return g;
2274 }
2275 }
2276
2277 // don't cache more than nSets transformations
2278 if (i == nSets)
2279 // reuse the last set
2280 --i;
2281 moveToFront(nSets - 1);
2282 if (!sets[0])
2283 sets[0] = new QGlyphSet;
2284 QGlyphSet *gs = sets[0];
2285 Q_ASSERT(gs != nullptr);
2286
2287 gs->clear();
2288 gs->transformationMatrix = m;
2289 const int maxCachedSize = maxCachedGlyphSize();
2290 gs->outline_drawing = fontDef.pixelSize * fontDef.pixelSize * qAbs(matrix.determinant()) > maxCachedSize * maxCachedSize;
2291
2292 return gs;
2293}
2294
2295void QFontEngineFT::TransformedGlyphSets::moveToFront(int i)
2296{
2297 QGlyphSet *g = sets[i];
2298 while (i > 0) {
2299 sets[i] = sets[i - 1];
2300 --i;
2301 }
2302 sets[0] = g;
2303}
2304
2305
2306QFontEngineFT::QGlyphSet *QFontEngineFT::loadGlyphSet(const QTransform &matrix)
2307{
2308 if (matrix.type() > QTransform::TxShear || !cacheEnabled)
2309 return nullptr;
2310
2311 // FT_Set_Transform only supports scalable fonts
2312 if (!FT_IS_SCALABLE(freetype->face))
2313 return matrix.type() <= QTransform::TxTranslate ? &defaultGlyphSet : nullptr;
2314
2315 return transformedGlyphSets.findSet(matrix, fontDef);
2316}
2317
2318void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
2319{
2320 FT_Face face = lockFace(Unscaled);
2321 FT_Set_Transform(face, nullptr, nullptr);
2322 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
2323
2324 int left = face->glyph->metrics.horiBearingX;
2325 int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width;
2326 int top = face->glyph->metrics.horiBearingY;
2327 int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
2328
2329 QFixedPoint p;
2330 p.x = 0;
2331 p.y = 0;
2332
2333 metrics->width = QFixed::fromFixed(right-left);
2334 metrics->height = QFixed::fromFixed(top-bottom);
2335 metrics->x = QFixed::fromFixed(left);
2336 metrics->y = QFixed::fromFixed(-top);
2337 metrics->xoff = QFixed::fromFixed(face->glyph->advance.x);
2338
2339 if (!FT_IS_SCALABLE(freetype->face))
2340 QFreetypeFace::addBitmapToPath(face->glyph, p, path);
2341 else
2342 QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6);
2343
2344 FT_Set_Transform(face, &freetype->matrix, nullptr);
2345 unlockFace();
2346}
2347
2348bool QFontEngineFT::supportsTransformation(const QTransform &transform) const
2349{
2350 return transform.type() <= QTransform::TxRotate;
2351}
2352
2353void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
2354{
2355 if (!glyphs.numGlyphs)
2356 return;
2357
2358 if (FT_IS_SCALABLE(freetype->face)) {
2359 QFontEngine::addOutlineToPath(x, y, glyphs, path, flags);
2360 } else {
2361 QVarLengthArray<QFixedPoint> positions;
2362 QVarLengthArray<glyph_t> positioned_glyphs;
2363 QTransform matrix;
2364 matrix.translate(x, y);
2365 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
2366
2367 FT_Face face = lockFace(Unscaled);
2368 for (int gl = 0; gl < glyphs.numGlyphs; gl++) {
2369 FT_UInt glyph = positioned_glyphs[gl];
2370 FT_Load_Glyph(face, glyph, FT_LOAD_TARGET_MONO);
2371 QFreetypeFace::addBitmapToPath(face->glyph, positions[gl], path);
2372 }
2373 unlockFace();
2374 }
2375}
2376
2377void QFontEngineFT::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int numGlyphs,
2378 QPainterPath *path, QTextItem::RenderFlags)
2379{
2380 FT_Face face = lockFace(Unscaled);
2381
2382 for (int gl = 0; gl < numGlyphs; gl++) {
2383 FT_UInt glyph = glyphs[gl];
2384
2385 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
2386
2387 FT_GlyphSlot g = face->glyph;
2388 if (g->format != FT_GLYPH_FORMAT_OUTLINE)
2389 continue;
2390 if (embolden)
2391 FT_GlyphSlot_Embolden(g);
2392 if (obliquen)
2393 FT_GlyphSlot_Oblique(g);
2394 QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize);
2395 }
2396 unlockFace();
2397}
2398
2399glyph_t QFontEngineFT::glyphIndex(uint ucs4) const
2400{
2401 glyph_t glyph = ucs4 < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[ucs4] : 0;
2402 if (glyph == 0) {
2403 FT_Face face = freetype->face;
2404 glyph = FT_Get_Char_Index(face, ucs4);
2405 if (glyph == 0) {
2406 // Certain fonts don't have no-break space and tab,
2407 // while we usually want to render them as space
2408 if (ucs4 == QChar::Nbsp || ucs4 == QChar::Tabulation) {
2409 glyph = FT_Get_Char_Index(face, QChar::Space);
2410 } else if (freetype->symbol_map) {
2411 // Symbol fonts can have more than one CMAPs, FreeType should take the
2412 // correct one for us by default, so we always try FT_Get_Char_Index
2413 // first. If it didn't work (returns 0), we will explicitly set the
2414 // CMAP to symbol font one and try again. symbol_map is not always the
2415 // correct one because in certain fonts like Wingdings symbol_map only
2416 // contains PUA codepoints instead of the common ones.
2417 FT_Set_Charmap(face, freetype->symbol_map);
2418 glyph = FT_Get_Char_Index(face, ucs4);
2419 FT_Set_Charmap(face, freetype->unicode_map);
2420 if (!glyph && symbol && ucs4 < 0x100)
2421 glyph = FT_Get_Char_Index(face, ucs4 + 0xf000);
2422 }
2423 }
2424 if (ucs4 < QFreetypeFace::cmapCacheSize)
2425 freetype->cmapCache[ucs4] = glyph;
2426 }
2427
2428 return glyph;
2429}
2430
2431QString QFontEngineFT::glyphName(glyph_t index) const
2432{
2433 QString result;
2434 if (index >= glyph_t(glyphCount()))
2435 return result;
2436
2437 FT_Face face = freetype->face;
2438 if (face->face_flags & FT_FACE_FLAG_GLYPH_NAMES) {
2439 char glyphName[128] = {};
2440 if (FT_Get_Glyph_Name(face, index, glyphName, sizeof(glyphName)) == 0)
2441 result = QString::fromUtf8(glyphName);
2442 }
2443
2444 return result.isEmpty() ? QFontEngine::glyphName(index) : result;
2445}
2446
2447int QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
2448 QFontEngine::ShaperFlags flags) const
2449{
2450 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
2451 if (*nglyphs < len) {
2452 *nglyphs = len;
2453 return -1;
2454 }
2455
2456 int mappedGlyphs = 0;
2457 int glyph_pos = 0;
2458 if (freetype->symbol_map) {
2459 FT_Face face = freetype->face;
2460 QStringIterator it(str, str + len);
2461 while (it.hasNext()) {
2462 uint uc = it.next();
2463 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
2464 if ( !glyphs->glyphs[glyph_pos] ) {
2465 // Symbol fonts can have more than one CMAPs, FreeType should take the
2466 // correct one for us by default, so we always try FT_Get_Char_Index
2467 // first. If it didn't work (returns 0), we will explicitly set the
2468 // CMAP to symbol font one and try again. symbol_map is not always the
2469 // correct one because in certain fonts like Wingdings symbol_map only
2470 // contains PUA codepoints instead of the common ones.
2471 glyph_t glyph = FT_Get_Char_Index(face, uc);
2472 // Certain symbol fonts don't have no-break space (0xa0) and tab (0x9),
2473 // while we usually want to render them as space
2474 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
2475 uc = 0x20;
2476 glyph = FT_Get_Char_Index(face, uc);
2477 }
2478 if (!glyph) {
2479 FT_Set_Charmap(face, freetype->symbol_map);
2480 glyph = FT_Get_Char_Index(face, uc);
2481 FT_Set_Charmap(face, freetype->unicode_map);
2482 if (!glyph && symbol && uc < 0x100)
2483 glyph = FT_Get_Char_Index(face, uc + 0xf000);
2484 }
2485 glyphs->glyphs[glyph_pos] = glyph;
2486 if (uc < QFreetypeFace::cmapCacheSize)
2487 freetype->cmapCache[uc] = glyph;
2488 }
2489 if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
2490 mappedGlyphs++;
2491 ++glyph_pos;
2492 }
2493 } else {
2494 FT_Face face = freetype->face;
2495 QStringIterator it(str, str + len);
2496 while (it.hasNext()) {
2497 uint uc = it.next();
2498 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
2499 if (!glyphs->glyphs[glyph_pos]) {
2500 {
2501 redo:
2502 glyph_t glyph = FT_Get_Char_Index(face, uc);
2503 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
2504 uc = 0x20;
2505 goto redo;
2506 }
2507 glyphs->glyphs[glyph_pos] = glyph;
2508 if (uc < QFreetypeFace::cmapCacheSize)
2509 freetype->cmapCache[uc] = glyph;
2510 }
2511 }
2512 if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
2513 mappedGlyphs++;
2514 ++glyph_pos;
2515 }
2516 }
2517
2518 *nglyphs = glyph_pos;
2519 glyphs->numGlyphs = glyph_pos;
2520
2521 if (!(flags & GlyphIndicesOnly))
2522 recalcAdvances(glyphs, flags);
2523
2524 return mappedGlyphs;
2525}
2526
2527bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const
2528{
2529 if (!FT_IS_SCALABLE(freetype->face))
2530 return false;
2531
2532 return default_hint_style == HintNone || default_hint_style == HintLight || (flags & DesignMetrics);
2533}
2534
2535QFixed QFontEngineFT::scaledBitmapMetrics(QFixed m) const
2536{
2537 return m * scalableBitmapScaleFactor;
2538}
2539
2540glyph_metrics_t QFontEngineFT::scaledBitmapMetrics(const glyph_metrics_t &m, const QTransform &t) const
2541{
2542 QTransform trans;
2543 trans.setMatrix(t.m11(), t.m12(), t.m13(),
2544 t.m21(), t.m22(), t.m23(),
2545 0, 0, t.m33());
2546 const qreal scaleFactor = scalableBitmapScaleFactor.toReal();
2547 trans.scale(scaleFactor, scaleFactor);
2548
2549 QRectF rect(m.x.toReal(), m.y.toReal(), m.width.toReal(), m.height.toReal());
2550 QPointF offset(m.xoff.toReal(), m.yoff.toReal());
2551
2552 rect = trans.mapRect(rect);
2553 offset = trans.map(offset);
2554
2555 glyph_metrics_t metrics;
2556 metrics.x = QFixed::fromReal(rect.x());
2557 metrics.y = QFixed::fromReal(rect.y());
2558 metrics.width = QFixed::fromReal(rect.width());
2559 metrics.height = QFixed::fromReal(rect.height());
2560 metrics.xoff = QFixed::fromReal(offset.x());
2561 metrics.yoff = QFixed::fromReal(offset.y());
2562 return metrics;
2563}
2564
2565void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
2566{
2567 FT_Face face = nullptr;
2568 bool design = shouldUseDesignMetrics(flags);
2569 for (int i = 0; i < glyphs->numGlyphs; i++) {
2570 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs->glyphs[i]) : nullptr;
2571 // Since we are passing Format_None to loadGlyph, use same default format logic as loadGlyph
2572 GlyphFormat acceptableFormat = (defaultFormat != Format_None) ? defaultFormat : Format_Mono;
2573 if (g && g->format == acceptableFormat) {
2574 glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
2575 } else {
2576 if (!face)
2577 face = lockFace();
2578 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
2579 glyphs->glyphs[i],
2580 QFixedPoint(),
2581 QColor(),
2582 Format_None,
2583 true);
2584 if (g)
2585 glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
2586 else
2587 glyphs->advances[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10)
2588 : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round();
2589 if (!cacheEnabled && g != &emptyGlyph)
2590 delete g;
2591 }
2592
2593 if (scalableBitmapScaleFactor != 1)
2594 glyphs->advances[i] *= scalableBitmapScaleFactor;
2595 }
2596 if (face)
2597 unlockFace();
2598}
2599
2600glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
2601{
2602 FT_Face face = nullptr;
2603
2604 glyph_metrics_t overall;
2605 // initialize with line height, we get the same behaviour on all platforms
2606 if (!isScalableBitmap()) {
2607 overall.y = -ascent();
2608 overall.height = ascent() + descent();
2609 } else {
2610 overall.y = QFixed::fromFixed(-metrics.ascender);
2611 overall.height = QFixed::fromFixed(metrics.ascender - metrics.descender);
2612 }
2613
2614 QFixed ymax = 0;
2615 QFixed xmax = 0;
2616 for (int i = 0; i < glyphs.numGlyphs; i++) {
2617 // If shaping has found this should be ignored, ignore it.
2618 if (!glyphs.advances[i] || glyphs.attributes[i].dontPrint)
2619 continue;
2620 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs.glyphs[i]) : nullptr;
2621 if (!g) {
2622 if (!face)
2623 face = lockFace();
2624 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
2625 glyphs.glyphs[i],
2626 QFixedPoint(),
2627 QColor(),
2628 Format_None,
2629 true);
2630 }
2631 if (g) {
2632 QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
2633 QFixed y = overall.yoff + glyphs.offsets[i].y - g->y;
2634 overall.x = qMin(overall.x, x);
2635 overall.y = qMin(overall.y, y);
2636 xmax = qMax(xmax, x.ceil() + g->width);
2637 ymax = qMax(ymax, y.ceil() + g->height);
2638 if (!cacheEnabled && g != &emptyGlyph)
2639 delete g;
2640 } else {
2641 int left = FLOOR(face->glyph->metrics.horiBearingX);
2642 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
2643 int top = CEIL(face->glyph->metrics.horiBearingY);
2644 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
2645
2646 QFixed x = overall.xoff + glyphs.offsets[i].x - (-TRUNC(left));
2647 QFixed y = overall.yoff + glyphs.offsets[i].y - TRUNC(top);
2648 overall.x = qMin(overall.x, x);
2649 overall.y = qMin(overall.y, y);
2650 xmax = qMax(xmax, x + TRUNC(right - left));
2651 ymax = qMax(ymax, y + TRUNC(top - bottom));
2652 }
2653 overall.xoff += glyphs.effectiveAdvance(i);
2654 }
2655 overall.height = qMax(overall.height, ymax - overall.y);
2656 overall.width = xmax - overall.x;
2657
2658 if (face)
2659 unlockFace();
2660
2661 if (isScalableBitmap())
2662 overall = scaledBitmapMetrics(overall, QTransform());
2663 return overall;
2664}
2665
2666glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph)
2667{
2668 FT_Face face = nullptr;
2669 glyph_metrics_t overall;
2670 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) : nullptr;
2671 if (!g) {
2672 face = lockFace();
2673 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
2674 glyph,
2675 QFixedPoint(),
2676 QColor(),
2677 Format_None,
2678 true);
2679 }
2680 if (g) {
2681 overall.x = g->x;
2682 overall.y = -g->y;
2683 overall.width = g->width;
2684 overall.height = g->height;
2685 overall.xoff = g->advance;
2686 if (!cacheEnabled && g != &emptyGlyph)
2687 delete g;
2688 } else {
2689 int left = FLOOR(face->glyph->metrics.horiBearingX);
2690 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
2691 int top = CEIL(face->glyph->metrics.horiBearingY);
2692 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
2693
2694 overall.width = TRUNC(right-left);
2695 overall.height = TRUNC(top-bottom);
2696 overall.x = TRUNC(left);
2697 overall.y = -TRUNC(top);
2698 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
2699 }
2700 if (face)
2701 unlockFace();
2702
2703 if (isScalableBitmap())
2704 overall = scaledBitmapMetrics(overall, QTransform());
2705 return overall;
2706}
2707
2708glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph, const QTransform &matrix)
2709{
2710 return alphaMapBoundingBox(glyph, QFixedPoint(), matrix, QFontEngine::Format_None);
2711}
2712
2713glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph,
2714 const QFixedPoint &subPixelPosition,
2715 const QTransform &matrix,
2716 QFontEngine::GlyphFormat format)
2717{
2718 // When rendering glyphs into a cache via the alphaMap* functions, we disable
2719 // outline drawing. To ensure the bounding box matches the rendered glyph, we
2720 // need to do the same here.
2721
2722 const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
2723 && matrix.type() > QTransform::TxTranslate;
2724 if (needsImageTransform && format == QFontEngine::Format_Mono)
2725 format = QFontEngine::Format_A8;
2726 Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, QColor(), true, true);
2727
2728 glyph_metrics_t overall;
2729 if (g) {
2730 overall.x = g->x;
2731 overall.y = -g->y;
2732 overall.width = g->width;
2733 overall.height = g->height;
2734 overall.xoff = g->advance;
2735 if (!cacheEnabled && g != &emptyGlyph)
2736 delete g;
2737 } else {
2738 FT_Face face = lockFace();
2739 int left = FLOOR(face->glyph->metrics.horiBearingX);
2740 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
2741 int top = CEIL(face->glyph->metrics.horiBearingY);
2742 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
2743
2744 overall.width = TRUNC(right-left);
2745 overall.height = TRUNC(top-bottom);
2746 overall.x = TRUNC(left);
2747 overall.y = -TRUNC(top);
2748 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
2749 unlockFace();
2750 }
2751
2752 if (isScalableBitmap() || needsImageTransform)
2753 overall = scaledBitmapMetrics(overall, matrix);
2754 return overall;
2755}
2756
2757static inline QImage alphaMapFromGlyphData(QFontEngineFT::Glyph *glyph, QFontEngine::GlyphFormat glyphFormat)
2758{
2759 if (glyph == nullptr || glyph->height == 0 || glyph->width == 0)
2760 return QImage();
2761
2762 QImage::Format format = QImage::Format_Invalid;
2763 int bytesPerLine = -1;
2764 switch (glyphFormat) {
2765 case QFontEngine::Format_Mono:
2766 format = QImage::Format_Mono;
2767 bytesPerLine = ((glyph->width + 31) & ~31) >> 3;
2768 break;
2769 case QFontEngine::Format_A8:
2770 format = QImage::Format_Alpha8;
2771 bytesPerLine = (glyph->width + 3) & ~3;
2772 break;
2773 case QFontEngine::Format_A32:
2774 format = QImage::Format_RGB32;
2775 bytesPerLine = glyph->width * 4;
2776 break;
2777 default:
2778 Q_UNREACHABLE();
2779 };
2780
2781 QImage img(static_cast<const uchar *>(glyph->data), glyph->width, glyph->height, bytesPerLine, format);
2782 if (format == QImage::Format_Mono)
2783 img.setColor(1, QColor(Qt::white).rgba()); // Expands color table to 2 items; item 0 set to transparent.
2784 return img;
2785}
2786
2787QFontEngine::Glyph *QFontEngineFT::glyphData(glyph_t glyphIndex,
2788 const QFixedPoint &subPixelPosition,
2789 QFontEngine::GlyphFormat neededFormat,
2790 const QTransform &t)
2791{
2792 Q_ASSERT(cacheEnabled);
2793
2794 if (isBitmapFont())
2795 neededFormat = Format_Mono;
2796 else if (neededFormat == Format_None && defaultFormat != Format_None)
2797 neededFormat = defaultFormat;
2798 else if (neededFormat == Format_None)
2799 neededFormat = Format_A8;
2800
2801 Glyph *glyph = loadGlyphFor(glyphIndex, subPixelPosition, neededFormat, t, QColor());
2802 if (!glyph || !glyph->width || !glyph->height)
2803 return nullptr;
2804
2805 return glyph;
2806}
2807
2808static inline bool is2dRotation(const QTransform &t)
2809{
2810 return qFuzzyCompare(t.m11(), t.m22()) && qFuzzyCompare(t.m12(), -t.m21())
2811 && qFuzzyCompare(t.m11()*t.m22() - t.m12()*t.m21(), qreal(1.0));
2812}
2813
2814QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g,
2815 const QFixedPoint &subPixelPosition,
2816 GlyphFormat format,
2817 const QTransform &t,
2818 QColor color,
2819 bool fetchBoundingBox,
2820 bool disableOutlineDrawing)
2821{
2822 QGlyphSet *glyphSet = loadGlyphSet(t);
2823 if (glyphSet != nullptr && glyphSet->outline_drawing && !disableOutlineDrawing && !fetchBoundingBox)
2824 return nullptr;
2825
2826 Glyph *glyph = glyphSet != nullptr ? glyphSet->getGlyph(g, subPixelPosition) : nullptr;
2827 if (!glyph || glyph->format != format || (!fetchBoundingBox && !glyph->data)) {
2828 QScopedValueRollback<HintStyle> saved_default_hint_style(default_hint_style);
2829 if (t.type() >= QTransform::TxScale && !is2dRotation(t))
2830 default_hint_style = HintNone; // disable hinting if the glyphs are transformed
2831
2832 lockFace();
2833 FT_Matrix m = this->matrix;
2834 FT_Matrix ftMatrix = glyphSet != nullptr ? glyphSet->transformationMatrix : QTransformToFTMatrix(t);
2835 FT_Matrix_Multiply(&ftMatrix, &m);
2836 freetype->matrix = m;
2837 glyph = loadGlyph(glyphSet, g, subPixelPosition, color, format, false, disableOutlineDrawing);
2838 unlockFace();
2839 }
2840
2841 return glyph;
2842}
2843
2844QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, const QFixedPoint &subPixelPosition)
2845{
2846 return alphaMapForGlyph(g, subPixelPosition, QTransform());
2847}
2848
2849QImage QFontEngineFT::alphaMapForGlyph(glyph_t g,
2850 const QFixedPoint &subPixelPosition,
2851 const QTransform &t)
2852{
2853 const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
2854 && t.type() > QTransform::TxTranslate;
2855 const GlyphFormat neededFormat = antialias || needsImageTransform ? Format_A8 : Format_Mono;
2856
2857 Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, QColor(), false, true);
2858
2859 QImage img = alphaMapFromGlyphData(glyph, neededFormat);
2860 if (needsImageTransform)
2861 img = img.transformed(t, Qt::FastTransformation);
2862 else
2863 img = img.copy();
2864
2865 if (!cacheEnabled && glyph != &emptyGlyph)
2866 delete glyph;
2867
2868 return img;
2869}
2870
2871QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g,
2872 const QFixedPoint &subPixelPosition,
2873 const QTransform &t)
2874{
2875 if (t.type() > QTransform::TxRotate)
2876 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
2877
2878 const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
2879 && t.type() > QTransform::TxTranslate;
2880
2881
2882 const GlyphFormat neededFormat = Format_A32;
2883
2884 Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, QColor(), false, true);
2885
2886 QImage img = alphaMapFromGlyphData(glyph, neededFormat);
2887 if (needsImageTransform)
2888 img = img.transformed(t, Qt::FastTransformation);
2889 else
2890 img = img.copy();
2891
2892 if (!cacheEnabled && glyph != &emptyGlyph)
2893 delete glyph;
2894
2895 if (!img.isNull())
2896 return img;
2897
2898 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
2899}
2900
2901QImage QFontEngineFT::bitmapForGlyph(glyph_t g,
2902 const QFixedPoint &subPixelPosition,
2903 const QTransform &t,
2904 const QColor &color)
2905{
2906 Glyph *glyph = loadGlyphFor(g, subPixelPosition, defaultFormat, t, color);
2907 if (glyph == nullptr)
2908 return QImage();
2909
2910 QImage img;
2911 if (defaultFormat == GlyphFormat::Format_ARGB)
2912 img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_ARGB32_Premultiplied).copy();
2913 else if (defaultFormat == GlyphFormat::Format_Mono)
2914 img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_Mono).copy();
2915
2916 if (!img.isNull() && (scalableBitmapScaleFactor != 1 || (!t.isIdentity() && !isSmoothlyScalable))) {
2917 QTransform trans(t);
2918 const qreal scaleFactor = scalableBitmapScaleFactor.toReal();
2919 trans.scale(scaleFactor, scaleFactor);
2920 img = img.transformed(trans, Qt::SmoothTransformation);
2921 }
2922
2923 if (!cacheEnabled && glyph != &emptyGlyph)
2924 delete glyph;
2925
2926 return img;
2927}
2928
2929void QFontEngineFT::removeGlyphFromCache(glyph_t glyph)
2930{
2931 defaultGlyphSet.removeGlyphFromCache(glyph, QFixedPoint());
2932}
2933
2934int QFontEngineFT::glyphCount() const
2935{
2936 int count = 0;
2937 FT_Face face = lockFace();
2938 if (face) {
2939 count = face->num_glyphs;
2940 unlockFace();
2941 }
2942 return count;
2943}
2944
2945FT_Face QFontEngineFT::lockFace(Scaling scale) const
2946{
2947 freetype->lock();
2948 FT_Face face = freetype->face;
2949 if (scale == Unscaled) {
2950 if (FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0) == 0) {
2951 freetype->xsize = face->units_per_EM << 6;
2952 freetype->ysize = face->units_per_EM << 6;
2953 }
2954 } else if (freetype->xsize != xsize || freetype->ysize != ysize) {
2955 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
2956 freetype->xsize = xsize;
2957 freetype->ysize = ysize;
2958 }
2959 if (freetype->matrix.xx != matrix.xx ||
2960 freetype->matrix.yy != matrix.yy ||
2961 freetype->matrix.xy != matrix.xy ||
2962 freetype->matrix.yx != matrix.yx) {
2963 freetype->matrix = matrix;
2964 FT_Set_Transform(face, &freetype->matrix, nullptr);
2965 }
2966
2967 return face;
2968}
2969
2970void QFontEngineFT::unlockFace() const
2971{
2972 freetype->unlock();
2973}
2974
2975FT_Face QFontEngineFT::non_locked_face() const
2976{
2977 return freetype->face;
2978}
2979
2980
2981QFontEngineFT::QGlyphSet::QGlyphSet()
2982 : outline_drawing(false)
2983{
2984 transformationMatrix.xx = 0x10000;
2985 transformationMatrix.yy = 0x10000;
2986 transformationMatrix.xy = 0;
2987 transformationMatrix.yx = 0;
2988 memset(fast_glyph_data, 0, sizeof(fast_glyph_data));
2989 fast_glyph_count = 0;
2990}
2991
2992QFontEngineFT::QGlyphSet::~QGlyphSet()
2993{
2994 clear();
2995}
2996
2997void QFontEngineFT::QGlyphSet::clear()
2998{
2999 if (fast_glyph_count > 0) {
3000 for (int i = 0; i < 256; ++i) {
3001 if (fast_glyph_data[i]) {
3002 delete fast_glyph_data[i];
3003 fast_glyph_data[i] = nullptr;
3004 }
3005 }
3006 fast_glyph_count = 0;
3007 }
3008 qDeleteAll(glyph_data);
3009 glyph_data.clear();
3010}
3011
3012void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index,
3013 const QFixedPoint &subPixelPosition)
3014{
3015 if (useFastGlyphData(index, subPixelPosition)) {
3016 if (fast_glyph_data[index]) {
3017 delete fast_glyph_data[index];
3018 fast_glyph_data[index] = nullptr;
3019 if (fast_glyph_count > 0)
3020 --fast_glyph_count;
3021 }
3022 } else {
3023 delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition));
3024 }
3025}
3026
3027void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index,
3028 const QFixedPoint &subPixelPosition,
3029 Glyph *glyph)
3030{
3031 if (useFastGlyphData(index, subPixelPosition)) {
3032 if (!fast_glyph_data[index])
3033 ++fast_glyph_count;
3034 fast_glyph_data[index] = glyph;
3035 } else {
3036 glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph);
3037 }
3038}
3039
3040int QFontEngineFT::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
3041{
3042 lockFace();
3043 bool hsubpixel = true;
3044 int vfactor = 1;
3045 int load_flags = loadFlags(nullptr, Format_A8, flags, hsubpixel, vfactor);
3046 int result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints);
3047 unlockFace();
3048 return result;
3049}
3050
3051bool QFontEngineFT::initFromFontEngine(const QFontEngineFT *fe)
3052{
3053 if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype))
3054 return false;
3055
3056 // Increase the reference of this QFreetypeFace since one more QFontEngineFT
3057 // will be using it
3058 freetype->ref.ref();
3059
3060 default_load_flags = fe->default_load_flags;
3061 default_hint_style = fe->default_hint_style;
3062 antialias = fe->antialias;
3063 transform = fe->transform;
3064 embolden = fe->embolden;
3065 obliquen = fe->obliquen;
3066 subpixelType = fe->subpixelType;
3067 lcdFilterType = fe->lcdFilterType;
3068 embeddedbitmap = fe->embeddedbitmap;
3069
3070 return true;
3071}
3072
3073QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize) const
3074{
3075 QFontDef fontDef(this->fontDef);
3076 fontDef.pixelSize = pixelSize;
3077 QFontEngineFT *fe = new QFontEngineFT(fontDef);
3078 if (!fe->initFromFontEngine(this)) {
3079 delete fe;
3080 return nullptr;
3081 } else {
3082 return fe;
3083 }
3084}
3085
3086Qt::HANDLE QFontEngineFT::handle() const
3087{
3088 return non_locked_face();
3089}
3090
3091QList<QFontVariableAxis> QFontEngineFT::variableAxes() const
3092{
3093 return freetype->variableAxes();
3094}
3095
3096QT_END_NAMESPACE
3097
3098#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)