Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
4#include "qdir.h"
5#include "qmetatype.h"
6#include "qtextstream.h"
7#include "qvariant.h"
8#include "qfontengine_ft_p.h"
9#include "private/qimage_p.h"
10#include <private/qstringiterator_p.h>
11#include <qguiapplication.h>
12#include <qscreen.h>
13#include <qpa/qplatformscreen.h>
14#include <QtCore/QUuid>
15#include <QtCore/QLoggingCategory>
16#include <QtGui/QPainterPath>
17
18#ifndef QT_NO_FREETYPE
19
20#include "qfile.h"
21#include "qfileinfo.h"
23#include "qthreadstorage.h"
24#include <qmath.h>
25#include <qendian.h>
26
27#include <memory>
28
29#include <ft2build.h>
30#include FT_FREETYPE_H
31#include FT_OUTLINE_H
32#include FT_SYNTHESIS_H
33#include FT_TRUETYPE_TABLES_H
34#include FT_TYPE1_TABLES_H
35#include FT_GLYPH_H
36#include FT_MODULE_H
37#include FT_LCD_FILTER_H
38#include FT_MULTIPLE_MASTERS_H
39
40#if defined(FT_CONFIG_OPTIONS_H)
41#include FT_CONFIG_OPTIONS_H
42#endif
43
44#if defined(FT_FONT_FORMATS_H)
45#include FT_FONT_FORMATS_H
46#endif
47
48#ifdef QT_LINUXBASE
49#include FT_ERRORS_H
50#endif
51
52#if !defined(QT_MAX_CACHED_GLYPH_SIZE)
53# define QT_MAX_CACHED_GLYPH_SIZE 64
54#endif
55
57
59
60using namespace Qt::StringLiterals;
61
62#define FLOOR(x) ((x) & -64)
63#define CEIL(x) (((x)+63) & -64)
64#define TRUNC(x) ((x) >> 6)
65#define ROUND(x) (((x)+32) & -64)
66
68{
69 FT_Face face = (FT_Face)user_data;
70
71 bool result = false;
72 if (FT_IS_SFNT(face)) {
73 FT_ULong len = *length;
74 result = FT_Load_Sfnt_Table(face, tag, 0, buffer, &len) == FT_Err_Ok;
75 *length = len;
76 Q_ASSERT(!result || int(*length) > 0);
77 }
78
79 return result;
80}
81
83
85#ifdef Q_OS_WIN
87#else
89#endif
90
91// -------------------------- Freetype support ------------------------------
92
94{
95public:
100
110
111 FT_Library library;
112 QHash<QFontEngine::FaceId, QFreetypeFace *> faces;
113 QHash<FaceStyle, int> faceIndices;
114};
115
117{
118 for (auto iter = faces.cbegin(); iter != faces.cend(); ++iter) {
119 iter.value()->cleanup();
120 if (!iter.value()->ref.deref())
121 delete iter.value();
122 }
123 faces.clear();
124 FT_Done_FreeType(library);
125 library = nullptr;
126}
127
128inline bool operator==(const QtFreetypeData::FaceStyle &style1, const QtFreetypeData::FaceStyle &style2)
129{
130 return style1.faceFileName == style2.faceFileName && style1.styleName == style2.styleName;
131}
132
133inline size_t qHash(const QtFreetypeData::FaceStyle &style, size_t seed)
134{
135 return qHashMulti(seed, style.faceFileName, style.styleName);
136}
137
138Q_GLOBAL_STATIC(QThreadStorage<QtFreetypeData *>, theFreetypeData)
139
141{
142 QtFreetypeData *&freetypeData = theFreetypeData()->localData();
143 if (!freetypeData)
144 freetypeData = new QtFreetypeData;
145 if (!freetypeData->library) {
146 FT_Init_FreeType(&freetypeData->library);
147#if defined(FT_FONT_FORMATS_H)
148 // Freetype defaults to disabling stem-darkening on CFF, we re-enable it.
149 FT_Bool no_darkening = false;
150 FT_Property_Set(freetypeData->library, "cff", "no-stem-darkening", &no_darkening);
151#endif
152 }
153 return freetypeData;
154}
155
156FT_Library qt_getFreetype()
157{
158 QtFreetypeData *freetypeData = qt_getFreetypeData();
159 Q_ASSERT(freetypeData->library);
160 return freetypeData->library;
161}
162
164{
165 int fsType = 0;
166 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
167 if (os2)
168 fsType = os2->fsType;
169 return fsType;
170}
171
172int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
173{
174 if (int error = FT_Load_Glyph(face, glyph, flags))
175 return error;
176
177 if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
179
180 *nPoints = face->glyph->outline.n_points;
181 if (!(*nPoints))
182 return Err_Ok;
183
184 if (point > *nPoints)
186
187 *xpos = QFixed::fromFixed(face->glyph->outline.points[point].x);
188 *ypos = QFixed::fromFixed(face->glyph->outline.points[point].y);
189
190 return Err_Ok;
191}
192
194{
195 return FT_IS_SCALABLE(face);
196}
197
199{
200#ifdef FT_HAS_COLOR
201 return !isScalable() && FT_HAS_COLOR(face);
202#else
203 return false;
204#endif
205}
206
208
209/*
210 * One font file can contain more than one font (bold/italic for example)
211 * find the right one and return it.
212 *
213 * Returns the freetype face or 0 in case of an empty file or any other problems
214 * (like not being able to open the file)
215 */
217 const QByteArray &fontData)
218{
219 if (face_id.filename.isEmpty() && fontData.isEmpty())
220 return nullptr;
221
222 QtFreetypeData *freetypeData = qt_getFreetypeData();
223
224 QFreetypeFace *freetype = nullptr;
225 auto it = freetypeData->faces.find(face_id);
226 if (it != freetypeData->faces.end()) {
227 freetype = *it;
228
229 Q_ASSERT(freetype->ref.loadRelaxed() > 0);
230 if (freetype->ref.loadRelaxed() == 1) {
231 // If there is only one reference left to the face, it means it is only referenced by
232 // the cache itself, and thus it is in cleanup state (but the final outside reference
233 // was removed on a different thread so it could not be deleted right away). We then
234 // complete the cleanup and pretend we didn't find it, so that it can be re-created with
235 // the present state.
236 freetype->cleanup();
237 freetypeData->faces.erase(it);
238 delete freetype;
239 freetype = nullptr;
240 } else {
241 freetype->ref.ref();
242 }
243 }
244
245 if (!freetype) {
246 const auto deleter = [](QFreetypeFace *f) { delete f; };
247 std::unique_ptr<QFreetypeFace, decltype(deleter)> newFreetype(new QFreetypeFace, deleter);
248 FT_Face face;
249 if (!face_id.filename.isEmpty()) {
251 if (const char *prefix = ":qmemoryfonts/"; face_id.filename.startsWith(prefix)) {
252 // from qfontdatabase.cpp
253 QByteArray idx = face_id.filename;
254 idx.remove(0, strlen(prefix)); // remove ':qmemoryfonts/'
255 bool ok = false;
256 newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok));
257 if (!ok)
258 newFreetype->fontData = QByteArray();
259 } else if (!QFileInfo(fileName).isNativePath()) {
262 return nullptr;
263 }
264 newFreetype->fontData = file.readAll();
265 }
266 } else {
267 newFreetype->fontData = fontData;
268 }
269 if (!newFreetype->fontData.isEmpty()) {
270 if (FT_New_Memory_Face(freetypeData->library, (const FT_Byte *)newFreetype->fontData.constData(), newFreetype->fontData.size(), face_id.index, &face)) {
271 return nullptr;
272 }
273 } else if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face)) {
274 return nullptr;
275 }
276
277#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
278 if (face_id.instanceIndex >= 0) {
279 qCDebug(lcFontMatch)
280 << "Selecting named instance" << (face_id.instanceIndex)
281 << "in" << face_id.filename;
282 FT_Set_Named_Instance(face, face_id.instanceIndex + 1);
283 }
284#endif
285 newFreetype->face = face;
286 newFreetype->mm_var = nullptr;
287 if (FT_IS_NAMED_INSTANCE(newFreetype->face)) {
288 FT_Error ftresult;
289 ftresult = FT_Get_MM_Var(face, &newFreetype->mm_var);
290 if (ftresult != FT_Err_Ok)
291 newFreetype->mm_var = nullptr;
292 }
293
294 newFreetype->ref.storeRelaxed(1);
295 newFreetype->xsize = 0;
296 newFreetype->ysize = 0;
297 newFreetype->matrix.xx = 0x10000;
298 newFreetype->matrix.yy = 0x10000;
299 newFreetype->matrix.xy = 0;
300 newFreetype->matrix.yx = 0;
301 newFreetype->unicode_map = nullptr;
302 newFreetype->symbol_map = nullptr;
303
304 memset(newFreetype->cmapCache, 0, sizeof(newFreetype->cmapCache));
305
306 for (int i = 0; i < newFreetype->face->num_charmaps; ++i) {
307 FT_CharMap cm = newFreetype->face->charmaps[i];
308 switch(cm->encoding) {
309 case FT_ENCODING_UNICODE:
310 newFreetype->unicode_map = cm;
311 break;
312 case FT_ENCODING_APPLE_ROMAN:
313 case FT_ENCODING_ADOBE_LATIN_1:
314 if (!newFreetype->unicode_map || newFreetype->unicode_map->encoding != FT_ENCODING_UNICODE)
315 newFreetype->unicode_map = cm;
316 break;
317 case FT_ENCODING_ADOBE_CUSTOM:
318 case FT_ENCODING_MS_SYMBOL:
319 if (!newFreetype->symbol_map)
320 newFreetype->symbol_map = cm;
321 break;
322 default:
323 break;
324 }
325 }
326
327 if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1)
328 FT_Set_Char_Size(face, newFreetype->face->available_sizes[0].x_ppem, newFreetype->face->available_sizes[0].y_ppem, 0, 0);
329
330 FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map);
331
332 if (!face_id.variableAxes.isEmpty()) {
333 FT_MM_Var *var = nullptr;
334 FT_Get_MM_Var(newFreetype->face, &var);
335 if (var != nullptr) {
336 QVarLengthArray<FT_Fixed, 16> coords(var->num_axis);
337 FT_Get_Var_Design_Coordinates(face, var->num_axis, coords.data());
338 for (FT_UInt i = 0; i < var->num_axis; ++i) {
339 if (const auto tag = QFont::Tag::fromValue(var->axis[i].tag)) {
340 const auto it = face_id.variableAxes.constFind(*tag);
341 if (it != face_id.variableAxes.constEnd())
342 coords[i] = FT_Fixed(*it * 65536);
343 }
344 }
345 FT_Set_Var_Design_Coordinates(face, var->num_axis, coords.data());
346 FT_Done_MM_Var(qt_getFreetype(), var);
347 }
348 }
349
350 QT_TRY {
351 freetypeData->faces.insert(face_id, newFreetype.get());
352 } QT_CATCH(...) {
353 newFreetype.release()->release(face_id);
354 // we could return null in principle instead of throwing
356 }
357 freetype = newFreetype.release();
358 freetype->ref.ref();
359 }
360 return freetype;
361}
362
363void QFreetypeFace::cleanup()
364{
365 hbFace.reset();
366 if (mm_var && face && face->glyph)
367 FT_Done_MM_Var(face->glyph->library, mm_var);
368 mm_var = nullptr;
369 FT_Done_Face(face);
370 face = nullptr;
371}
372
374{
375 Q_UNUSED(face_id);
376 bool deleteThis = !ref.deref();
377
378 // If the only reference left over is the cache's reference, we remove it from the cache,
379 // granted that we are on the correct thread. If not, we leave it there to be cleaned out
380 // later. While we are at it, we also purge all left over faces which are only referenced
381 // from the cache.
382 if (face && ref.loadRelaxed() == 1) {
383 QtFreetypeData *freetypeData = qt_getFreetypeData();
384 for (auto it = freetypeData->faces.constBegin(); it != freetypeData->faces.constEnd(); ) {
385 if (it.value()->ref.loadRelaxed() == 1) {
386 it.value()->cleanup();
387 if (it.value() == this)
388 deleteThis = true; // This face, delete at end of function for safety
389 else
390 delete it.value();
391 it = freetypeData->faces.erase(it);
392 } else {
393 ++it;
394 }
395 }
396
397 if (freetypeData->faces.isEmpty()) {
398 FT_Done_FreeType(freetypeData->library);
399 freetypeData->library = nullptr;
400 }
401 }
402
403 if (deleteThis)
404 delete this;
405}
406
407static int computeFaceIndex(const QString &faceFileName, const QString &styleName)
408{
409 FT_Library library = qt_getFreetype();
410
411 int faceIndex = 0;
412 int numFaces = 0;
413
414 do {
415 FT_Face face;
416
417 FT_Error error = FT_New_Face(library, faceFileName.toUtf8().constData(), faceIndex, &face);
418 if (error != FT_Err_Ok) {
419 qDebug() << "FT_New_Face failed for face index" << faceIndex << ':' << Qt::hex << error;
420 break;
421 }
422
423 const bool found = QLatin1StringView(face->style_name) == styleName;
424 numFaces = face->num_faces;
425
426 FT_Done_Face(face);
427
428 if (found)
429 return faceIndex;
430 } while (++faceIndex < numFaces);
431
432 // Fall back to the first font face in the file
433 return 0;
434}
435
436int QFreetypeFace::getFaceIndexByStyleName(const QString &faceFileName, const QString &styleName)
437{
438 QtFreetypeData *freetypeData = qt_getFreetypeData();
439
440 // Try to get from cache
441 QtFreetypeData::FaceStyle faceStyle(faceFileName, styleName);
442 int faceIndex = freetypeData->faceIndices.value(faceStyle, -1);
443
444 if (faceIndex >= 0)
445 return faceIndex;
446
447 faceIndex = computeFaceIndex(faceFileName, styleName);
448
449 freetypeData->faceIndices.insert(faceStyle, faceIndex);
450
451 return faceIndex;
452}
453
454void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor)
455{
456 *ysize = qRound(fontDef.pixelSize * 64);
457 *xsize = *ysize * fontDef.stretch / 100;
458 *scalableBitmapScaleFactor = 1;
459 *outline_drawing = false;
460
461 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) {
462 int best = 0;
463 if (!isScalableBitmap()) {
464 /*
465 * Bitmap only faces must match exactly, so find the closest
466 * one (height dominant search)
467 */
468 for (int i = 1; i < face->num_fixed_sizes; i++) {
469 if (qAbs(*ysize - face->available_sizes[i].y_ppem) <
470 qAbs(*ysize - face->available_sizes[best].y_ppem) ||
471 (qAbs(*ysize - face->available_sizes[i].y_ppem) ==
472 qAbs(*ysize - face->available_sizes[best].y_ppem) &&
473 qAbs(*xsize - face->available_sizes[i].x_ppem) <
474 qAbs(*xsize - face->available_sizes[best].x_ppem))) {
475 best = i;
476 }
477 }
478 } else {
479 // Select the shortest bitmap strike whose height is larger than the desired height
480 for (int i = 1; i < face->num_fixed_sizes; i++) {
481 if (face->available_sizes[i].y_ppem < *ysize) {
482 if (face->available_sizes[i].y_ppem > face->available_sizes[best].y_ppem)
483 best = i;
484 } else if (face->available_sizes[best].y_ppem < *ysize) {
485 best = i;
486 } else if (face->available_sizes[i].y_ppem < face->available_sizes[best].y_ppem) {
487 best = i;
488 }
489 }
490 }
491
492 // According to freetype documentation we must use FT_Select_Size
493 // to make sure we can select the desired bitmap strike index
494 if (FT_Select_Size(face, best) == 0) {
495 if (isScalableBitmap())
496 *scalableBitmapScaleFactor = QFixed::fromReal((qreal)fontDef.pixelSize / face->available_sizes[best].height);
497 *xsize = face->available_sizes[best].x_ppem;
498 *ysize = face->available_sizes[best].y_ppem;
499 } else {
500 *xsize = *ysize = 0;
501 }
502 } else {
503 *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6));
504 }
505}
506
508{
510 p.postscriptName = FT_Get_Postscript_Name(face);
511 PS_FontInfoRec font_info;
512 if (FT_Get_PS_Font_Info(face, &font_info) == 0)
513 p.copyright = font_info.notice;
514 if (FT_IS_SCALABLE(face)) {
515 p.ascent = face->ascender;
516 p.descent = -face->descender;
517 p.leading = face->height - face->ascender + face->descender;
518 p.emSquare = face->units_per_EM;
519 p.boundingBox = QRectF(face->bbox.xMin, -face->bbox.yMax,
520 face->bbox.xMax - face->bbox.xMin,
521 face->bbox.yMax - face->bbox.yMin);
522 } else {
523 p.ascent = QFixed::fromFixed(face->size->metrics.ascender);
524 p.descent = QFixed::fromFixed(-face->size->metrics.descender);
525 p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender);
526 p.emSquare = face->size->metrics.y_ppem;
527// p.boundingBox = QRectF(-p.ascent.toReal(), 0, (p.ascent + p.descent).toReal(), face->size->metrics.max_advance/64.);
528 p.boundingBox = QRectF(0, -p.ascent.toReal(),
529 face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() );
530 }
531 p.italicAngle = 0;
532 p.capHeight = p.ascent;
533 p.lineWidth = face->underline_thickness;
534
535 return p;
536}
537
542
543/* Some fonts (such as MingLiu rely on hinting to scale different
544 components to their correct sizes. While this is really broken (it
545 should be done in the component glyph itself, not the hinter) we
546 will have to live with it.
547
548 This means we can not use FT_LOAD_NO_HINTING to get the glyph
549 outline. All we can do is to load the unscaled glyph and scale it
550 down manually when required.
551*/
552static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale)
553{
554 x_scale = FT_MulDiv(x_scale, 1 << 10, face->units_per_EM);
555 y_scale = FT_MulDiv(y_scale, 1 << 10, face->units_per_EM);
556 FT_Vector *p = g->outline.points;
557 const FT_Vector *e = p + g->outline.n_points;
558 while (p < e) {
559 p->x = FT_MulFix(p->x, x_scale);
560 p->y = FT_MulFix(p->y, y_scale);
561 ++p;
562 }
563}
564
565#define GLYPH2PATH_DEBUG QT_NO_QDEBUG_MACRO // qDebug
566void QFreetypeFace::addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale)
567{
568 const qreal factor = 1/64.;
569 scaleOutline(face, g, x_scale, y_scale);
570
571 QPointF cp = point.toPointF();
572
573 // convert the outline to a painter path
574 int i = 0;
575 for (int j = 0; j < g->outline.n_contours; ++j) {
576 int last_point = g->outline.contours[j];
577 GLYPH2PATH_DEBUG() << "contour:" << i << "to" << last_point;
578 QPointF start = QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
579 if (!(g->outline.tags[i] & 1)) { // start point is not on curve:
580 if (!(g->outline.tags[last_point] & 1)) { // end point is not on curve:
581 GLYPH2PATH_DEBUG() << " start and end point are not on curve";
582 start = (QPointF(g->outline.points[last_point].x*factor,
583 -g->outline.points[last_point].y*factor) + start) / 2.0;
584 } else {
585 GLYPH2PATH_DEBUG() << " end point is on curve, start is not";
586 start = QPointF(g->outline.points[last_point].x*factor,
587 -g->outline.points[last_point].y*factor);
588 }
589 --i; // to use original start point as control point below
590 }
591 start += cp;
592 GLYPH2PATH_DEBUG() << " start at" << start;
593
594 path->moveTo(start);
595 QPointF c[4];
596 c[0] = start;
597 int n = 1;
598 while (i < last_point) {
599 ++i;
600 c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
601 GLYPH2PATH_DEBUG() << " " << i << c[n] << "tag =" << (int)g->outline.tags[i]
602 << ": on curve =" << (bool)(g->outline.tags[i] & 1);
603 ++n;
604 switch (g->outline.tags[i] & 3) {
605 case 2:
606 // cubic bezier element
607 if (n < 4)
608 continue;
609 c[3] = (c[3] + c[2])/2;
610 --i;
611 break;
612 case 0:
613 // quadratic bezier element
614 if (n < 3)
615 continue;
616 c[3] = (c[1] + c[2])/2;
617 c[2] = (2*c[1] + c[3])/3;
618 c[1] = (2*c[1] + c[0])/3;
619 --i;
620 break;
621 case 1:
622 case 3:
623 if (n == 2) {
624 GLYPH2PATH_DEBUG() << " lineTo" << c[1];
625 path->lineTo(c[1]);
626 c[0] = c[1];
627 n = 1;
628 continue;
629 } else if (n == 3) {
630 c[3] = c[2];
631 c[2] = (2*c[1] + c[3])/3;
632 c[1] = (2*c[1] + c[0])/3;
633 }
634 break;
635 }
636 GLYPH2PATH_DEBUG() << " cubicTo" << c[1] << c[2] << c[3];
637 path->cubicTo(c[1], c[2], c[3]);
638 c[0] = c[3];
639 n = 1;
640 }
641
642 if (n == 1) {
643 GLYPH2PATH_DEBUG() << " closeSubpath";
644 path->closeSubpath();
645 } else {
646 c[3] = start;
647 if (n == 2) {
648 c[2] = (2*c[1] + c[3])/3;
649 c[1] = (2*c[1] + c[0])/3;
650 }
651 GLYPH2PATH_DEBUG() << " close cubicTo" << c[1] << c[2] << c[3];
652 path->cubicTo(c[1], c[2], c[3]);
653 }
654 ++i;
655 }
656}
657
658extern void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path);
659
660void QFreetypeFace::addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path)
661{
662 if (slot->format != FT_GLYPH_FORMAT_BITMAP
663 || slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO)
664 return;
665
666 QPointF cp = point.toPointF();
667 qt_addBitmapToPath(cp.x() + TRUNC(slot->metrics.horiBearingX), cp.y() - TRUNC(slot->metrics.horiBearingY),
668 slot->bitmap.buffer, slot->bitmap.pitch, slot->bitmap.width, slot->bitmap.rows, path);
669}
670
671static inline void convertRGBToARGB(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
672{
673 const int offs = bgr ? -1 : 1;
674 const int w = width * 3;
675 while (height--) {
676 uint *dd = dst;
677 for (int x = 0; x < w; x += 3) {
678 uchar red = src[x + 1 - offs];
679 uchar green = src[x + 1];
680 uchar blue = src[x + 1 + offs];
681 *dd++ = (0xFFU << 24) | (red << 16) | (green << 8) | blue;
682 }
683 dst += width;
684 src += src_pitch;
685 }
686}
687
688static inline void convertRGBToARGB_V(const uchar *src, uint *dst, int width, int height, int src_pitch, bool bgr)
689{
690 const int offs = bgr ? -src_pitch : src_pitch;
691 while (height--) {
692 for (int x = 0; x < width; x++) {
693 uchar red = src[x + src_pitch - offs];
694 uchar green = src[x + src_pitch];
695 uchar blue = src[x + src_pitch + offs];
696 *dst++ = (0XFFU << 24) | (red << 16) | (green << 8) | blue;
697 }
698 src += 3*src_pitch;
699 }
700}
701
711
713{
714 auto engine = std::make_unique<QFontEngineFT>(fontDef);
715
718
719 if (antialias) {
723 engine->subpixelType = QFontEngine::Subpixel_None;
724 } else {
726 engine->subpixelType = subpixelType;
727 }
728 }
729
730 if (!engine->init(faceId, antialias, format, fontData) || engine->invalid()) {
731 qWarning("QFontEngineFT: Failed to create FreeType font engine");
732 return nullptr;
733 }
734
735 engine->setQtDefaultHintStyle(static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
736 return engine.release();
737}
738
739namespace {
740 class QFontEngineFTRawData: public QFontEngineFT
741 {
742 public:
743 QFontEngineFTRawData(const QFontDef &fontDef) : QFontEngineFT(fontDef)
744 {
745 }
746
747 void updateFamilyNameAndStyle()
748 {
749 fontDef.families = QStringList(QString::fromLatin1(freetype->face->family_name));
750
751 if (freetype->face->style_flags & FT_STYLE_FLAG_ITALIC)
752 fontDef.style = QFont::StyleItalic;
753
754 if (freetype->face->style_flags & FT_STYLE_FLAG_BOLD)
755 fontDef.weight = QFont::Bold;
756 }
757
758 bool initFromData(const QByteArray &fontData, const QMap<QFont::Tag, float> &variableAxisValues)
759 {
760 FaceId faceId;
761 faceId.filename = "";
762 faceId.index = 0;
763 faceId.uuid = QUuid::createUuid().toByteArray();
764 faceId.variableAxes = variableAxisValues;
765
766 return init(faceId, true, Format_None, fontData);
767 }
768 };
769}
770
772 qreal pixelSize,
773 QFont::HintingPreference hintingPreference,
774 const QMap<QFont::Tag, float> &variableAxisValues)
775{
777 fontDef.pixelSize = pixelSize;
779 fontDef.hintingPreference = hintingPreference;
780 fontDef.variableAxisValues = variableAxisValues;
781
782 QFontEngineFTRawData *fe = new QFontEngineFTRawData(fontDef);
783 if (!fe->initFromData(fontData, variableAxisValues)) {
784 delete fe;
785 return nullptr;
786 }
787
788 fe->updateFamilyNameAndStyle();
789 fe->setQtDefaultHintStyle(static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
790
791 return fe;
792}
793
795 : QFontEngine(Freetype)
796{
797 fontDef = fd;
798 matrix.xx = 0x10000;
799 matrix.yy = 0x10000;
800 matrix.xy = 0;
801 matrix.yx = 0;
802 cache_cost = 100 * 1024;
803 kerning_pairs_loaded = false;
804 transform = false;
805 embolden = false;
806 obliquen = false;
807 antialias = true;
808 freetype = nullptr;
809 default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
812 lcdFilterType = (int)((quintptr) FT_LCD_FILTER_DEFAULT);
813 defaultFormat = Format_None;
814 embeddedbitmap = false;
815 const QByteArray env = qgetenv("QT_NO_FT_CACHE");
816 cacheEnabled = env.isEmpty() || env.toInt() == 0;
818 forceAutoHint = false;
819 stemDarkeningDriver = false;
820}
821
823{
824 if (freetype)
825 freetype->release(face_id);
826}
827
833
834static void dont_delete(void*) {}
835
836static FT_UShort calculateActualWeight(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId)
837{
838 FT_MM_Var *var = freetypeFace->mm_var;
839 if (var != nullptr && faceId.instanceIndex >= 0 && FT_UInt(faceId.instanceIndex) < var->num_namedstyles) {
840 for (FT_UInt axis = 0; axis < var->num_axis; ++axis) {
841 if (var->axis[axis].tag == QFont::Tag("wght").value()) {
842 return var->namedstyle[faceId.instanceIndex].coords[axis] >> 16;
843 }
844 }
845 }
846 if (const TT_OS2 *os2 = reinterpret_cast<const TT_OS2 *>(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) {
847 return os2->usWeightClass;
848 }
849
850 return 700;
851}
852
853static bool calculateActualItalic(QFreetypeFace *freetypeFace, FT_Face face, QFontEngine::FaceId faceId)
854{
855 FT_MM_Var *var = freetypeFace->mm_var;
856 if (var != nullptr && faceId.instanceIndex >= 0 && FT_UInt(faceId.instanceIndex) < var->num_namedstyles) {
857 for (FT_UInt axis = 0; axis < var->num_axis; ++axis) {
858 if (var->axis[axis].tag == QFont::Tag("ital").value()) {
859 return (var->namedstyle[faceId.instanceIndex].coords[axis] >> 16) == 1;
860 }
861 }
862 }
863
864 return (face->style_flags & FT_STYLE_FLAG_ITALIC);
865}
866
867bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
868 QFreetypeFace *freetypeFace)
869{
870 freetype = freetypeFace;
871 if (!freetype) {
872 xsize = 0;
873 ysize = 0;
874 return false;
875 }
876 defaultFormat = format;
877 this->antialias = antialias;
878
879 if (!antialias)
881 else
882 glyphFormat = defaultFormat;
883
884 face_id = faceId;
885
886 symbol = freetype->symbol_map != nullptr;
887 PS_FontInfoRec psrec;
888 // don't assume that type1 fonts are symbol fonts by default
889 if (FT_Get_PS_Font_Info(freetype->face, &psrec) == FT_Err_Ok) {
890 symbol = !fontDef.families.isEmpty() && bool(fontDef.families.constFirst().contains("symbol"_L1, Qt::CaseInsensitive));
891 }
892
893 freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing, &scalableBitmapScaleFactor);
894
895 FT_Face face = lockFace();
896
897 if (FT_IS_SCALABLE(face)) {
898 bool isItalic = calculateActualItalic(freetype, face, faceId);
899 bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !isItalic && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_ITALIC");
900 if (fake_oblique)
901 obliquen = true;
902 FT_Set_Transform(face, &matrix, nullptr);
904 // fake bold
905 if ((fontDef.weight >= QFont::Bold) && !(face->style_flags & FT_STYLE_FLAG_BOLD) && !FT_IS_FIXED_WIDTH(face) && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD")) {
906 FT_UShort actualWeight = calculateActualWeight(freetype, face, faceId);
907 if (actualWeight < 700 &&
908 (fontDef.pixelSize < 64 || qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_BOLD_LIMIT"))) {
909 embolden = true;
910 }
911 }
912 // underline metrics
913 line_thickness = QFixed::fromFixed(FT_MulFix(face->underline_thickness, face->size->metrics.y_scale));
914 QFixed center_position = QFixed::fromFixed(-FT_MulFix(face->underline_position, face->size->metrics.y_scale));
915 underline_position = center_position - line_thickness / 2;
916 } else {
917 // ad hoc algorithm
918 int score = fontDef.weight * fontDef.pixelSize;
919 line_thickness = score / 7000;
920 // looks better with thicker line for small pointsizes
921 if (line_thickness < 2 && score >= 1050)
922 line_thickness = 2;
923 underline_position = ((line_thickness * 2) + 3) / 6;
924
925 cacheEnabled = false;
926 if (isScalableBitmap())
927 glyphFormat = defaultFormat = GlyphFormat::Format_ARGB;
928 }
929 if (line_thickness < 1)
930 line_thickness = 1;
931
932 metrics = face->size->metrics;
933
934 /*
935 TrueType fonts with embedded bitmaps may have a bitmap font specific
936 ascent/descent in the EBLC table. There is no direct public API
937 to extract those values. The only way we've found is to trick freetype
938 into thinking that it's not a scalable font in FT_Select_Size so that
939 the metrics are retrieved from the bitmap strikes.
940 */
941 if (FT_IS_SCALABLE(face)) {
942 for (int i = 0; i < face->num_fixed_sizes; ++i) {
943 if (xsize == face->available_sizes[i].x_ppem && ysize == face->available_sizes[i].y_ppem) {
944 face->face_flags &= ~FT_FACE_FLAG_SCALABLE;
945
946 FT_Select_Size(face, i);
947 if (face->size->metrics.ascender + face->size->metrics.descender > 0) {
948 FT_Pos leading = metrics.height - metrics.ascender + metrics.descender;
949 metrics.ascender = face->size->metrics.ascender;
950 metrics.descender = face->size->metrics.descender;
951 if (metrics.descender > 0
952 && QString::fromUtf8(face->family_name) == "Courier New"_L1) {
953 metrics.descender *= -1;
954 }
955 metrics.height = metrics.ascender - metrics.descender + leading;
956 }
957 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
958
959 face->face_flags |= FT_FACE_FLAG_SCALABLE;
960 break;
961 }
962 }
963 }
964#if defined(FT_FONT_FORMATS_H)
965 const char *fmt = FT_Get_Font_Format(face);
966 if (fmt && qstrncmp(fmt, "CFF", 4) == 0) {
967 FT_Bool no_stem_darkening = true;
968 FT_Error err = FT_Property_Get(qt_getFreetype(), "cff", "no-stem-darkening", &no_stem_darkening);
969 if (err == FT_Err_Ok)
970 stemDarkeningDriver = !no_stem_darkening;
971 else
972 stemDarkeningDriver = false;
973 }
974#endif
975
977
978 if (!freetype->hbFace) {
979 faceData.user_data = face;
980 faceData.get_font_table = ft_getSfntTable;
981 (void)harfbuzzFace(); // populates face_
982 freetype->hbFace = std::move(face_);
983 } else {
984 Q_ASSERT(!face_);
985 }
986 // we share the HB face in QFreeTypeFace, so do not let ~QFontEngine() destroy it
987 face_ = Holder(freetype->hbFace.get(), dont_delete);
988
989 unlockFace();
990
992 return true;
993}
994
996{
997 switch (hintingPreference) {
1000 break;
1003 break;
1006 break;
1009 break;
1010 }
1011}
1012
1017
1022
1023int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags,
1024 bool &hsubpixel, int &vfactor) const
1025{
1026 int load_flags = FT_LOAD_DEFAULT | default_load_flags;
1027 int load_target = default_hint_style == HintLight
1028 ? FT_LOAD_TARGET_LIGHT
1029 : FT_LOAD_TARGET_NORMAL;
1030
1031 if (format == Format_Mono) {
1032 load_target = FT_LOAD_TARGET_MONO;
1033 } else if (format == Format_A32) {
1035 hsubpixel = true;
1037 vfactor = 3;
1038 } else if (format == Format_ARGB) {
1039#ifdef FT_LOAD_COLOR
1040 load_flags |= FT_LOAD_COLOR;
1041#endif
1042 }
1043
1044 if (set && set->outline_drawing)
1045 load_flags |= FT_LOAD_NO_BITMAP;
1046
1047 if (default_hint_style == HintNone || (flags & DesignMetrics) || (set && set->outline_drawing))
1048 load_flags |= FT_LOAD_NO_HINTING;
1049 else
1050 load_flags |= load_target;
1051
1052 if (forceAutoHint)
1053 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1054
1055 return load_flags;
1056}
1057
1059{
1060 // false if exceeds QFontEngineFT::Glyph metrics
1061 return info.width > 0xFF || info.height > 0xFF || info.linearAdvance > 0x7FFF;
1062}
1063
1064static inline void transformBoundingBox(int *left, int *top, int *right, int *bottom, FT_Matrix *matrix)
1065{
1066 int l, r, t, b;
1067 FT_Vector vector;
1068 vector.x = *left;
1069 vector.y = *top;
1070 FT_Vector_Transform(&vector, matrix);
1071 l = r = vector.x;
1072 t = b = vector.y;
1073 vector.x = *right;
1074 vector.y = *top;
1075 FT_Vector_Transform(&vector, matrix);
1076 if (l > vector.x) l = vector.x;
1077 if (r < vector.x) r = vector.x;
1078 if (t < vector.y) t = vector.y;
1079 if (b > vector.y) b = vector.y;
1080 vector.x = *right;
1081 vector.y = *bottom;
1082 FT_Vector_Transform(&vector, matrix);
1083 if (l > vector.x) l = vector.x;
1084 if (r < vector.x) r = vector.x;
1085 if (t < vector.y) t = vector.y;
1086 if (b > vector.y) b = vector.y;
1087 vector.x = *left;
1088 vector.y = *bottom;
1089 FT_Vector_Transform(&vector, matrix);
1090 if (l > vector.x) l = vector.x;
1091 if (r < vector.x) r = vector.x;
1092 if (t < vector.y) t = vector.y;
1093 if (b > vector.y) b = vector.y;
1094 *left = l;
1095 *right = r;
1096 *top = t;
1097 *bottom = b;
1098}
1099
1101 const QFixedPoint &subPixelPosition,
1103 bool fetchMetricsOnly,
1104 bool disableOutlineDrawing) const
1105{
1106// Q_ASSERT(freetype->lock == 1);
1107
1108 if (format == Format_None)
1109 format = defaultFormat != Format_None ? defaultFormat : Format_Mono;
1111
1112 Glyph *g = set ? set->getGlyph(glyph, subPixelPosition) : nullptr;
1113 if (g && g->format == format && (fetchMetricsOnly || g->data))
1114 return g;
1115
1116 if (!g && set && set->isGlyphMissing(glyph))
1117 return &emptyGlyph;
1118
1119
1120 FT_Face face = freetype->face;
1121
1122 FT_Matrix matrix = freetype->matrix;
1123
1124 FT_Vector v;
1125 v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.x.value());
1126 v.y = format == Format_Mono ? 0 : FT_Pos(-subPixelPosition.y.value());
1127 FT_Set_Transform(face, &matrix, &v);
1128
1129 bool hsubpixel = false;
1130 int vfactor = 1;
1131 int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor);
1132
1133 bool transform = matrix.xx != 0x10000
1134 || matrix.yy != 0x10000
1135 || matrix.xy != 0
1136 || matrix.yx != 0;
1137
1139 load_flags |= FT_LOAD_NO_BITMAP;
1140
1141 FT_Error err = FT_Load_Glyph(face, glyph, load_flags);
1142 if (err && (load_flags & FT_LOAD_NO_BITMAP)) {
1143 load_flags &= ~FT_LOAD_NO_BITMAP;
1144 err = FT_Load_Glyph(face, glyph, load_flags);
1145 }
1146 if (err == FT_Err_Too_Few_Arguments) {
1147 // this is an error in the bytecode interpreter, just try to run without it
1148 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1149 err = FT_Load_Glyph(face, glyph, load_flags);
1150 } else if (err == FT_Err_Execution_Too_Long) {
1151 // This is an error in the bytecode, probably a web font made by someone who
1152 // didn't test bytecode hinting at all so disable for it for all glyphs.
1153 qWarning("load glyph failed due to broken hinting bytecode in font, switching to auto hinting");
1154 default_load_flags |= FT_LOAD_FORCE_AUTOHINT;
1155 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1156 err = FT_Load_Glyph(face, glyph, load_flags);
1157 }
1158 if (err != FT_Err_Ok) {
1159 qWarning("load glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
1160 if (set)
1161 set->setGlyphMissing(glyph);
1162 return &emptyGlyph;
1163 }
1164
1165 FT_GlyphSlot slot = face->glyph;
1166
1167 if (embolden)
1168 FT_GlyphSlot_Embolden(slot);
1169 if (obliquen) {
1170 FT_GlyphSlot_Oblique(slot);
1171
1172 // While Embolden alters the metrics of the slot, oblique does not, so we need
1173 // to fix this ourselves.
1174 transform = true;
1175 FT_Matrix m;
1176 m.xx = 0x10000;
1177 m.yx = 0x0;
1178 m.xy = 0x6000;
1179 m.yy = 0x10000;
1180
1181 FT_Matrix_Multiply(&m, &matrix);
1182 }
1183
1185 info.linearAdvance = slot->linearHoriAdvance >> 10;
1186 info.xOff = TRUNC(ROUND(slot->advance.x));
1187 info.yOff = 0;
1188
1189 if ((set && set->outline_drawing && !disableOutlineDrawing) || fetchMetricsOnly) {
1190 int left = slot->metrics.horiBearingX;
1191 int right = slot->metrics.horiBearingX + slot->metrics.width;
1192 int top = slot->metrics.horiBearingY;
1193 int bottom = slot->metrics.horiBearingY - slot->metrics.height;
1194
1195 if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP)
1197
1198 left = FLOOR(left);
1199 right = CEIL(right);
1200 bottom = FLOOR(bottom);
1201 top = CEIL(top);
1202
1203 info.x = TRUNC(left);
1204 info.y = TRUNC(top);
1205 info.width = TRUNC(right - left);
1206 info.height = TRUNC(top - bottom);
1207
1208 // If any of the metrics are too large to fit, don't cache them
1209 // Also, avoid integer overflow when linearAdvance is to large to fit in a signed short
1211 return nullptr;
1212
1213 g = new Glyph;
1214 g->data = nullptr;
1215 g->linearAdvance = info.linearAdvance;
1216 g->width = info.width;
1217 g->height = info.height;
1218 g->x = info.x;
1219 g->y = info.y;
1220 g->advance = info.xOff;
1221 g->format = format;
1222
1223 if (set)
1224 set->setGlyph(glyph, subPixelPosition, g);
1225
1226 return g;
1227 }
1228
1229 int glyph_buffer_size = 0;
1230 std::unique_ptr<uchar[]> glyph_buffer;
1231 FT_Render_Mode renderMode = (default_hint_style == HintLight) ? FT_RENDER_MODE_LIGHT : FT_RENDER_MODE_NORMAL;
1232 switch (format) {
1233 case Format_Mono:
1234 renderMode = FT_RENDER_MODE_MONO;
1235 break;
1236 case Format_A32:
1237 if (!hsubpixel && vfactor == 1) {
1238 qWarning("Format_A32 requested, but subpixel layout is unknown.");
1239 return nullptr;
1240 }
1241
1242 renderMode = hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V;
1243 break;
1244 case Format_A8:
1245 case Format_ARGB:
1246 break;
1247 default:
1248 Q_UNREACHABLE();
1249 }
1250 FT_Library_SetLcdFilter(slot->library, (FT_LcdFilter)lcdFilterType);
1251
1252 err = FT_Render_Glyph(slot, renderMode);
1253 if (err != FT_Err_Ok)
1254 qWarning("render glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
1255
1256 FT_Library_SetLcdFilter(slot->library, FT_LCD_FILTER_NONE);
1257
1258 info.height = slot->bitmap.rows;
1259 info.width = slot->bitmap.width;
1260 info.x = slot->bitmap_left;
1261 info.y = slot->bitmap_top;
1262 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD)
1263 info.width = info.width / 3;
1264 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V)
1265 info.height = info.height / vfactor;
1266
1267 int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 :
1268 (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4));
1269
1270 glyph_buffer_size = info.height * pitch;
1271 glyph_buffer.reset(new uchar[glyph_buffer_size]);
1272
1273 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
1274 uchar *src = slot->bitmap.buffer;
1275 uchar *dst = glyph_buffer.get();
1276 int h = slot->bitmap.rows;
1277 // Some fonts return bitmaps even when we requested something else:
1278 if (format == Format_Mono) {
1279 int bytes = ((info.width + 7) & ~7) >> 3;
1280 while (h--) {
1281 memcpy (dst, src, bytes);
1282 dst += pitch;
1283 src += slot->bitmap.pitch;
1284 }
1285 } else if (format == Format_A8) {
1286 while (h--) {
1287 for (int x = 0; x < int{info.width}; x++)
1288 dst[x] = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00);
1289 dst += pitch;
1290 src += slot->bitmap.pitch;
1291 }
1292 } else {
1293 while (h--) {
1294 uint *dd = reinterpret_cast<uint *>(dst);
1295 for (int x = 0; x < int{info.width}; x++)
1296 dd[x] = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffffff : 0x00000000);
1297 dst += pitch;
1298 src += slot->bitmap.pitch;
1299 }
1300 }
1301 } else if (slot->bitmap.pixel_mode == 7 /*FT_PIXEL_MODE_BGRA*/) {
1303 uchar *src = slot->bitmap.buffer;
1304 uchar *dst = glyph_buffer.get();
1305 int h = slot->bitmap.rows;
1306 while (h--) {
1307#if Q_BYTE_ORDER == Q_BIG_ENDIAN
1308 const quint32 *srcPixel = (const quint32 *)src;
1309 quint32 *dstPixel = (quint32 *)dst;
1310 for (int x = 0; x < static_cast<int>(slot->bitmap.width); x++, srcPixel++, dstPixel++) {
1311 const quint32 pixel = *srcPixel;
1312 *dstPixel = qbswap(pixel);
1313 }
1314#else
1315 memcpy(dst, src, slot->bitmap.width * 4);
1316#endif
1317 dst += slot->bitmap.pitch;
1318 src += slot->bitmap.pitch;
1319 }
1320 info.linearAdvance = info.xOff = slot->bitmap.width;
1321 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
1323 uchar *src = slot->bitmap.buffer;
1324 uchar *dst = glyph_buffer.get();
1325 int h = slot->bitmap.rows;
1326 int bytes = info.width;
1327 while (h--) {
1328 memcpy (dst, src, bytes);
1329 dst += pitch;
1330 src += slot->bitmap.pitch;
1331 }
1332 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
1334 convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB);
1335 } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) {
1337 convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB);
1338 } else {
1339 qWarning("QFontEngine: Glyph rendered in unknown pixel_mode=%d", slot->bitmap.pixel_mode);
1340 return nullptr;
1341 }
1342
1343 if (!g) {
1344 g = new Glyph;
1345 g->data = nullptr;
1346 }
1347
1348 g->linearAdvance = info.linearAdvance;
1349 g->width = info.width;
1350 g->height = info.height;
1351 g->x = info.x;
1352 g->y = info.y;
1353 g->advance = info.xOff;
1354 g->format = format;
1355 delete [] g->data;
1356 g->data = glyph_buffer.release();
1357
1358 if (set)
1359 set->setGlyph(glyph, subPixelPosition, g);
1360
1361 return g;
1362}
1363
1365{
1366 return face_id;
1367}
1368
1370{
1372 if (p.postscriptName.isEmpty()) {
1374 }
1375
1376 return freetype->properties();
1377}
1378
1380{
1381 if (FT_IS_SCALABLE(freetype->face))
1382 return freetype->face->units_per_EM;
1383 else
1384 return freetype->face->size->metrics.y_ppem;
1385}
1386
1391
1393{
1394 int s = 0;
1395 if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC))
1397 if ((fontDef.weight >= QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD))
1398 s |= SynthesizedBold;
1399 if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face))
1401 return s;
1402}
1403
1405{
1407 m_descent = QFixed::fromFixed(-metrics.descender);
1408 m_leading = QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender);
1409
1411
1412 if (scalableBitmapScaleFactor != 1) {
1413 m_ascent *= scalableBitmapScaleFactor;
1414 m_descent *= scalableBitmapScaleFactor;
1415 m_leading *= scalableBitmapScaleFactor;
1416 }
1417}
1418
1420{
1421 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
1422 if (os2 && os2->version >= 2) {
1423 lockFace();
1424 QFixed answer = QFixed::fromFixed(FT_MulFix(os2->sCapHeight, freetype->face->size->metrics.y_scale));
1425 unlockFace();
1426 return answer;
1427 }
1428 return calculatedCapHeight();
1429}
1430
1432{
1433 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
1434 if (os2 && os2->sxHeight) {
1435 lockFace();
1436 QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize();
1437 unlockFace();
1438 return answer;
1439 }
1440
1441 return QFontEngine::xHeight();
1442}
1443
1445{
1446 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
1447 if (os2 && os2->xAvgCharWidth) {
1448 lockFace();
1449 QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize();
1450 unlockFace();
1451 return answer;
1452 }
1453
1455}
1456
1458{
1459 QFixed max_advance = QFixed::fromFixed(metrics.max_advance);
1460 if (scalableBitmapScaleFactor != 1)
1461 max_advance *= scalableBitmapScaleFactor;
1462 return max_advance.toReal();
1463}
1464
1466{
1467 return line_thickness;
1468}
1469
1471{
1472 return underline_position;
1473}
1474
1475void QFontEngineFT::doKerning(QGlyphLayout *g, QFontEngine::ShaperFlags flags) const
1476{
1477 if (!kerning_pairs_loaded) {
1478 kerning_pairs_loaded = true;
1479 lockFace();
1480 if (freetype->face->size->metrics.x_ppem != 0) {
1481 QFixed scalingFactor = emSquareSize() / QFixed(freetype->face->size->metrics.x_ppem);
1482 unlockFace();
1483 const_cast<QFontEngineFT *>(this)->loadKerningPairs(scalingFactor);
1484 } else {
1485 unlockFace();
1486 }
1487 }
1488
1489 if (shouldUseDesignMetrics(flags))
1491 else
1492 flags &= ~DesignMetrics;
1493
1495}
1496
1497static inline FT_Matrix QTransformToFTMatrix(const QTransform &matrix)
1498{
1499 FT_Matrix m;
1500
1501 m.xx = FT_Fixed(matrix.m11() * 65536);
1502 m.xy = FT_Fixed(-matrix.m21() * 65536);
1503 m.yx = FT_Fixed(-matrix.m12() * 65536);
1504 m.yy = FT_Fixed(matrix.m22() * 65536);
1505
1506 return m;
1507}
1508
1509QFontEngineFT::QGlyphSet *QFontEngineFT::TransformedGlyphSets::findSet(const QTransform &matrix, const QFontDef &fontDef)
1510{
1511 FT_Matrix m = QTransformToFTMatrix(matrix);
1512
1513 int i = 0;
1514 for (; i < nSets; ++i) {
1515 QGlyphSet *g = sets[i];
1516 if (!g)
1517 break;
1518 if (g->transformationMatrix.xx == m.xx
1519 && g->transformationMatrix.xy == m.xy
1520 && g->transformationMatrix.yx == m.yx
1521 && g->transformationMatrix.yy == m.yy) {
1522
1523 // found a match, move it to the front
1524 moveToFront(i);
1525 return g;
1526 }
1527 }
1528
1529 // don't cache more than nSets transformations
1530 if (i == nSets)
1531 // reuse the last set
1532 --i;
1533 moveToFront(nSets - 1);
1534 if (!sets[0])
1535 sets[0] = new QGlyphSet;
1536 QGlyphSet *gs = sets[0];
1537 gs->clear();
1538 gs->transformationMatrix = m;
1540 Q_ASSERT(gs != nullptr);
1541
1542 return gs;
1543}
1544
1545void QFontEngineFT::TransformedGlyphSets::moveToFront(int i)
1546{
1547 QGlyphSet *g = sets[i];
1548 while (i > 0) {
1549 sets[i] = sets[i - 1];
1550 --i;
1551 }
1552 sets[0] = g;
1553}
1554
1555
1557{
1558 if (matrix.type() > QTransform::TxShear || !cacheEnabled)
1559 return nullptr;
1560
1561 // FT_Set_Transform only supports scalable fonts
1562 if (!FT_IS_SCALABLE(freetype->face))
1563 return matrix.type() <= QTransform::TxTranslate ? &defaultGlyphSet : nullptr;
1564
1565 return transformedGlyphSets.findSet(matrix, fontDef);
1566}
1567
1569{
1570 FT_Face face = lockFace(Unscaled);
1571 FT_Set_Transform(face, nullptr, nullptr);
1572 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
1573
1574 int left = face->glyph->metrics.horiBearingX;
1575 int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width;
1576 int top = face->glyph->metrics.horiBearingY;
1577 int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
1578
1579 QFixedPoint p;
1580 p.x = 0;
1581 p.y = 0;
1582
1587 metrics->xoff = QFixed::fromFixed(face->glyph->advance.x);
1588
1589 if (!FT_IS_SCALABLE(freetype->face))
1591 else
1592 QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6);
1593
1594 FT_Set_Transform(face, &freetype->matrix, nullptr);
1595 unlockFace();
1596}
1597
1602
1603void QFontEngineFT::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
1604{
1605 if (!glyphs.numGlyphs)
1606 return;
1607
1608 if (FT_IS_SCALABLE(freetype->face)) {
1610 } else {
1611 QVarLengthArray<QFixedPoint> positions;
1612 QVarLengthArray<glyph_t> positioned_glyphs;
1614 matrix.translate(x, y);
1615 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
1616
1617 FT_Face face = lockFace(Unscaled);
1618 for (int gl = 0; gl < glyphs.numGlyphs; gl++) {
1619 FT_UInt glyph = positioned_glyphs[gl];
1620 FT_Load_Glyph(face, glyph, FT_LOAD_TARGET_MONO);
1622 }
1623 unlockFace();
1624 }
1625}
1626
1628 QPainterPath *path, QTextItem::RenderFlags)
1629{
1630 FT_Face face = lockFace(Unscaled);
1631
1632 for (int gl = 0; gl < numGlyphs; gl++) {
1633 FT_UInt glyph = glyphs[gl];
1634
1635 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
1636
1637 FT_GlyphSlot g = face->glyph;
1638 if (g->format != FT_GLYPH_FORMAT_OUTLINE)
1639 continue;
1640 if (embolden)
1641 FT_GlyphSlot_Embolden(g);
1642 if (obliquen)
1643 FT_GlyphSlot_Oblique(g);
1644 QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize);
1645 }
1646 unlockFace();
1647}
1648
1650{
1651 glyph_t glyph = ucs4 < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[ucs4] : 0;
1652 if (glyph == 0) {
1653 FT_Face face = freetype->face;
1654 glyph = FT_Get_Char_Index(face, ucs4);
1655 if (glyph == 0) {
1656 // Certain fonts don't have no-break space and tab,
1657 // while we usually want to render them as space
1658 if (ucs4 == QChar::Nbsp || ucs4 == QChar::Tabulation) {
1659 glyph = FT_Get_Char_Index(face, QChar::Space);
1660 } else if (freetype->symbol_map) {
1661 // Symbol fonts can have more than one CMAPs, FreeType should take the
1662 // correct one for us by default, so we always try FT_Get_Char_Index
1663 // first. If it didn't work (returns 0), we will explicitly set the
1664 // CMAP to symbol font one and try again. symbol_map is not always the
1665 // correct one because in certain fonts like Wingdings symbol_map only
1666 // contains PUA codepoints instead of the common ones.
1667 FT_Set_Charmap(face, freetype->symbol_map);
1668 glyph = FT_Get_Char_Index(face, ucs4);
1669 FT_Set_Charmap(face, freetype->unicode_map);
1670 if (!glyph && symbol && ucs4 < 0x100)
1671 glyph = FT_Get_Char_Index(face, ucs4 + 0xf000);
1672 }
1673 }
1675 freetype->cmapCache[ucs4] = glyph;
1676 }
1677
1678 return glyph;
1679}
1680
1681int QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs,
1682 QFontEngine::ShaperFlags flags) const
1683{
1684 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
1685 if (*nglyphs < len) {
1686 *nglyphs = len;
1687 return -1;
1688 }
1689
1690 int mappedGlyphs = 0;
1691 int glyph_pos = 0;
1692 if (freetype->symbol_map) {
1693 FT_Face face = freetype->face;
1695 while (it.hasNext()) {
1696 uint uc = it.next();
1697 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
1698 if ( !glyphs->glyphs[glyph_pos] ) {
1699 // Symbol fonts can have more than one CMAPs, FreeType should take the
1700 // correct one for us by default, so we always try FT_Get_Char_Index
1701 // first. If it didn't work (returns 0), we will explicitly set the
1702 // CMAP to symbol font one and try again. symbol_map is not always the
1703 // correct one because in certain fonts like Wingdings symbol_map only
1704 // contains PUA codepoints instead of the common ones.
1705 glyph_t glyph = FT_Get_Char_Index(face, uc);
1706 // Certain symbol fonts don't have no-break space (0xa0) and tab (0x9),
1707 // while we usually want to render them as space
1708 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
1709 uc = 0x20;
1710 glyph = FT_Get_Char_Index(face, uc);
1711 }
1712 if (!glyph) {
1713 FT_Set_Charmap(face, freetype->symbol_map);
1714 glyph = FT_Get_Char_Index(face, uc);
1715 FT_Set_Charmap(face, freetype->unicode_map);
1716 if (!glyph && symbol && uc < 0x100)
1717 glyph = FT_Get_Char_Index(face, uc + 0xf000);
1718 }
1719 glyphs->glyphs[glyph_pos] = glyph;
1721 freetype->cmapCache[uc] = glyph;
1722 }
1723 if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
1724 mappedGlyphs++;
1725 ++glyph_pos;
1726 }
1727 } else {
1728 FT_Face face = freetype->face;
1730 while (it.hasNext()) {
1731 uint uc = it.next();
1732 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
1733 if (!glyphs->glyphs[glyph_pos]) {
1734 {
1735 redo:
1736 glyph_t glyph = FT_Get_Char_Index(face, uc);
1737 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
1738 uc = 0x20;
1739 goto redo;
1740 }
1741 glyphs->glyphs[glyph_pos] = glyph;
1743 freetype->cmapCache[uc] = glyph;
1744 }
1745 }
1746 if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
1747 mappedGlyphs++;
1748 ++glyph_pos;
1749 }
1750 }
1751
1752 *nglyphs = glyph_pos;
1753 glyphs->numGlyphs = glyph_pos;
1754
1755 if (!(flags & GlyphIndicesOnly))
1756 recalcAdvances(glyphs, flags);
1757
1758 return mappedGlyphs;
1759}
1760
1761bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const
1762{
1763 if (!FT_IS_SCALABLE(freetype->face))
1764 return false;
1765
1767}
1768
1769QFixed QFontEngineFT::scaledBitmapMetrics(QFixed m) const
1770{
1771 return m * scalableBitmapScaleFactor;
1772}
1773
1774glyph_metrics_t QFontEngineFT::scaledBitmapMetrics(const glyph_metrics_t &m, const QTransform &t) const
1775{
1776 QTransform trans;
1777 trans.setMatrix(t.m11(), t.m12(), t.m13(),
1778 t.m21(), t.m22(), t.m23(),
1779 0, 0, t.m33());
1780 const qreal scaleFactor = scalableBitmapScaleFactor.toReal();
1781 trans.scale(scaleFactor, scaleFactor);
1782
1783 QRectF rect(m.x.toReal(), m.y.toReal(), m.width.toReal(), m.height.toReal());
1784 QPointF offset(m.xoff.toReal(), m.yoff.toReal());
1785
1786 rect = trans.mapRect(rect);
1787 offset = trans.map(offset);
1788
1791 metrics.y = QFixed::fromReal(rect.y());
1792 metrics.width = QFixed::fromReal(rect.width());
1793 metrics.height = QFixed::fromReal(rect.height());
1794 metrics.xoff = QFixed::fromReal(offset.x());
1795 metrics.yoff = QFixed::fromReal(offset.y());
1796 return metrics;
1797}
1798
1799void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
1800{
1801 FT_Face face = nullptr;
1802 bool design = shouldUseDesignMetrics(flags);
1803 for (int i = 0; i < glyphs->numGlyphs; i++) {
1804 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs->glyphs[i]) : nullptr;
1805 // Since we are passing Format_None to loadGlyph, use same default format logic as loadGlyph
1806 GlyphFormat acceptableFormat = (defaultFormat != Format_None) ? defaultFormat : Format_Mono;
1807 if (g && g->format == acceptableFormat) {
1808 glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
1809 } else {
1810 if (!face)
1811 face = lockFace();
1812 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
1813 glyphs->glyphs[i],
1814 QFixedPoint(),
1816 true);
1817 if (g)
1818 glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
1819 else
1820 glyphs->advances[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10)
1821 : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round();
1822 if (!cacheEnabled && g != &emptyGlyph)
1823 delete g;
1824 }
1825
1826 if (scalableBitmapScaleFactor != 1)
1827 glyphs->advances[i] *= scalableBitmapScaleFactor;
1828 }
1829 if (face)
1830 unlockFace();
1831}
1832
1834{
1835 FT_Face face = nullptr;
1836
1837 glyph_metrics_t overall;
1838 // initialize with line height, we get the same behaviour on all platforms
1839 if (!isScalableBitmap()) {
1840 overall.y = -ascent();
1841 overall.height = ascent() + descent();
1842 } else {
1843 overall.y = QFixed::fromFixed(-metrics.ascender);
1844 overall.height = QFixed::fromFixed(metrics.ascender - metrics.descender);
1845 }
1846
1847 QFixed ymax = 0;
1848 QFixed xmax = 0;
1849 for (int i = 0; i < glyphs.numGlyphs; i++) {
1850 // If shaping has found this should be ignored, ignore it.
1851 if (!glyphs.advances[i] || glyphs.attributes[i].dontPrint)
1852 continue;
1853 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs.glyphs[i]) : nullptr;
1854 if (!g) {
1855 if (!face)
1856 face = lockFace();
1857 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
1858 glyphs.glyphs[i],
1859 QFixedPoint(),
1861 true);
1862 }
1863 if (g) {
1864 QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
1865 QFixed y = overall.yoff + glyphs.offsets[i].y - g->y;
1866 overall.x = qMin(overall.x, x);
1867 overall.y = qMin(overall.y, y);
1868 xmax = qMax(xmax, x.ceil() + g->width);
1869 ymax = qMax(ymax, y.ceil() + g->height);
1870 if (!cacheEnabled && g != &emptyGlyph)
1871 delete g;
1872 } else {
1873 int left = FLOOR(face->glyph->metrics.horiBearingX);
1874 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1875 int top = CEIL(face->glyph->metrics.horiBearingY);
1876 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1877
1878 QFixed x = overall.xoff + glyphs.offsets[i].x - (-TRUNC(left));
1879 QFixed y = overall.yoff + glyphs.offsets[i].y - TRUNC(top);
1880 overall.x = qMin(overall.x, x);
1881 overall.y = qMin(overall.y, y);
1882 xmax = qMax(xmax, x + TRUNC(right - left));
1883 ymax = qMax(ymax, y + TRUNC(top - bottom));
1884 }
1885 overall.xoff += glyphs.effectiveAdvance(i);
1886 }
1887 overall.height = qMax(overall.height, ymax - overall.y);
1888 overall.width = xmax - overall.x;
1889
1890 if (face)
1891 unlockFace();
1892
1893 if (isScalableBitmap())
1894 overall = scaledBitmapMetrics(overall, QTransform());
1895 return overall;
1896}
1897
1899{
1900 FT_Face face = nullptr;
1901 glyph_metrics_t overall;
1902 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) : nullptr;
1903 if (!g) {
1904 face = lockFace();
1905 g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr,
1906 glyph,
1907 QFixedPoint(),
1909 true);
1910 }
1911 if (g) {
1912 overall.x = g->x;
1913 overall.y = -g->y;
1914 overall.width = g->width;
1915 overall.height = g->height;
1916 overall.xoff = g->advance;
1917 if (!cacheEnabled && g != &emptyGlyph)
1918 delete g;
1919 } else {
1920 int left = FLOOR(face->glyph->metrics.horiBearingX);
1921 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1922 int top = CEIL(face->glyph->metrics.horiBearingY);
1923 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1924
1925 overall.width = TRUNC(right-left);
1926 overall.height = TRUNC(top-bottom);
1927 overall.x = TRUNC(left);
1928 overall.y = -TRUNC(top);
1929 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
1930 }
1931 if (face)
1932 unlockFace();
1933
1934 if (isScalableBitmap())
1935 overall = scaledBitmapMetrics(overall, QTransform());
1936 return overall;
1937}
1938
1943
1945 const QFixedPoint &subPixelPosition,
1946 const QTransform &matrix,
1948{
1949 // When rendering glyphs into a cache via the alphaMap* functions, we disable
1950 // outline drawing. To ensure the bounding box matches the rendered glyph, we
1951 // need to do the same here.
1952
1953 const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
1954 && matrix.type() > QTransform::TxTranslate;
1955 if (needsImageTransform && format == QFontEngine::Format_Mono)
1957 Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true, true);
1958
1959 glyph_metrics_t overall;
1960 if (g) {
1961 overall.x = g->x;
1962 overall.y = -g->y;
1963 overall.width = g->width;
1964 overall.height = g->height;
1965 overall.xoff = g->advance;
1966 if (!cacheEnabled && g != &emptyGlyph)
1967 delete g;
1968 } else {
1969 FT_Face face = lockFace();
1970 int left = FLOOR(face->glyph->metrics.horiBearingX);
1971 int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
1972 int top = CEIL(face->glyph->metrics.horiBearingY);
1973 int bottom = FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
1974
1975 overall.width = TRUNC(right-left);
1976 overall.height = TRUNC(top-bottom);
1977 overall.x = TRUNC(left);
1978 overall.y = -TRUNC(top);
1979 overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
1980 unlockFace();
1981 }
1982
1983 if (isScalableBitmap() || needsImageTransform)
1984 overall = scaledBitmapMetrics(overall, matrix);
1985 return overall;
1986}
1987
1989{
1990 if (glyph == nullptr || glyph->height == 0 || glyph->width == 0)
1991 return QImage();
1992
1994 int bytesPerLine = -1;
1995 switch (glyphFormat) {
1998 bytesPerLine = ((glyph->width + 31) & ~31) >> 3;
1999 break;
2002 bytesPerLine = (glyph->width + 3) & ~3;
2003 break;
2006 bytesPerLine = glyph->width * 4;
2007 break;
2008 default:
2009 Q_UNREACHABLE();
2010 };
2011
2012 QImage img(static_cast<const uchar *>(glyph->data), glyph->width, glyph->height, bytesPerLine, format);
2014 img.setColor(1, QColor(Qt::white).rgba()); // Expands color table to 2 items; item 0 set to transparent.
2015 return img;
2016}
2017
2019 const QFixedPoint &subPixelPosition,
2020 QFontEngine::GlyphFormat neededFormat,
2021 const QTransform &t)
2022{
2024
2025 if (isBitmapFont())
2026 neededFormat = Format_Mono;
2027 else if (neededFormat == Format_None && defaultFormat != Format_None)
2028 neededFormat = defaultFormat;
2029 else if (neededFormat == Format_None)
2030 neededFormat = Format_A8;
2031
2032 Glyph *glyph = loadGlyphFor(glyphIndex, subPixelPosition, neededFormat, t);
2033 if (!glyph || !glyph->width || !glyph->height)
2034 return nullptr;
2035
2036 return glyph;
2037}
2038
2039static inline bool is2dRotation(const QTransform &t)
2040{
2041 return qFuzzyCompare(t.m11(), t.m22()) && qFuzzyCompare(t.m12(), -t.m21())
2042 && qFuzzyCompare(t.m11()*t.m22() - t.m12()*t.m21(), qreal(1.0));
2043}
2044
2046 const QFixedPoint &subPixelPosition,
2048 const QTransform &t,
2049 bool fetchBoundingBox,
2050 bool disableOutlineDrawing)
2051{
2052 QGlyphSet *glyphSet = loadGlyphSet(t);
2053 if (glyphSet != nullptr && glyphSet->outline_drawing && !disableOutlineDrawing && !fetchBoundingBox)
2054 return nullptr;
2055
2056 Glyph *glyph = glyphSet != nullptr ? glyphSet->getGlyph(g, subPixelPosition) : nullptr;
2057 if (!glyph || glyph->format != format || (!fetchBoundingBox && !glyph->data)) {
2058 QScopedValueRollback<HintStyle> saved_default_hint_style(default_hint_style);
2059 if (t.type() >= QTransform::TxScale && !is2dRotation(t))
2060 default_hint_style = HintNone; // disable hinting if the glyphs are transformed
2061
2062 lockFace();
2063 FT_Matrix m = this->matrix;
2064 FT_Matrix ftMatrix = glyphSet != nullptr ? glyphSet->transformationMatrix : QTransformToFTMatrix(t);
2065 FT_Matrix_Multiply(&ftMatrix, &m);
2066 freetype->matrix = m;
2067 glyph = loadGlyph(glyphSet, g, subPixelPosition, format, false, disableOutlineDrawing);
2068 unlockFace();
2069 }
2070
2071 return glyph;
2072}
2073
2075{
2076 return alphaMapForGlyph(g, subPixelPosition, QTransform());
2077}
2078
2080 const QFixedPoint &subPixelPosition,
2081 const QTransform &t)
2082{
2083 const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
2084 && t.type() > QTransform::TxTranslate;
2085 const GlyphFormat neededFormat = antialias || needsImageTransform ? Format_A8 : Format_Mono;
2086
2087 Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true);
2088
2089 QImage img = alphaMapFromGlyphData(glyph, neededFormat);
2090 if (needsImageTransform)
2091 img = img.transformed(t, Qt::SmoothTransformation);
2092 else
2093 img = img.copy();
2094
2095 if (!cacheEnabled && glyph != &emptyGlyph)
2096 delete glyph;
2097
2098 return img;
2099}
2100
2102 const QFixedPoint &subPixelPosition,
2103 const QTransform &t)
2104{
2105 if (t.type() > QTransform::TxRotate)
2106 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
2107
2108 const GlyphFormat neededFormat = Format_A32;
2109
2110 Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true);
2111
2112 QImage img = alphaMapFromGlyphData(glyph, neededFormat);
2113 img = img.copy();
2114
2115 if (!cacheEnabled && glyph != &emptyGlyph)
2116 delete glyph;
2117
2118 if (!img.isNull())
2119 return img;
2120
2121 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
2122}
2123
2125 const QFixedPoint &subPixelPosition,
2126 const QTransform &t,
2127 const QColor &color)
2128{
2129 Q_UNUSED(color);
2130
2131 Glyph *glyph = loadGlyphFor(g, subPixelPosition, defaultFormat, t);
2132 if (glyph == nullptr)
2133 return QImage();
2134
2135 QImage img;
2136 if (defaultFormat == GlyphFormat::Format_ARGB)
2137 img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_ARGB32_Premultiplied).copy();
2138 else if (defaultFormat == GlyphFormat::Format_Mono)
2139 img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_Mono).copy();
2140
2141 if (!img.isNull() && (!t.isIdentity() || scalableBitmapScaleFactor != 1)) {
2142 QTransform trans(t);
2143 const qreal scaleFactor = scalableBitmapScaleFactor.toReal();
2144 trans.scale(scaleFactor, scaleFactor);
2145 img = img.transformed(trans, Qt::SmoothTransformation);
2146 }
2147
2148 if (!cacheEnabled && glyph != &emptyGlyph)
2149 delete glyph;
2150
2151 return img;
2152}
2153
2155{
2156 defaultGlyphSet.removeGlyphFromCache(glyph, QFixedPoint());
2157}
2158
2160{
2161 int count = 0;
2162 FT_Face face = lockFace();
2163 if (face) {
2164 count = face->num_glyphs;
2165 unlockFace();
2166 }
2167 return count;
2168}
2169
2171{
2172 freetype->lock();
2173 FT_Face face = freetype->face;
2174 if (scale == Unscaled) {
2175 if (FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0) == 0) {
2176 freetype->xsize = face->units_per_EM << 6;
2177 freetype->ysize = face->units_per_EM << 6;
2178 }
2179 } else if (freetype->xsize != xsize || freetype->ysize != ysize) {
2180 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
2181 freetype->xsize = xsize;
2182 freetype->ysize = ysize;
2183 }
2184 if (freetype->matrix.xx != matrix.xx ||
2185 freetype->matrix.yy != matrix.yy ||
2186 freetype->matrix.xy != matrix.xy ||
2187 freetype->matrix.yx != matrix.yx) {
2189 FT_Set_Transform(face, &freetype->matrix, nullptr);
2190 }
2191
2192 return face;
2193}
2194
2196{
2197 freetype->unlock();
2198}
2199
2201{
2202 return freetype->face;
2203}
2204
2205
2207 : outline_drawing(false)
2208{
2209 transformationMatrix.xx = 0x10000;
2210 transformationMatrix.yy = 0x10000;
2211 transformationMatrix.xy = 0;
2212 transformationMatrix.yx = 0;
2213 memset(fast_glyph_data, 0, sizeof(fast_glyph_data));
2214 fast_glyph_count = 0;
2215}
2216
2221
2223{
2224 if (fast_glyph_count > 0) {
2225 for (int i = 0; i < 256; ++i) {
2226 if (fast_glyph_data[i]) {
2227 delete fast_glyph_data[i];
2228 fast_glyph_data[i] = nullptr;
2229 }
2230 }
2231 fast_glyph_count = 0;
2232 }
2233 qDeleteAll(glyph_data);
2234 glyph_data.clear();
2235}
2236
2238 const QFixedPoint &subPixelPosition)
2239{
2240 if (useFastGlyphData(index, subPixelPosition)) {
2241 if (fast_glyph_data[index]) {
2242 delete fast_glyph_data[index];
2243 fast_glyph_data[index] = nullptr;
2244 if (fast_glyph_count > 0)
2245 --fast_glyph_count;
2246 }
2247 } else {
2248 delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition));
2249 }
2250}
2251
2253 const QFixedPoint &subPixelPosition,
2254 Glyph *glyph)
2255{
2256 if (useFastGlyphData(index, subPixelPosition)) {
2257 if (!fast_glyph_data[index])
2258 ++fast_glyph_count;
2259 fast_glyph_data[index] = glyph;
2260 } else {
2261 glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph);
2262 }
2263}
2264
2265int QFontEngineFT::getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
2266{
2267 lockFace();
2268 bool hsubpixel = true;
2269 int vfactor = 1;
2270 int load_flags = loadFlags(nullptr, Format_A8, flags, hsubpixel, vfactor);
2271 int result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints);
2272 unlockFace();
2273 return result;
2274}
2275
2277{
2278 if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype))
2279 return false;
2280
2281 // Increase the reference of this QFreetypeFace since one more QFontEngineFT
2282 // will be using it
2283 freetype->ref.ref();
2284
2285 default_load_flags = fe->default_load_flags;
2286 default_hint_style = fe->default_hint_style;
2287 antialias = fe->antialias;
2288 transform = fe->transform;
2289 embolden = fe->embolden;
2290 obliquen = fe->obliquen;
2291 subpixelType = fe->subpixelType;
2292 lcdFilterType = fe->lcdFilterType;
2293 embeddedbitmap = fe->embeddedbitmap;
2294
2295 return true;
2296}
2297
2299{
2300 QFontDef fontDef(this->fontDef);
2301 fontDef.pixelSize = pixelSize;
2303 if (!fe->initFromFontEngine(this)) {
2304 delete fe;
2305 return nullptr;
2306 } else {
2307 return fe;
2308 }
2309}
2310
2312{
2313 return non_locked_face();
2314}
2315
2317
2318#endif // QT_NO_FREETYPE
bool ref() noexcept
T loadRelaxed() const noexcept
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
int toInt(bool *ok=nullptr, int base=10) const
Returns the byte array converted to an int using base base, which is ten by default.
bool startsWith(QByteArrayView bv) const
Definition qbytearray.h:223
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
QByteArray & remove(qsizetype index, qsizetype len)
Removes len bytes from the array, starting at index position pos, and returns a reference to the arra...
\inmodule QtCore
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
static QString decodeName(const QByteArray &localFileName)
This does the reverse of QFile::encodeName() using localFileName.
Definition qfile.h:162
bool expectsGammaCorrectedBlending() const override
glyph_metrics_t boundingBox(const QGlyphLayout &glyphs) override
int stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, ShaperFlags flags) const override
void addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) override
void setDefaultHintStyle(HintStyle style) override
HintStyle default_hint_style
glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, const QFixedPoint &subPixelPosition, const QTransform &matrix, QFontEngine::GlyphFormat format) override
int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints) override
void initializeHeightMetrics() const override
QGlyphSet * loadGlyphSet(const QTransform &matrix)
QFixed underlinePosition() const override
QFixed lineThickness() const override
QFixed emSquareSize() const override
bool initFromFontEngine(const QFontEngineFT *fontEngine)
QFreetypeFace * freetype
QFontEngine * cloneWithSize(qreal pixelSize) const override
void getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) override
QImage alphaMapForGlyph(glyph_t g) override
QFontEngineFT(const QFontDef &fd)
bool isScalableBitmap() const
QFixed xHeight() const override
FT_Face non_locked_face() const
bool init(FaceId faceId, bool antiaalias, GlyphFormat defaultFormat=Format_None, const QByteArray &fontData=QByteArray())
void removeGlyphFromCache(glyph_t glyph) override
qreal maxCharWidth() const override
void doKerning(QGlyphLayout *, ShaperFlags) const override
void setQtDefaultHintStyle(QFont::HintingPreference hintingPreference)
Qt::HANDLE handle() const override
QFontEngine::Properties properties() const override
QFontEngine::FaceId faceId() const override
Glyph * loadGlyphFor(glyph_t g, const QFixedPoint &subPixelPosition, GlyphFormat format, const QTransform &t, bool fetchBoundingBox=false, bool disableOutlineDrawing=false)
Glyph * loadGlyph(uint glyph, const QFixedPoint &subPixelPosition, GlyphFormat format=Format_None, bool fetchMetricsOnly=false, bool disableOutlineDrawing=false) const
int glyphCount() const override
static QFontEngineFT * create(const QFontDef &fontDef, FaceId faceId, const QByteArray &fontData=QByteArray())
bool isBitmapFont() const
Glyph * glyphData(glyph_t glyph, const QFixedPoint &subPixelPosition, GlyphFormat neededFormat, const QTransform &t) override
glyph_t glyphIndex(uint ucs4) const override
void unlockFace() const
void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, QPainterPath *path, QTextItem::RenderFlags flags) override
int synthesized() const override
QFixed capHeight() const override
QImage alphaRGBMapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t) override
QImage bitmapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t, const QColor &color) override
virtual ~QFontEngineFT()
SubpixelAntialiasingType subpixelType
QFixed averageCharWidth() const override
bool getSfntTableData(uint tag, uchar *buffer, uint *length) const override
Returns true if the font table idetified by tag exists in the font; returns false otherwise.
bool supportsTransformation(const QTransform &transform) const override
FT_Face lockFace(Scaling scale=Scaled) const
void recalcAdvances(QGlyphLayout *glyphs, ShaperFlags flags) const override
void * get() const noexcept
void reset() noexcept
virtual QFixed descent() const
void loadKerningPairs(QFixed scalingFactor)
QFixed calculatedCapHeight() const
static QByteArray convertToPostscriptFontFamilyName(const QByteArray &fontFamily)
int m_subPixelPositionCount
static bool isIgnorableChar(char32_t ucs4)
QFontDef fontDef
virtual QFixed ascent() const
struct QFontEngine::FaceData faceData
virtual QImage alphaRGBMapForGlyph(glyph_t, const QFixedPoint &subPixelPosition, const QTransform &t)
virtual QFixed averageCharWidth() const
virtual void initializeHeightMetrics() const
virtual void addOutlineToPath(qreal, qreal, const QGlyphLayout &, QPainterPath *, QTextItem::RenderFlags flags)
virtual void doKerning(QGlyphLayout *, ShaperFlags) const
void getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags, QVarLengthArray< glyph_t > &glyphs_out, QVarLengthArray< QFixedPoint > &positions)
GlyphFormat glyphFormat
virtual QFixed leading() const
void * harfbuzzFace() const
virtual QFixed xHeight() const
HintingPreference
Definition qfont.h:55
@ PreferNoHinting
Definition qfont.h:57
@ PreferFullHinting
Definition qfont.h:59
@ PreferVerticalHinting
Definition qfont.h:58
@ PreferDefaultHinting
Definition qfont.h:56
@ Unstretched
Definition qfont.h:89
@ NoSubpixelAntialias
Definition qfont.h:48
@ NoAntialias
Definition qfont.h:47
@ Bold
Definition qfont.h:70
@ StyleItalic
Definition qfont.h:78
@ StyleNormal
Definition qfont.h:77
int getPointInOutline(glyph_t glyph, int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
static int getFaceIndexByStyleName(const QString &faceFileName, const QString &styleName)
bool isScalable() const
FT_CharMap symbol_map
FT_CharMap unicode_map
static void addBitmapToPath(FT_GlyphSlot slot, const QFixedPoint &point, QPainterPath *path)
int fsType() const
static void addGlyphToPath(FT_Face face, FT_GlyphSlot g, const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale)
bool isScalableBitmap() const
glyph_t cmapCache[cmapCacheSize]
static QFreetypeFace * getFace(const QFontEngine::FaceId &face_id, const QByteArray &fontData=QByteArray())
void release(const QFontEngine::FaceId &face_id)
QFontEngine::Properties properties() const
void computeSize(const QFontDef &fontDef, int *xsize, int *ysize, bool *outline_drawing, QFixed *scalableBitmapScaleFactor)
FT_MM_Var * mm_var
bool getSfntTable(uint tag, uchar *buffer, uint *length) const
QScreen * primaryScreen
the primary (or default) screen of the application.
const_iterator cbegin() const noexcept
Definition qhash.h:1214
const_iterator cend() const noexcept
Definition qhash.h:1218
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
\inmodule QtGui
Definition qimage.h:37
QImage copy(const QRect &rect=QRect()) const
Returns a sub-area of the image as a new image.
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_Alpha8
Definition qimage.h:65
@ Format_RGB32
Definition qimage.h:46
@ Format_Invalid
Definition qimage.h:42
@ Format_Mono
Definition qimage.h:43
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
const_iterator constFind(const Key &key) const
Definition qmap.h:655
bool isEmpty() const
Definition qmap.h:269
const_iterator constEnd() const
Definition qmap.h:604
\inmodule QtGui
virtual SubpixelAntialiasingType subpixelAntialiasingTypeHint() const
Returns a hint about this screen's subpixel layout structure.
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
\inmodule QtCore\reentrant
Definition qrect.h:484
The QScreen class is used to query screen properties. \inmodule QtGui.
Definition qscreen.h:32
QPlatformScreen * handle() const
Get the platform screen handle.
Definition qscreen.cpp:83
iterator end()
Definition qset.h:140
const_iterator constBegin() const noexcept
Definition qset.h:139
const_iterator constEnd() const noexcept
Definition qset.h:143
iterator erase(const_iterator i)
Definition qset.h:145
iterator find(const T &value)
Definition qset.h:159
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QByteArray toUtf8() const &
Definition qstring.h:634
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
QTransform & scale(qreal sx, qreal sy)
Scales the coordinate system by sx horizontally and sy vertically, and returns a reference to the mat...
QPoint map(const QPoint &p) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setMatrix(qreal m11, qreal m12, qreal m13, qreal m21, qreal m22, qreal m23, qreal m31, qreal m32, qreal m33)
Sets the matrix elements to the specified values, m11, m12, m13 m21, m22, m23 m31,...
QTransform & translate(qreal dx, qreal dy)
Moves the coordinate system dx along the x axis and dy along the y axis, and returns a reference to t...
QRect mapRect(const QRect &) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QUuid createUuid()
On any platform other than Windows, this function returns a new UUID with variant QUuid::DCE and vers...
Definition quuid.cpp:997
void * data()
Returns a pointer to the contained object as a generic void* that can be written to.
QHash< QFontEngine::FaceId, QFreetypeFace * > faces
QHash< FaceStyle, int > faceIndices
QString str
[2]
b clear()
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
rect
[4]
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
@ SmoothTransformation
@ white
Definition qnamespace.h:31
void * HANDLE
@ CaseInsensitive
int qstrncmp(const char *str1, const char *str2, size_t len)
QList< QString > QStringList
Constructs a string list that contains the given string, str.
AudioChannelLayoutTag tag
static const QCssKnownValue positions[NumKnownPositionModes - 1]
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void * user_data
DBusConnection const char DBusError * error
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
constexpr T qbswap(T source)
Definition qendian.h:103
#define QT_RETHROW
#define QT_CATCH(A)
#define QT_TRY
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
static QFontEngineFT::Glyph emptyGlyph
void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path)
#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)
size_t qHash(const QtFreetypeData::FaceStyle &style, size_t seed)
static bool ft_getSfntTable(void *user_data, uint tag, uchar *buffer, uint *length)
static const QFontEngine::HintStyle ftInitialDefaultHintStyle
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
QByteArray qt_fontdata_from_index(int)
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)
Q_GUI_EXPORT FT_Library qt_getFreetype()
@ Err_Invalid_SubTable
@ Err_Ok
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define TRUNC(x)
constexpr QtPrivate::QHashMultiReturnType< T... > qHashMulti(size_t seed, const T &... args) noexcept(std::conjunction_v< QtPrivate::QNothrowHashable< T >... >)
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLboolean GLboolean GLboolean b
GLenum GLsizeiptr const void GLsizei faceIndex
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLuint index
[2]
GLboolean r
[2]
GLenum GLuint GLenum GLsizei length
GLsizei const GLubyte GLsizei GLenum const void * coords
GLdouble GLdouble GLdouble GLdouble top
GLenum GLenum GLsizei count
GLdouble GLdouble right
GLenum const void GLbitfield GLsizei numGlyphs
GLenum face
GLfloat GLfloat f
GLsizei GLenum const void GLuint GLsizei GLfloat * metrics
GLenum src
GLenum GLuint buffer
GLint GLsizei width
GLuint color
[2]
GLint left
GLenum type
GLenum GLenum dst
GLint GLint bottom
GLuint GLfloat x0
GLbitfield flags
GLuint start
GLenum GLuint GLintptr offset
GLboolean GLboolean g
GLuint64 GLenum GLint fd
GLint ref
GLfloat n
GLint GLsizei GLsizei GLenum format
GLenum GLsizeiptr const void * fontData
GLuint GLfloat GLfloat y0
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLuint GLenum GLenum transform
GLdouble s
[6]
Definition qopenglext.h:235
GLbyte GLbyte blue
Definition qopenglext.h:385
const GLubyte * c
GLint void * img
Definition qopenglext.h:233
GLuint GLenum matrix
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLenum GLsizei len
GLbyte green
Definition qopenglext.h:385
GLenum GLenum GLenum GLenum GLenum scale
#define QT_MAX_CACHED_GLYPH_SIZE
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScreen * screen
[1]
Definition main.cpp:29
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
unsigned int glyph_t
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
size_t quintptr
Definition qtypes.h:167
unsigned int uint
Definition qtypes.h:34
double qreal
Definition qtypes.h:187
QVideoFrameFormat::PixelFormat fmt
QFuture< QSet< QChar > > set
[10]
QList< int > vector
[14]
QFile file
[0]
QObject::connect nullptr
QHostInfo info
[0]
QJSEngine engine
[0]
QGraphicsSvgItem * red
QFixed y
Definition qfixed_p.h:163
constexpr QPointF toPointF() const
Definition qfixed_p.h:166
QFixed x
Definition qfixed_p.h:162
static constexpr QFixed fromReal(qreal r)
Definition qfixed_p.h:35
static constexpr QFixed fromFixed(int fixed)
Definition qfixed_p.h:36
constexpr int value() const
Definition qfixed_p.h:38
constexpr QFixed ceil() const
Definition qfixed_p.h:47
constexpr qreal toReal() const
Definition qfixed_p.h:42
uint hintingPreference
Definition qfont_p.h:67
uint stretch
Definition qfont_p.h:65
uint style
Definition qfont_p.h:66
uint styleStrategy
Definition qfont_p.h:64
qreal pixelSize
Definition qfont_p.h:61
QMap< QFont::Tag, float > variableAxisValues
Definition qfont_p.h:58
uint weight
Definition qfont_p.h:70
QStringList families
Definition qfont_p.h:54
QString styleName
Definition qfont_p.h:55
void setGlyph(glyph_t index, const QFixedPoint &spp, Glyph *glyph)
void removeGlyphFromCache(glyph_t index, const QFixedPoint &subPixelPosition)
Glyph * getGlyph(glyph_t index, const QFixedPoint &subPixelPositionX=QFixedPoint()) const
QMap< QFont::Tag, float > variableAxes
unsigned short height
unsigned short width
The QFont::Tag type provides access to advanced font features.
Definition qfont.h:215
constexpr quint32 value() const noexcept
Returns the numerical value of this tag.
Definition qfont.h:227
QFixed effectiveAdvance(int item) const
QGlyphAttributes * attributes
glyph_t * glyphs
QFixedPoint * offsets
QFixed * advances
FaceStyle(QString faceFileName, QString styleName)