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