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