10#include "private/qfontdatabase_p.h"
11#include "private/qimage_p.h"
12#include <private/qstringiterator_p.h>
13#include <qguiapplication.h>
15#include <qpa/qplatformscreen.h>
16#include <QtCore/QUuid>
17#include <QtCore/QLoggingCategory>
18#include <QtGui/QPainterPath>
24#include <qscopedvaluerollback.h>
28#include <private/qcolrpaintgraphrenderer_p.h>
35#include FT_SYNTHESIS_H
36#include FT_TRUETYPE_TABLES_H
37#include FT_TYPE1_TABLES_H
40#include FT_LCD_FILTER_H
41#include FT_MULTIPLE_MASTERS_H
43#if defined(FT_CONFIG_OPTIONS_H)
44#include FT_CONFIG_OPTIONS_H
47#if defined(FT_FONT_FORMATS_H)
48#include FT_FONT_FORMATS_H
57using namespace Qt::StringLiterals;
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
)
64static bool ft_getSfntTable(
void *user_data, uint tag, uchar *buffer, uint *length)
66 FT_Face face = (FT_Face)user_data;
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
83 QFontEngineFT::HintFull;
85 QFontEngineFT::HintNone;
116 for (
auto iter = faces.cbegin(); iter != faces.cend(); ++iter) {
117 iter.value()->cleanup();
118 if (!iter.value()->ref.deref())
123 for (
auto iter = staleFaces.cbegin(); iter != staleFaces.cend(); ++iter) {
125 if (!(*iter)->ref.deref())
130 FT_Done_FreeType(library);
136 return style1.faceFileName == style2.faceFileName && style1.styleName == style2.styleName;
141 return qHashMulti(seed, style.faceFileName, style.styleName);
151 if (!freetypeData->library) {
152 FT_Init_FreeType(&freetypeData->library);
153#if defined(FT_FONT_FORMATS_H)
155 FT_Bool no_darkening =
false;
156 FT_Property_Set(freetypeData->library,
"cff",
"no-stem-darkening", &no_darkening);
165 Q_ASSERT(freetypeData->library);
166 return freetypeData->library;
169int QFreetypeFace::fsType()
const
172 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
174 fsType = os2->fsType;
178int QFreetypeFace::getPointInOutline(glyph_t glyph,
int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
180 if (
int error = FT_Load_Glyph(face, glyph, flags))
183 if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
184 return Err_Invalid_SubTable;
186 *nPoints = face->glyph->outline.n_points;
190 if (point >= *nPoints)
191 return Err_Invalid_SubTable;
193 *xpos = QFixed::fromFixed(face->glyph->outline.points[point].x);
194 *ypos = QFixed::fromFixed(face->glyph->outline.points[point].y);
199bool QFreetypeFace::isScalableBitmap()
const
202 return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face);
211
212
213
214
215
216
217QFreetypeFace *QFreetypeFace::getFace(
const QFontEngine::FaceId &face_id,
218 const QByteArray &fontData)
220 if (face_id.filename.isEmpty() && fontData.isEmpty())
223 QtFreetypeData *freetypeData = qt_getFreetypeData();
226 freetypeData->staleFaces.removeIf([](QFreetypeFace *face) {
227 if (face->ref.loadRelaxed() == 1) {
235 QFreetypeFace *freetype =
nullptr;
236 auto it = freetypeData->faces.find(face_id);
237 if (it != freetypeData->faces.end()) {
240 Q_ASSERT(freetype->ref.loadRelaxed() > 0);
241 if (freetype->ref.loadRelaxed() == 1) {
248 freetypeData->faces.erase(it);
257 const auto deleter = [](QFreetypeFace *f) {
delete f; };
258 std::unique_ptr<QFreetypeFace,
decltype(deleter)> newFreetype(
new QFreetypeFace, deleter);
261 if (!face_id.filename.isEmpty()) {
262 QString fileName = QFile::decodeName(face_id.filename);
263 if (
const char *prefix =
":qmemoryfonts/"; face_id.filename.startsWith(prefix)) {
265 QByteArray idx = face_id.filename;
266 idx.remove(0, strlen(prefix));
268 newFreetype->fontData = qt_fontdata_from_index(idx.toInt(&ok));
270 newFreetype->fontData = QByteArray();
271 }
else if (!QFileInfo(fileName).isNativePath()) {
272 QFile file(fileName);
273 if (!file.open(QIODevice::ReadOnly)) {
276 newFreetype->fontData = file.readAll();
279 newFreetype->fontData = fontData;
282 FT_Int major, minor, patch;
283 FT_Library_Version(qt_getFreetype(), &major, &minor, &patch);
284 const bool goodVersion = major > 2 || (major == 2 && minor > 13) || (major == 2 && minor == 13 && patch > 2);
286 if (!newFreetype->fontData.isEmpty()) {
287 if (FT_New_Memory_Face(freetypeData->library,
288 (
const FT_Byte *)newFreetype->fontData.constData(),
289 newFreetype->fontData.size(),
299 if (FT_Reference_Face(face))
301 }
else if (!FT_HAS_MULTIPLE_MASTERS(face)
302 || FT_New_Memory_Face(freetypeData->library,
303 (
const FT_Byte *)newFreetype->fontData.constData(),
304 newFreetype->fontData.size(),
306 &tmpFace) != FT_Err_Ok) {
310 if (FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &face))
317 if (FT_Reference_Face(face))
319 }
else if (!FT_HAS_MULTIPLE_MASTERS(face)
320 || FT_New_Face(freetypeData->library, face_id.filename, face_id.index, &tmpFace) != FT_Err_Ok) {
325#if (FREETYPE_MAJOR*10000
+ FREETYPE_MINOR*100
+ FREETYPE_PATCH) >= 20900
326 if (face_id.instanceIndex >= 0) {
328 <<
"Selecting named instance" << (face_id.instanceIndex)
329 <<
"in" << face_id.filename;
330 FT_Set_Named_Instance(face, face_id.instanceIndex + 1);
338 if (tmpFace !=
nullptr) {
340 if (FT_Get_MM_Var(tmpFace, &var) == FT_Err_Ok) {
341 for (FT_UInt i = 0; i < var->num_axis; ++i) {
342 FT_Var_Axis *axis = var->axis + i;
344 QFontVariableAxis fontVariableAxis;
345 if (
const auto tag = QFont::Tag::fromValue(axis->tag)) {
346 fontVariableAxis.setTag(*tag);
348 qWarning() <<
"QFreetypeFace::getFace: Invalid variable axis tag encountered"
352 fontVariableAxis.setMinimumValue(axis->minimum / 65536.0);
353 fontVariableAxis.setMaximumValue(axis->maximum / 65536.0);
354 fontVariableAxis.setDefaultValue(axis->def / 65536.0);
355 fontVariableAxis.setName(QString::fromUtf8(axis->name));
357 newFreetype->variableAxisList.append(fontVariableAxis);
360 if (!face_id.variableAxes.isEmpty()) {
361 QVarLengthArray<FT_Fixed, 16> coords(var->num_axis);
362 FT_Get_Var_Design_Coordinates(face, var->num_axis, coords.data());
363 for (qsizetype i = 0; i < newFreetype->variableAxisList.size(); ++i) {
364 const QFontVariableAxis &axis = newFreetype->variableAxisList.at(i);
365 if (axis.tag().isValid()) {
366 const auto it = face_id.variableAxes.constFind(axis.tag());
367 if (it != face_id.variableAxes.constEnd())
368 coords[i] = FT_Fixed(*it * 65536);
371 FT_Set_Var_Design_Coordinates(face, var->num_axis, coords.data());
374 FT_Done_MM_Var(qt_getFreetype(), var);
377 FT_Done_Face(tmpFace);
380 newFreetype->face = face;
381 newFreetype->mm_var =
nullptr;
382 if (FT_IS_NAMED_INSTANCE(newFreetype->face)) {
384 ftresult = FT_Get_MM_Var(face, &newFreetype->mm_var);
385 if (ftresult != FT_Err_Ok)
386 newFreetype->mm_var =
nullptr;
389 newFreetype->ref.storeRelaxed(1);
390 newFreetype->xsize = 0;
391 newFreetype->ysize = 0;
392 newFreetype->matrix.xx = 0x10000;
393 newFreetype->matrix.yy = 0x10000;
394 newFreetype->matrix.xy = 0;
395 newFreetype->matrix.yx = 0;
396 newFreetype->unicode_map =
nullptr;
397 newFreetype->symbol_map =
nullptr;
399 memset(newFreetype->cmapCache, 0,
sizeof(newFreetype->cmapCache));
401 for (
int i = 0; i < newFreetype->face->num_charmaps; ++i) {
402 FT_CharMap cm = newFreetype->face->charmaps[i];
403 switch(cm->encoding) {
404 case FT_ENCODING_UNICODE:
405 newFreetype->unicode_map = cm;
407 case FT_ENCODING_APPLE_ROMAN:
408 case FT_ENCODING_ADOBE_LATIN_1:
409 if (!newFreetype->unicode_map || newFreetype->unicode_map->encoding != FT_ENCODING_UNICODE)
410 newFreetype->unicode_map = cm;
412 case FT_ENCODING_ADOBE_CUSTOM:
413 case FT_ENCODING_MS_SYMBOL:
414 if (!newFreetype->symbol_map)
415 newFreetype->symbol_map = cm;
422 if (!FT_IS_SCALABLE(newFreetype->face) && newFreetype->face->num_fixed_sizes == 1)
423 FT_Set_Char_Size(face, newFreetype->face->available_sizes[0].x_ppem, newFreetype->face->available_sizes[0].y_ppem, 0, 0);
425 FT_Set_Charmap(newFreetype->face, newFreetype->unicode_map);
428 freetypeData->faces.insert(face_id, newFreetype.get());
430 newFreetype.release()->release(face_id);
434 freetype = newFreetype.release();
440void QFreetypeFace::cleanup()
444 FT_Done_MM_Var(qt_getFreetype(), mm_var);
450void QFreetypeFace::release(
const QFontEngine::FaceId &face_id)
453 bool deleteThis = !ref.deref();
459 if (face && ref.loadRelaxed() == 1) {
460 QtFreetypeData *freetypeData = qt_getFreetypeData();
462 freetypeData->staleFaces.removeIf([&deleteThis,
this](QFreetypeFace *face){
463 if (face->ref.loadRelaxed() == 1) {
474 for (
auto it = freetypeData->faces.constBegin();
475 it != freetypeData->faces.constEnd();
476 it = freetypeData->faces.erase(it)) {
477 if (it.value()->ref.loadRelaxed() == 1) {
478 it.value()->cleanup();
479 if (it.value() ==
this)
484 freetypeData->staleFaces.append(it.value());
488 if (freetypeData->faces.isEmpty() && freetypeData->staleFaces.isEmpty()) {
489 FT_Done_FreeType(freetypeData->library);
490 freetypeData->library =
nullptr;
500 FT_Library library = qt_getFreetype();
508 FT_Error error = FT_New_Face(library, faceFileName.toUtf8().constData(), faceIndex, &face);
509 if (error != FT_Err_Ok) {
510 qDebug() <<
"FT_New_Face failed for face index" << faceIndex <<
':' << Qt::hex << error;
514 const bool found = QLatin1StringView(face->style_name) == styleName;
515 numFaces = face->num_faces;
521 }
while (++faceIndex < numFaces);
527int QFreetypeFace::getFaceIndexByStyleName(
const QString &faceFileName,
const QString &styleName)
529 QtFreetypeData *freetypeData = qt_getFreetypeData();
532 QtFreetypeData::FaceStyle faceStyle(faceFileName, styleName);
533 int faceIndex = freetypeData->faceIndices.value(faceStyle, -1);
538 faceIndex = computeFaceIndex(faceFileName, styleName);
540 freetypeData->faceIndices.insert(faceStyle, faceIndex);
545void QFreetypeFace::computeSize(
const QFontDef &fontDef,
int *xsize,
int *ysize,
bool *outline_drawing, QFixed *scalableBitmapScaleFactor)
547 *ysize = qRound(fontDef.pixelSize * 64);
548 *xsize = *ysize * fontDef.stretch / 100;
549 *scalableBitmapScaleFactor = 1;
550 *outline_drawing =
false;
552 if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) {
554 if (!isScalableBitmap()) {
556
557
558
559 for (
int i = 1; i < face->num_fixed_sizes; i++) {
560 if (qAbs(*ysize - face->available_sizes[i].y_ppem) <
561 qAbs(*ysize - face->available_sizes[best].y_ppem) ||
562 (qAbs(*ysize - face->available_sizes[i].y_ppem) ==
563 qAbs(*ysize - face->available_sizes[best].y_ppem) &&
564 qAbs(*xsize - face->available_sizes[i].x_ppem) <
565 qAbs(*xsize - face->available_sizes[best].x_ppem))) {
571 for (
int i = 1; i < face->num_fixed_sizes; i++) {
572 if (face->available_sizes[i].y_ppem < *ysize) {
573 if (face->available_sizes[i].y_ppem > face->available_sizes[best].y_ppem)
575 }
else if (face->available_sizes[best].y_ppem < *ysize) {
577 }
else if (face->available_sizes[i].y_ppem < face->available_sizes[best].y_ppem) {
585 if (FT_Select_Size(face, best) == 0) {
586 const auto bestHeight = face->available_sizes[best].height;
587 if (isScalableBitmap() && bestHeight != 0)
588 *scalableBitmapScaleFactor = QFixed::fromReal((qreal)fontDef.pixelSize / bestHeight);
589 *xsize = face->available_sizes[best].x_ppem;
590 *ysize = face->available_sizes[best].y_ppem;
595#if defined FT_HAS_COLOR
596 if (FT_HAS_COLOR(face)) {
597 *outline_drawing =
false;
601 int maxCachedGlyphSize = QFontEngine::maxCachedGlyphSize();
602 *outline_drawing = (*xsize > (maxCachedGlyphSize << 6) || *ysize > (maxCachedGlyphSize << 6));
607QFontEngine::Properties QFreetypeFace::properties()
const
609 QFontEngine::Properties p;
610 p.postscriptName = FT_Get_Postscript_Name(face);
611 PS_FontInfoRec font_info;
612 if (FT_Get_PS_Font_Info(face, &font_info) == 0)
613 p.copyright = font_info.notice;
614 if (FT_IS_SCALABLE(face)
615#if defined(FT_HAS_COLOR)
616 && !FT_HAS_COLOR(face)
619 p.ascent = face->ascender;
620 p.descent = -face->descender;
621 p.leading = face->height - face->ascender + face->descender;
622 p.emSquare = face->units_per_EM;
623 p.boundingBox = QRectF(face->bbox.xMin, -face->bbox.yMax,
624 face->bbox.xMax - face->bbox.xMin,
625 face->bbox.yMax - face->bbox.yMin);
627 p.ascent = QFixed::fromFixed(face->size->metrics.ascender);
628 p.descent = QFixed::fromFixed(-face->size->metrics.descender);
629 p.leading = QFixed::fromFixed(face->size->metrics.height - face->size->metrics.ascender + face->size->metrics.descender);
630 p.emSquare = face->size->metrics.y_ppem;
632 p.boundingBox = QRectF(0, -p.ascent.toReal(),
633 face->size->metrics.max_advance/64, (p.ascent + p.descent).toReal() );
636 p.capHeight = p.ascent;
637 p.lineWidth = face->underline_thickness;
642bool QFreetypeFace::getSfntTable(uint tag, uchar *buffer, uint *length)
const
644 return ft_getSfntTable(face, tag, buffer, length);
648
649
650
651
652
653
654
655
656static void scaleOutline(FT_Face face, FT_GlyphSlot g, FT_Fixed x_scale, FT_Fixed y_scale)
658 x_scale = FT_MulDiv(x_scale, 1 << 10, face->units_per_EM);
659 y_scale = FT_MulDiv(y_scale, 1 << 10, face->units_per_EM);
660 FT_Vector *p = g->outline.points;
661 const FT_Vector *e = p + g->outline.n_points;
663 p->x = FT_MulFix(p->x, x_scale);
664 p->y = FT_MulFix(p->y, y_scale);
669#define GLYPH2PATH_DEBUG QT_NO_QDEBUG_MACRO
670void QFreetypeFace::addGlyphToPath(FT_Face face, FT_GlyphSlot g,
const QFixedPoint &point, QPainterPath *path, FT_Fixed x_scale, FT_Fixed y_scale)
672 const qreal factor = 1/64.;
673 scaleOutline(face, g, x_scale, y_scale);
675 QPointF cp = point.toPointF();
679 for (
int j = 0; j < g->outline.n_contours; ++j) {
680 int last_point = g->outline.contours[j];
682 QPointF start = QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
683 if (!(g->outline.tags[i] & 1)) {
684 if (!(g->outline.tags[last_point] & 1)) {
686 start = (QPointF(g->outline.points[last_point].x*factor,
687 -g->outline.points[last_point].y*factor) + start) / 2.0;
690 start = QPointF(g->outline.points[last_point].x*factor,
691 -g->outline.points[last_point].y*factor);
702 while (i < last_point) {
704 c[n] = cp + QPointF(g->outline.points[i].x*factor, -g->outline.points[i].y*factor);
705 GLYPH2PATH_DEBUG() <<
" " << i << c[n] <<
"tag =" << (
int)g->outline.tags[i]
706 <<
": on curve =" << (
bool)(g->outline.tags[i] & 1);
708 switch (g->outline.tags[i] & 3) {
713 c[3] = (c[3] + c[2])/2;
720 c[3] = (c[1] + c[2])/2;
721 c[2] = (2*c[1] + c[3])/3;
722 c[1] = (2*c[1] + c[0])/3;
735 c[2] = (2*c[1] + c[3])/3;
736 c[1] = (2*c[1] + c[0])/3;
741 path->cubicTo(c[1], c[2], c[3]);
748 path->closeSubpath();
752 c[2] = (2*c[1] + c[3])/3;
753 c[1] = (2*c[1] + c[0])/3;
756 path->cubicTo(c[1], c[2], c[3]);
762extern void qt_addBitmapToPath(qreal x0, qreal y0,
const uchar *image_data,
int bpl,
int w,
int h, QPainterPath *path);
764void QFreetypeFace::addBitmapToPath(FT_GlyphSlot slot,
const QFixedPoint &point, QPainterPath *path)
766 if (slot->format != FT_GLYPH_FORMAT_BITMAP
767 || slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO)
770 QPointF cp = point.toPointF();
771 qt_addBitmapToPath(cp.x() +
TRUNC(slot->metrics.horiBearingX), cp.y() -
TRUNC(slot->metrics.horiBearingY),
772 slot->bitmap.buffer, slot->bitmap.pitch, slot->bitmap.width, slot->bitmap.rows, path);
775static inline void convertRGBToARGB(
const uchar *src, uint *dst,
int width,
int height,
int src_pitch,
bool bgr)
777 const int offs = bgr ? -1 : 1;
778 const int w = width * 3;
781 for (
int x = 0; x < w; x += 3) {
782 uchar red = src[x + 1 - offs];
783 uchar green = src[x + 1];
784 uchar blue = src[x + 1 + offs];
785 *dd++ = (0xFFU << 24) | (red << 16) | (green << 8) | blue;
792static inline void convertRGBToARGB_V(
const uchar *src, uint *dst,
int width,
int height,
int src_pitch,
bool bgr)
794 const int offs = bgr ? -src_pitch : src_pitch;
796 for (
int x = 0; x < width; x++) {
797 uchar red = src[x + src_pitch - offs];
798 uchar green = src[x + src_pitch];
799 uchar blue = src[x + src_pitch + offs];
800 *dst++ = (0XFFU << 24) | (red << 16) | (green << 8) | blue;
808 static int type = -1;
810 if (QScreen *screen = QGuiApplication::primaryScreen())
811 type = screen->handle()->subpixelAntialiasingTypeHint();
813 return static_cast<QFontEngine::SubpixelAntialiasingType>(type);
816QFontEngineFT *QFontEngineFT::create(
const QFontDef &fontDef, FaceId faceId,
const QByteArray &fontData)
818 auto engine = std::make_unique<QFontEngineFT>(fontDef);
820 QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_Mono;
821 const bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
824 QFontEngine::SubpixelAntialiasingType subpixelType = subpixelAntialiasingTypeHint();
825 if (subpixelType == QFontEngine::Subpixel_None || (fontDef.styleStrategy & QFont::NoSubpixelAntialias)) {
826 format = QFontEngineFT::Format_A8;
827 engine->subpixelType = QFontEngine::Subpixel_None;
829 format = QFontEngineFT::Format_A32;
830 engine->subpixelType = subpixelType;
834 if (!engine->init(faceId, antialias, format, fontData) || engine->invalid()) {
835 qWarning(
"QFontEngineFT: Failed to create FreeType font engine");
839 engine->setQtDefaultHintStyle(
static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
840 return engine.release();
845 FT_MM_Var *var = freetypeFace->mm_var;
846 if (var !=
nullptr && faceId.instanceIndex >= 0 && FT_UInt(faceId.instanceIndex) < var->num_namedstyles) {
847 for (FT_UInt axis = 0; axis < var->num_axis; ++axis) {
848 if (var->axis[axis].tag == QFont::Tag(
"wght").value()) {
849 return var->namedstyle[faceId.instanceIndex].coords[axis] >> 16;
853 if (
const TT_OS2 *os2 =
reinterpret_cast<
const TT_OS2 *>(FT_Get_Sfnt_Table(face, ft_sfnt_os2))) {
854 return os2->usWeightClass;
861 class QFontEngineFTRawData:
public QFontEngineFT
864 QFontEngineFTRawData(
const QFontDef &fontDef) : QFontEngineFT(fontDef)
868 void updateFamilyNameAndStyle()
870 fontDef.families = QStringList(QString::fromLatin1(freetype->face->family_name));
872 if (freetype->face->style_flags & FT_STYLE_FLAG_ITALIC)
873 fontDef.style = QFont::StyleItalic;
875 if (freetype->face->style_flags & FT_STYLE_FLAG_BOLD)
876 fontDef.weight = QFont::Bold;
878 fontDef.weight = calculateActualWeight(freetype, freetype->face, faceId());
882 const QMap<QFont::Tag,
float> &variableAxisValues,
886 faceId.filename =
"";
888 faceId.uuid = QUuid::createUuid().toByteArray();
889 faceId.variableAxes = variableAxisValues;
890 faceId.instanceIndex = instanceIndex;
892 return init(faceId,
true, Format_None, fontData);
897QFontEngineFT *QFontEngineFT::create(
const QByteArray &fontData,
899 QFont::HintingPreference hintingPreference,
900 const QMap<QFont::Tag,
float> &variableAxisValues,
904 fontDef.pixelSize = pixelSize;
905 fontDef.stretch = QFont::Unstretched;
906 fontDef.hintingPreference = hintingPreference;
907 fontDef.variableAxisValues = variableAxisValues;
909 QFontEngineFTRawData *fe =
new QFontEngineFTRawData(fontDef);
910 if (!fe->initFromData(fontData, variableAxisValues, instanceIndex)) {
915 fe->updateFamilyNameAndStyle();
916 fe->setQtDefaultHintStyle(
static_cast<QFont::HintingPreference>(fontDef.hintingPreference));
921QFontEngineFT::QFontEngineFT(
const QFontDef &fd)
922 : QFontEngine(Freetype)
929 cache_cost = 100 * 1024;
930 kerning_pairs_loaded =
false;
936 default_load_flags = FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
937 default_hint_style = ftInitialDefaultHintStyle;
938 subpixelType = Subpixel_None;
939 lcdFilterType = (
int)((quintptr) FT_LCD_FILTER_DEFAULT);
940 defaultFormat = Format_None;
941 embeddedbitmap =
false;
942 const QByteArray env = qgetenv(
"QT_NO_FT_CACHE");
943 cacheEnabled = env.isEmpty() || env.toInt() == 0;
944 m_subPixelPositionCount = 4;
945 forceAutoHint =
false;
946 stemDarkeningDriver =
false;
947 verticalHintingOnly =
false;
950QFontEngineFT::~QFontEngineFT()
953 freetype->release(face_id);
956bool QFontEngineFT::init(FaceId faceId,
bool antialias, GlyphFormat format,
957 const QByteArray &fontData)
959 return init(faceId, antialias, format, QFreetypeFace::getFace(faceId, fontData));
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;
975 return (face->style_flags & FT_STYLE_FLAG_ITALIC);
978bool QFontEngineFT::init(FaceId faceId,
bool antialias, GlyphFormat format,
979 QFreetypeFace *freetypeFace)
981 freetype = freetypeFace;
987 defaultFormat = format;
988 this->antialias = antialias;
991 glyphFormat = QFontEngine::Format_Mono;
993 glyphFormat = defaultFormat;
997 symbol = freetype->symbol_map !=
nullptr;
998 PS_FontInfoRec psrec;
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));
1004 freetype->computeSize(fontDef, &xsize, &ysize, &defaultGlyphSet.outline_drawing, &scalableBitmapScaleFactor);
1006 FT_Face face = lockFace();
1008 if (FT_IS_SCALABLE(face)
1009#if defined(FT_HAS_COLOR)
1010 && !FT_HAS_COLOR(face)
1013 bool isItalic = calculateActualItalic(freetype, face, faceId);
1014 bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !isItalic && !qEnvironmentVariableIsSet(
"QT_NO_SYNTHESIZED_ITALIC");
1017 FT_Set_Transform(face, &matrix,
nullptr);
1018 freetype->matrix = matrix;
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"))) {
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;
1033 int score = fontDef.weight * fontDef.pixelSize;
1034 line_thickness = score / 7000;
1036 if (line_thickness < 2 && score >= 1050)
1038 underline_position = ((line_thickness * 2) + 3) / 6;
1040 cacheEnabled =
false;
1041#if defined(FT_HAS_COLOR)
1042 if (FT_HAS_COLOR(face))
1043 glyphFormat = defaultFormat = GlyphFormat::Format_ARGB;
1046 if (line_thickness < 1)
1049 metrics = face->size->metrics;
1052
1053
1054
1055
1056
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;
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;
1072 metrics.height = metrics.ascender - metrics.descender + leading;
1074 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
1076 face->face_flags |= FT_FACE_FLAG_SCALABLE;
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;
1089 stemDarkeningDriver =
false;
1093 fontDef.styleName = QString::fromUtf8(face->style_name);
1095 if (!freetype->hbFace) {
1096 faceData.user_data = face;
1097 faceData.get_font_table = ft_getSfntTable;
1098 (
void)harfbuzzFace();
1099 freetype->hbFace = std::move(face_);
1104 face_ = Holder(freetype->hbFace.get(), dont_delete);
1108 fsType = freetype->fsType();
1112void QFontEngineFT::setQtDefaultHintStyle(QFont::HintingPreference hintingPreference)
1114 switch (hintingPreference) {
1115 case QFont::PreferNoHinting:
1116 setDefaultHintStyle(HintNone);
1118 case QFont::PreferFullHinting:
1119 setDefaultHintStyle(HintFull);
1121 case QFont::PreferVerticalHinting:
1122 setDefaultHintStyle(HintLight);
1124 case QFont::PreferDefaultHinting:
1125 setDefaultHintStyle(ftInitialDefaultHintStyle);
1130void QFontEngineFT::setDefaultHintStyle(HintStyle style)
1132 default_hint_style = style;
1138#if defined(FT_FONT_FORMATS_H)
1139 FT_UInt version = 0;
1140 if (FT_Property_Get(library,
"truetype",
"interpreter-version", &version) != FT_Err_Ok)
1142 return version == 40;
1150bool QFontEngineFT::setVerticalHintingOnly()
1155 if ((default_hint_style == HintMedium || default_hint_style == HintFull)
1156 && !hasTrueTypeInterpreterV40(qt_getFreetype())) {
1159 verticalHintingOnly =
true;
1163bool QFontEngineFT::expectsGammaCorrectedBlending(QFontEngine::GlyphFormat format)
const
1166 return stemDarkeningDriver;
1169int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format,
int flags,
1170 bool &hsubpixel,
int &vfactor)
const
1172 int load_flags = FT_LOAD_DEFAULT | default_load_flags;
1173 int load_target = default_hint_style == HintLight
1174 ? FT_LOAD_TARGET_LIGHT
1175 : verticalHintingOnly
1176 ? FT_LOAD_TARGET_LCD
1177 : FT_LOAD_TARGET_NORMAL;
1179 if (format == Format_Mono) {
1180 load_target = FT_LOAD_TARGET_MONO;
1181 }
else if (format == Format_A32) {
1182 if (subpixelType == Subpixel_RGB || subpixelType == Subpixel_BGR)
1184 else if (subpixelType == Subpixel_VRGB || subpixelType == Subpixel_VBGR)
1186 }
else if (format == Format_ARGB) {
1188 load_flags |= FT_LOAD_COLOR;
1192 if (set && set->outline_drawing)
1193 load_flags |= FT_LOAD_NO_BITMAP;
1195 if (default_hint_style == HintNone || (flags & DesignMetrics) || (set && set->outline_drawing))
1196 load_flags |= FT_LOAD_NO_HINTING;
1198 load_flags |= load_target;
1201 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1209 return info.width > 0xFF || info.height > 0xFF || info.linearAdvance > 0x7FFF;
1218 FT_Vector_Transform(&vector, matrix);
1223 FT_Vector_Transform(&vector, matrix);
1224 if (l > vector.x) l = vector.x;
1225 if (r < vector.x) r = vector.x;
1226 if (t < vector.y) t = vector.y;
1227 if (b > vector.y) b = vector.y;
1230 FT_Vector_Transform(&vector, matrix);
1231 if (l > vector.x) l = vector.x;
1232 if (r < vector.x) r = vector.x;
1233 if (t < vector.y) t = vector.y;
1234 if (b > vector.y) b = vector.y;
1237 FT_Vector_Transform(&vector, matrix);
1238 if (l > vector.x) l = vector.x;
1239 if (r < vector.x) r = vector.x;
1240 if (t < vector.y) t = vector.y;
1241 if (b > vector.y) b = vector.y;
1248#if defined(QFONTENGINE_FT_SUPPORT_COLRV1)
1249#define FROM_FIXED_16_16(value) (value / 65536.0
)
1251static inline QTransform FTAffineToQTransform(
const FT_Affine23 &matrix)
1253 qreal m11 = FROM_FIXED_16_16(matrix.xx);
1254 qreal m21 = -FROM_FIXED_16_16(matrix.xy);
1255 qreal m12 = -FROM_FIXED_16_16(matrix.yx);
1256 qreal m22 = FROM_FIXED_16_16(matrix.yy);
1257 qreal dx = FROM_FIXED_16_16(matrix.dx);
1258 qreal dy = -FROM_FIXED_16_16(matrix.dy);
1260 return QTransform(m11, m12, m21, m22, dx, dy);
1263bool QFontEngineFT::traverseColr1(FT_OpaquePaint opaquePaint,
1264 QSet<std::pair<FT_Byte *, FT_Bool> > *loops,
1265 QColor foregroundColor,
1267 ushort paletteCount,
1268 QColrPaintGraphRenderer *paintGraphRenderer,
1271 FT_Face face = freetype->face;
1273 if (maxColrv1NestingDepth() > 0 && depth > maxColrv1NestingDepth()) {
1274 qCWarning(lcColrv1) <<
"COLRv1 graph nested deeper than" << maxColrv1NestingDepth()
1275 <<
"levels; aborting traversal";
1279 auto key = std::pair{opaquePaint.p, opaquePaint.insert_root_transform};
1280 if (loops->contains(key)) {
1281 qCWarning(lcColrv1) <<
"Cycle detected in COLRv1 graph";
1285 paintGraphRenderer->save();
1287 auto cleanup = qScopeGuard([&paintGraphRenderer, &key, &loops]() {
1289 paintGraphRenderer->restore();
1292 FT_COLR_Paint paint;
1293 if (!FT_Get_Paint(face, opaquePaint, &paint))
1296 if (paint.format == FT_COLR_PAINTFORMAT_COLR_LAYERS) {
1297 FT_OpaquePaint layerPaint;
1298 layerPaint.p =
nullptr;
1299 while (FT_Get_Paint_Layers(face, &paint.u.colr_layers.layer_iterator, &layerPaint)) {
1300 if (!traverseColr1(layerPaint, loops, foregroundColor, palette, paletteCount, paintGraphRenderer, depth + 1))
1303 }
else if (paint.format == FT_COLR_PAINTFORMAT_TRANSFORM
1304 || paint.format == FT_COLR_PAINTFORMAT_SCALE
1305 || paint.format == FT_COLR_PAINTFORMAT_TRANSLATE
1306 || paint.format == FT_COLR_PAINTFORMAT_ROTATE
1307 || paint.format == FT_COLR_PAINTFORMAT_SKEW) {
1310 FT_OpaquePaint nextPaint;
1311 switch (paint.format) {
1312 case FT_COLR_PAINTFORMAT_TRANSFORM:
1313 xform = FTAffineToQTransform(paint.u.transform.affine);
1314 nextPaint = paint.u.transform.paint;
1316 case FT_COLR_PAINTFORMAT_SCALE:
1318 qreal centerX = FROM_FIXED_16_16(paint.u.scale.center_x);
1319 qreal centerY = -FROM_FIXED_16_16(paint.u.scale.center_y);
1320 qreal scaleX = FROM_FIXED_16_16(paint.u.scale.scale_x);
1321 qreal scaleY = FROM_FIXED_16_16(paint.u.scale.scale_y);
1323 xform.translate(centerX, centerY);
1324 xform.scale(scaleX, scaleY);
1325 xform.translate(-centerX, -centerY);
1327 nextPaint = paint.u.scale.paint;
1330 case FT_COLR_PAINTFORMAT_ROTATE:
1332 qreal centerX = FROM_FIXED_16_16(paint.u.rotate.center_x);
1333 qreal centerY = -FROM_FIXED_16_16(paint.u.rotate.center_y);
1334 qreal angle = -FROM_FIXED_16_16(paint.u.rotate.angle) * 180.0;
1336 xform.translate(centerX, centerY);
1337 xform.rotate(angle);
1338 xform.translate(-centerX, -centerY);
1340 nextPaint = paint.u.rotate.paint;
1344 case FT_COLR_PAINTFORMAT_SKEW:
1346 qreal centerX = FROM_FIXED_16_16(paint.u.skew.center_x);
1347 qreal centerY = -FROM_FIXED_16_16(paint.u.skew.center_y);
1348 qreal angleX = FROM_FIXED_16_16(paint.u.skew.x_skew_angle) * M_PI;
1349 qreal angleY = -FROM_FIXED_16_16(paint.u.skew.y_skew_angle) * M_PI;
1351 xform.translate(centerX, centerY);
1352 xform.shear(qTan(angleX), qTan(angleY));
1353 xform.translate(-centerX, -centerY);
1355 nextPaint = paint.u.rotate.paint;
1358 case FT_COLR_PAINTFORMAT_TRANSLATE:
1360 qreal dx = FROM_FIXED_16_16(paint.u.translate.dx);
1361 qreal dy = -FROM_FIXED_16_16(paint.u.translate.dy);
1363 xform.translate(dx, dy);
1364 nextPaint = paint.u.rotate.paint;
1371 paintGraphRenderer->prependTransform(xform);
1372 if (!traverseColr1(nextPaint, loops, foregroundColor, palette, paletteCount, paintGraphRenderer, depth + 1))
1374 }
else if (paint.format == FT_COLR_PAINTFORMAT_LINEAR_GRADIENT
1375 || paint.format == FT_COLR_PAINTFORMAT_RADIAL_GRADIENT
1376 || paint.format == FT_COLR_PAINTFORMAT_SWEEP_GRADIENT
1377 || paint.format == FT_COLR_PAINTFORMAT_SOLID) {
1378 auto getPaletteColor = [&palette, &paletteCount, &foregroundColor](FT_UInt16 index,
1381 if (index < paletteCount) {
1382 const FT_Color &paletteColor = palette[index];
1383 color = qRgba(paletteColor.red,
1386 paletteColor.alpha);
1387 }
else if (index == 0xffff) {
1388 color = foregroundColor;
1391 if (color.isValid())
1392 color.setAlphaF(color.alphaF() * (alpha / 16384.0));
1397 auto gatherGradientStops = [&](FT_ColorStopIterator it) {
1399 ret.resize(it.num_color_stops);
1401 FT_ColorStop colorStop;
1402 while (FT_Get_Colorline_Stops(face, &colorStop, &it)) {
1403 uint index = it.current_color_stop - 1;
1404 if (qsizetype(index) < ret.size()) {
1405 QGradientStop &gradientStop = ret[index];
1406 gradientStop.first = FROM_FIXED_16_16(colorStop.stop_offset);
1407 gradientStop.second = getPaletteColor(colorStop.color.palette_index,
1408 colorStop.color.alpha);
1415 auto extendToSpread = [](FT_PaintExtend extend) {
1417 case FT_COLR_PAINT_EXTEND_REPEAT:
1418 return QGradient::RepeatSpread;
1419 case FT_COLR_PAINT_EXTEND_REFLECT:
1420 return QGradient::ReflectSpread;
1422 return QGradient::PadSpread;
1426 if (paintGraphRenderer->isRendering()) {
1427 if (paint.format == FT_COLR_PAINTFORMAT_LINEAR_GRADIENT) {
1428 const qreal p0x = FROM_FIXED_16_16(paint.u.linear_gradient.p0.x);
1429 const qreal p0y = -FROM_FIXED_16_16(paint.u.linear_gradient.p0.y);
1431 const qreal p1x = FROM_FIXED_16_16(paint.u.linear_gradient.p1.x);
1432 const qreal p1y = -FROM_FIXED_16_16(paint.u.linear_gradient.p1.y);
1434 const qreal p2x = FROM_FIXED_16_16(paint.u.linear_gradient.p2.x);
1435 const qreal p2y = -FROM_FIXED_16_16(paint.u.linear_gradient.p2.y);
1437 QPointF p0(p0x, p0y);
1438 QPointF p1(p1x, p1y);
1439 QPointF p2(p2x, p2y);
1441 const QGradient::Spread spread =
1442 extendToSpread(paint.u.linear_gradient.colorline.extend);
1443 const QGradientStops stops =
1444 gatherGradientStops(paint.u.linear_gradient.colorline.color_stop_iterator);
1445 paintGraphRenderer->setLinearGradient(p0, p1, p2, spread, stops);
1447 }
else if (paint.format == FT_COLR_PAINTFORMAT_RADIAL_GRADIENT) {
1448 const qreal c0x = FROM_FIXED_16_16(paint.u.radial_gradient.c0.x);
1449 const qreal c0y = -FROM_FIXED_16_16(paint.u.radial_gradient.c0.y);
1450 const qreal r0 = FROM_FIXED_16_16(paint.u.radial_gradient.r0);
1451 const qreal c1x = FROM_FIXED_16_16(paint.u.radial_gradient.c1.x);
1452 const qreal c1y = -FROM_FIXED_16_16(paint.u.radial_gradient.c1.y);
1453 const qreal r1 = FROM_FIXED_16_16(paint.u.radial_gradient.r1);
1455 const QPointF c0(c0x, c0y);
1456 const QPointF c1(c1x, c1y);
1457 const QGradient::Spread spread =
1458 extendToSpread(paint.u.radial_gradient.colorline.extend);
1459 const QGradientStops stops =
1460 gatherGradientStops(paint.u.radial_gradient.colorline.color_stop_iterator);
1462 paintGraphRenderer->setRadialGradient(c0, r0, c1, r1, spread, stops);
1463 }
else if (paint.format == FT_COLR_PAINTFORMAT_SWEEP_GRADIENT) {
1464 const qreal centerX = FROM_FIXED_16_16(paint.u.sweep_gradient.center.x);
1465 const qreal centerY = -FROM_FIXED_16_16(paint.u.sweep_gradient.center.y);
1466 const qreal startAngle = 180.0 * FROM_FIXED_16_16(paint.u.sweep_gradient.start_angle);
1467 const qreal endAngle = 180.0 * FROM_FIXED_16_16(paint.u.sweep_gradient.end_angle);
1469 const QPointF center(centerX, centerY);
1471 const QGradient::Spread spread = extendToSpread(paint.u.radial_gradient.colorline.extend);
1472 const QGradientStops stops = gatherGradientStops(paint.u.sweep_gradient.colorline.color_stop_iterator);
1474 paintGraphRenderer->setConicalGradient(center, startAngle, endAngle, spread, stops);
1476 }
else if (paint.format == FT_COLR_PAINTFORMAT_SOLID) {
1477 QColor color = getPaletteColor(paint.u.solid.color.palette_index,
1478 paint.u.solid.color.alpha);
1479 if (!color.isValid()) {
1480 qCWarning(lcColrv1) <<
"Invalid palette index in COLRv1 graph:"
1481 << paint.u.solid.color.palette_index;
1485 paintGraphRenderer->setSolidColor(color);
1489 paintGraphRenderer->drawCurrentPath();
1490 }
else if (paint.format == FT_COLR_PAINTFORMAT_COMPOSITE) {
1491 if (!paintGraphRenderer->isRendering()) {
1492 if (!traverseColr1(paint.u.composite.backdrop_paint,
1501 if (!traverseColr1(paint.u.composite.source_paint,
1511 QPainter::CompositionMode compositionMode = QPainter::CompositionMode_SourceOver;
1512 switch (paint.u.composite.composite_mode) {
1513 case FT_COLR_COMPOSITE_CLEAR:
1514 compositionMode = QPainter::CompositionMode_Clear;
1516 case FT_COLR_COMPOSITE_SRC:
1517 compositionMode = QPainter::CompositionMode_Source;
1519 case FT_COLR_COMPOSITE_DEST:
1520 compositionMode = QPainter::CompositionMode_Destination;
1522 case FT_COLR_COMPOSITE_SRC_OVER:
1523 compositionMode = QPainter::CompositionMode_SourceOver;
1525 case FT_COLR_COMPOSITE_DEST_OVER:
1526 compositionMode = QPainter::CompositionMode_DestinationOver;
1528 case FT_COLR_COMPOSITE_SRC_IN:
1529 compositionMode = QPainter::CompositionMode_SourceIn;
1531 case FT_COLR_COMPOSITE_DEST_IN:
1532 compositionMode = QPainter::CompositionMode_DestinationIn;
1534 case FT_COLR_COMPOSITE_SRC_OUT:
1535 compositionMode = QPainter::CompositionMode_SourceOut;
1537 case FT_COLR_COMPOSITE_DEST_OUT:
1538 compositionMode = QPainter::CompositionMode_DestinationOut;
1540 case FT_COLR_COMPOSITE_SRC_ATOP:
1541 compositionMode = QPainter::CompositionMode_SourceAtop;
1543 case FT_COLR_COMPOSITE_DEST_ATOP:
1544 compositionMode = QPainter::CompositionMode_DestinationAtop;
1546 case FT_COLR_COMPOSITE_XOR:
1547 compositionMode = QPainter::CompositionMode_Xor;
1549 case FT_COLR_COMPOSITE_PLUS:
1550 compositionMode = QPainter::CompositionMode_Plus;
1552 case FT_COLR_COMPOSITE_SCREEN:
1553 compositionMode = QPainter::CompositionMode_Screen;
1555 case FT_COLR_COMPOSITE_OVERLAY:
1556 compositionMode = QPainter::CompositionMode_Overlay;
1558 case FT_COLR_COMPOSITE_DARKEN:
1559 compositionMode = QPainter::CompositionMode_Darken;
1561 case FT_COLR_COMPOSITE_LIGHTEN:
1562 compositionMode = QPainter::CompositionMode_Lighten;
1564 case FT_COLR_COMPOSITE_COLOR_DODGE:
1565 compositionMode = QPainter::CompositionMode_ColorDodge;
1567 case FT_COLR_COMPOSITE_COLOR_BURN:
1568 compositionMode = QPainter::CompositionMode_ColorBurn;
1570 case FT_COLR_COMPOSITE_HARD_LIGHT:
1571 compositionMode = QPainter::CompositionMode_HardLight;
1573 case FT_COLR_COMPOSITE_SOFT_LIGHT:
1574 compositionMode = QPainter::CompositionMode_SoftLight;
1576 case FT_COLR_COMPOSITE_DIFFERENCE:
1577 compositionMode = QPainter::CompositionMode_Difference;
1579 case FT_COLR_COMPOSITE_EXCLUSION:
1580 compositionMode = QPainter::CompositionMode_Exclusion;
1582 case FT_COLR_COMPOSITE_MULTIPLY:
1583 compositionMode = QPainter::CompositionMode_Multiply;
1586 qCWarning(lcColrv1) <<
"Unsupported COLRv1 composition mode" << paint.u.composite.composite_mode;
1590 QColrPaintGraphRenderer compositeRenderer;
1591 compositeRenderer.setBoundingRect(paintGraphRenderer->boundingRect());
1592 compositeRenderer.beginRender(fontDef.pixelSize / face->units_per_EM,
1593 paintGraphRenderer->currentTransform());
1594 if (!traverseColr1(paint.u.composite.backdrop_paint,
1604 compositeRenderer.setCompositionMode(compositionMode);
1605 if (!traverseColr1(paint.u.composite.source_paint,
1614 paintGraphRenderer->drawImage(compositeRenderer.endRender());
1616 }
else if (paint.format == FT_COLR_PAINTFORMAT_GLYPH) {
1617 FT_Error error = FT_Load_Glyph(face,
1618 paint.u.glyph.glyphID,
1619 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);
1621 qCWarning(lcColrv1) <<
"Failed to load glyph"
1622 << paint.u.glyph.glyphID
1623 <<
"in COLRv1 graph. Error: " << error;
1628 QFreetypeFace::addGlyphToPath(face,
1632 face->units_per_EM << 6,
1633 face->units_per_EM << 6);
1635 paintGraphRenderer->appendPath(path);
1637 if (!traverseColr1(paint.u.glyph.paint, loops, foregroundColor, palette, paletteCount, paintGraphRenderer, depth + 1))
1639 }
else if (paint.format == FT_COLR_PAINTFORMAT_COLR_GLYPH) {
1640 FT_OpaquePaint otherOpaquePaint;
1641 otherOpaquePaint.p =
nullptr;
1642 if (!FT_Get_Color_Glyph_Paint(face,
1643 paint.u.colr_glyph.glyphID,
1644 FT_COLOR_NO_ROOT_TRANSFORM,
1645 &otherOpaquePaint)) {
1646 qCWarning(lcColrv1) <<
"Failed to load color glyph"
1647 << paint.u.colr_glyph.glyphID
1648 <<
"in COLRv1 graph.";
1652 if (!traverseColr1(otherOpaquePaint, loops, foregroundColor, palette, paletteCount, paintGraphRenderer, depth + 1))
1659QFontEngineFT::Glyph *QFontEngineFT::loadColrv1Glyph(QGlyphSet *set,
1662 const QColor &foregroundColor,
1663 bool fetchMetricsOnly)
const
1665 FT_Face face = freetype->face;
1668 memset(&info, 0,
sizeof(info));
1672 FT_Load_Glyph(face, glyph, FT_LOAD_DEFAULT
1675 | FT_LOAD_BITMAP_METRICS_ONLY);
1676 info.linearAdvance =
int(face->glyph->linearHoriAdvance >> 10);
1677 info.xOff =
short(TRUNC(ROUND(face->glyph->advance.x)));
1679 FT_OpaquePaint opaquePaint;
1680 opaquePaint.p =
nullptr;
1681 if (!FT_Get_Color_Glyph_Paint(face, glyph, FT_COLOR_INCLUDE_ROOT_TRANSFORM, &opaquePaint))
1686 FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0);
1690 FT_Get_Transform(face, &matrix, &delta);
1691 QTransform originalXform(FROM_FIXED_16_16(matrix.xx), -FROM_FIXED_16_16(matrix.yx),
1692 -FROM_FIXED_16_16(matrix.xy), FROM_FIXED_16_16(matrix.yy),
1693 FROM_FIXED_16_16(delta.x), FROM_FIXED_16_16(delta.y));
1697 FT_Set_Transform(face,
nullptr,
nullptr);
1699 auto cleanup = qScopeGuard([&]() {
1701 FT_Set_Char_Size(face, xsize, ysize, 0, 0);
1702 FT_Set_Transform(face, &matrix, &delta);
1705 qCDebug(lcColrv1).noquote() <<
"================== Start collecting COLRv1 metrics for" << glyph;
1706 QRect designCoordinateBounds;
1710 if (colrv1_bounds_cache_id == glyph) {
1711 designCoordinateBounds = colrv1_bounds_cache;
1716 if (FT_Get_Color_Glyph_ClipBox(face, glyph, &clipBox)) {
1717 FT_Pos left = qMin(clipBox.bottom_left.x, qMin(clipBox.bottom_right.x, qMin(clipBox.top_left.x, clipBox.top_right.x)));
1718 FT_Pos right = qMax(clipBox.bottom_left.x, qMax(clipBox.bottom_right.x, qMax(clipBox.top_left.x, clipBox.top_right.x)));
1720 FT_Pos top = qMin(-clipBox.bottom_left.y, qMin(-clipBox.bottom_right.y, qMin(-clipBox.top_left.y, -clipBox.top_right.y)));
1721 FT_Pos bottom = qMax(-clipBox.bottom_left.y, qMax(-clipBox.bottom_right.y, qMax(-clipBox.top_left.y, -clipBox.top_right.y)));
1723 qreal scale = 1.0 / 64.0;
1724 designCoordinateBounds = QRect(QPoint(qFloor(left * scale), qFloor(top * scale)),
1725 QPoint(qCeil(right * scale), qCeil(bottom * scale)));
1728 QColrPaintGraphRenderer boundingRectCalculator;
1729 boundingRectCalculator.beginCalculateBoundingBox();
1730 QSet<std::pair<FT_Byte *, FT_Bool> > loops;
1731 if (traverseColr1(opaquePaint,
1736 &boundingRectCalculator)) {
1737 designCoordinateBounds = boundingRectCalculator.boundingRect().toAlignedRect();
1741 colrv1_bounds_cache_id = glyph;
1742 colrv1_bounds_cache = designCoordinateBounds;
1745 QTransform initialTransform;
1746 initialTransform.scale(fontDef.pixelSize / face->units_per_EM,
1747 fontDef.pixelSize / face->units_per_EM);
1748 QRect bounds = initialTransform.mapRect(designCoordinateBounds);
1749 bounds = originalXform.mapRect(bounds);
1751 info.x = bounds.left();
1752 info.y = -bounds.top();
1753 info.width = bounds.width();
1754 info.height = bounds.height();
1756 qCDebug(lcColrv1) <<
"Bounds of" << glyph <<
"==" << bounds;
1759 QImage destinationImage;
1760 if (!fetchMetricsOnly && !bounds.size().isEmpty()) {
1761 if (glyphBoundingBoxExceedsLimit(bounds.width(),
1763 effectivePixelSizeSquared(originalXform))) {
1767 FT_Palette_Data paletteData;
1768 if (FT_Palette_Data_Get(face, &paletteData))
1771 FT_Color *palette =
nullptr;
1772 FT_Error error = FT_Palette_Select(face, 0, &palette);
1774 qWarning(
"selecting palette for COLRv1 failed, err=%x face=%p, glyph=%d",
1780 if (palette ==
nullptr)
1783 ushort paletteCount = paletteData.num_palette_entries;
1785 QColrPaintGraphRenderer paintGraphRenderer;
1786 paintGraphRenderer.setBoundingRect(bounds);
1787 paintGraphRenderer.beginRender(fontDef.pixelSize / face->units_per_EM,
1791 QSet<std::pair<FT_Byte *, FT_Bool> > loops;
1792 if (!traverseColr1(opaquePaint,
1797 &paintGraphRenderer)) {
1801 destinationImage = paintGraphRenderer.endRender();
1804 if (fetchMetricsOnly || !destinationImage.isNull()) {
1809 set->setGlyph(glyph, QFixedPoint{}, g);
1812 g->linearAdvance = info.linearAdvance;
1813 g->width = info.width;
1814 g->height = info.height;
1817 g->advance = info.xOff;
1818 g->format = Format_ARGB;
1820 if (!fetchMetricsOnly && !destinationImage.isNull()) {
1821 g->data =
new uchar[info.height * info.width * 4];
1822 memcpy(g->data, destinationImage.constBits(), info.height * info.width * 4);
1835QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
1836 const QFixedPoint &subPixelPosition,
1839 bool fetchMetricsOnly,
1840 bool disableOutlineDrawing)
const
1844 if (format == Format_None)
1845 format = defaultFormat != Format_None ? defaultFormat : Format_Mono;
1846 Q_ASSERT(format != Format_None);
1848 Glyph *g = set ? set->getGlyph(glyph, subPixelPosition) :
nullptr;
1849 if (g && g->format == format && (fetchMetricsOnly || g->data))
1852 if (!g && set && set->isGlyphMissing(glyph))
1856 FT_Face face = freetype->face;
1858 FT_Matrix matrix = freetype->matrix;
1859 bool transform = matrix.xx != 0x10000
1860 || matrix.yy != 0x10000
1863 if (obliquen && transform) {
1868 slant.xx = 0x10000L;
1870 slant.xy = obliqueSlant;
1871 slant.yy = 0x10000L;
1873 FT_Matrix_Multiply(&matrix, &slant);
1878 v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.x.value());
1879 v.y = format == Format_Mono ? 0 : FT_Pos(-subPixelPosition.y.value());
1880 FT_Set_Transform(face, &matrix, &v);
1882 bool hsubpixel =
false;
1884 int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor);
1886 if (transform || obliquen || (format != Format_Mono && !isScalableBitmap()))
1887 load_flags |= FT_LOAD_NO_BITMAP;
1889#if (FREETYPE_MAJOR*10000
+ FREETYPE_MINOR*100
+ FREETYPE_PATCH) >= 20701
1891 if (fetchMetricsOnly)
1892 load_flags |= FT_LOAD_BITMAP_METRICS_ONLY;
1895#if defined(QFONTENGINE_FT_SUPPORT_COLRV1)
1896 if (FT_IS_SCALABLE(freetype->face)
1897 && FT_HAS_COLOR(freetype->face)
1898 && (load_flags & FT_LOAD_COLOR)) {
1900 Glyph *ret = loadColrv1Glyph(set, g, glyph, color, fetchMetricsOnly);
1908 FT_Error err = FT_Load_Glyph(face, glyph, load_flags);
1909 if (err && (load_flags & FT_LOAD_NO_BITMAP)) {
1910 load_flags &= ~FT_LOAD_NO_BITMAP;
1911 err = FT_Load_Glyph(face, glyph, load_flags);
1913 if (err == FT_Err_Too_Few_Arguments) {
1915 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1916 err = FT_Load_Glyph(face, glyph, load_flags);
1917 }
else if (err == FT_Err_Execution_Too_Long) {
1920 qWarning(
"load glyph failed due to broken hinting bytecode in font, switching to auto hinting");
1921 default_load_flags |= FT_LOAD_FORCE_AUTOHINT;
1922 load_flags |= FT_LOAD_FORCE_AUTOHINT;
1923 err = FT_Load_Glyph(face, glyph, load_flags);
1925 if (err != FT_Err_Ok) {
1926 qWarning(
"load glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
1928 set->setGlyphMissing(glyph);
1932 FT_GlyphSlot slot = face->glyph;
1935 FT_GlyphSlot_Embolden(slot);
1936 if (obliquen && !transform) {
1937 FT_GlyphSlot_Oblique(slot);
1948 FT_Matrix_Multiply(&m, &matrix);
1952 info.linearAdvance = slot->linearHoriAdvance >> 10;
1956 if ((set && set->outline_drawing && !disableOutlineDrawing) || fetchMetricsOnly) {
1957 int left = slot->metrics.horiBearingX;
1958 int right = slot->metrics.horiBearingX + slot->metrics.width;
1959 int top = slot->metrics.horiBearingY;
1960 int bottom = slot->metrics.horiBearingY - slot->metrics.height;
1962 if (transform && slot->format != FT_GLYPH_FORMAT_BITMAP)
1963 transformBoundingBox(&left, &top, &right, &bottom, &matrix);
1966 right =
CEIL(right);
1967 bottom =
FLOOR(bottom);
1970 info.x =
TRUNC(left);
1971 info.y =
TRUNC(top);
1972 info.width =
TRUNC(right - left);
1973 info.height =
TRUNC(top - bottom);
1977 if (areMetricsTooLarge(info))
1982 g->linearAdvance = info.linearAdvance;
1983 g->width = info.width;
1984 g->height = info.height;
1987 g->advance = info.xOff;
1991 set->setGlyph(glyph, subPixelPosition, g);
1996 if (slot->format == FT_GLYPH_FORMAT_OUTLINE) {
1997 const QTransform xform(matrix.xx / 65536.0, matrix.xy / 65536.0,
1998 matrix.yx / 65536.0, matrix.yy / 65536.0, 0, 0);
2000 FT_Outline_Get_CBox(&slot->outline, &cbox);
2001 const qreal glyphWidth = qreal((cbox.xMax - cbox.xMin) >> 6);
2002 const qreal glyphHeight = qreal((cbox.yMax - cbox.yMin) >> 6);
2003 if (glyphBoundingBoxExceedsLimit(glyphWidth, glyphHeight, effectivePixelSizeSquared(xform))) {
2005 set->setGlyphMissing(glyph);
2010 int glyph_buffer_size = 0;
2011 std::unique_ptr<uchar[]> glyph_buffer;
2012 FT_Render_Mode renderMode = (default_hint_style == HintLight) ? FT_RENDER_MODE_LIGHT : FT_RENDER_MODE_NORMAL;
2015 renderMode = FT_RENDER_MODE_MONO;
2018 if (!hsubpixel && vfactor == 1) {
2019 qWarning(
"Format_A32 requested, but subpixel layout is unknown.");
2023 renderMode = hsubpixel ? FT_RENDER_MODE_LCD : FT_RENDER_MODE_LCD_V;
2031 FT_Library_SetLcdFilter(slot->library, (FT_LcdFilter)lcdFilterType);
2033 err = FT_Render_Glyph(slot, renderMode);
2034 FT_Library_SetLcdFilter(slot->library, FT_LCD_FILTER_NONE);
2036 if (err != FT_Err_Ok) {
2037 qWarning(
"render glyph failed err=%x face=%p, glyph=%d", err, face, glyph);
2041 info.height = slot->bitmap.rows;
2042 info.width = slot->bitmap.width;
2043 info.x = slot->bitmap_left;
2044 info.y = slot->bitmap_top;
2045 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD)
2046 info.width = info.width / 3;
2047 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V)
2048 info.height = info.height / vfactor;
2050 int pitch = (format == Format_Mono ? ((info.width + 31) & ~31) >> 3 :
2051 (format == Format_A8 ? (info.width + 3) & ~3 : info.width * 4));
2053 glyph_buffer_size = info.height * pitch;
2054 glyph_buffer.reset(
new uchar[glyph_buffer_size]);
2056 if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
2057 uchar *src = slot->bitmap.buffer;
2058 uchar *dst = glyph_buffer.get();
2059 int h = slot->bitmap.rows;
2061 if (format == Format_Mono) {
2062 int bytes = ((info.width + 7) & ~7) >> 3;
2064 memcpy (dst, src, bytes);
2066 src += slot->bitmap.pitch;
2068 }
else if (format == Format_A8) {
2070 for (
int x = 0; x <
int{info.width}; x++)
2071 dst[x] = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xff : 0x00);
2073 src += slot->bitmap.pitch;
2077 uint *dd =
reinterpret_cast<uint *>(dst);
2078 for (
int x = 0; x <
int{info.width}; x++)
2079 dd[x] = ((src[x >> 3] & (0x80 >> (x & 7))) ? 0xffffffff : 0x00000000);
2081 src += slot->bitmap.pitch;
2084 }
else if (slot->bitmap.pixel_mode == 7 ) {
2085 Q_ASSERT(format == Format_ARGB);
2086 uchar *src = slot->bitmap.buffer;
2087 uchar *dst = glyph_buffer.get();
2088 int h = slot->bitmap.rows;
2090#if Q_BYTE_ORDER == Q_BIG_ENDIAN
2091 const quint32 *srcPixel = (
const quint32 *)src;
2092 quint32 *dstPixel = (quint32 *)dst;
2093 for (
int x = 0; x <
static_cast<
int>(slot->bitmap.width); x++, srcPixel++, dstPixel++) {
2094 const quint32 pixel = *srcPixel;
2095 *dstPixel = qbswap(pixel);
2098 memcpy(dst, src, slot->bitmap.width * 4);
2100 dst += slot->bitmap.pitch;
2101 src += slot->bitmap.pitch;
2103 info.linearAdvance = info.xOff = slot->bitmap.width;
2104 }
else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
2105 if (format == Format_A8) {
2106 uchar *src = slot->bitmap.buffer;
2107 uchar *dst = glyph_buffer.get();
2108 int h = slot->bitmap.rows;
2109 int bytes = info.width;
2111 memcpy (dst, src, bytes);
2113 src += slot->bitmap.pitch;
2115 }
else if (format == Format_ARGB) {
2116 uchar *src = slot->bitmap.buffer;
2117 quint32 *dstPixel =
reinterpret_cast<quint32 *>(glyph_buffer.get());
2118 int h = slot->bitmap.rows;
2120 for (
int x = 0; x <
static_cast<
int>(slot->bitmap.width); ++x) {
2121 uchar alpha = src[x];
2122 float alphaF = alpha / 255.0;
2123 dstPixel[x] = qRgba(qRound(alphaF * color.red()),
2124 qRound(alphaF * color.green()),
2125 qRound(alphaF * color.blue()),
2128 src += slot->bitmap.pitch;
2129 dstPixel += info.width;
2132 }
else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
2133 Q_ASSERT(format == Format_A32);
2134 convertRGBToARGB(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_RGB);
2135 }
else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) {
2136 Q_ASSERT(format == Format_A32);
2137 convertRGBToARGB_V(slot->bitmap.buffer, (uint *)glyph_buffer.get(), info.width, info.height, slot->bitmap.pitch, subpixelType != Subpixel_VRGB);
2139 qWarning(
"QFontEngine: Glyph rendered in unknown pixel_mode=%d", slot->bitmap.pixel_mode);
2148 g->linearAdvance = info.linearAdvance;
2149 g->width = info.width;
2150 g->height = info.height;
2153 g->advance = info.xOff;
2156 g->data = glyph_buffer.release();
2159 set->setGlyph(glyph, subPixelPosition, g);
2164QFontEngine::FaceId QFontEngineFT::faceId()
const
2169QFontEngine::Properties QFontEngineFT::properties()
const
2171 Properties p = freetype->properties();
2172 if (p.postscriptName.isEmpty()) {
2173 p.postscriptName = QFontEngine::convertToPostscriptFontFamilyName(fontDef.family().toUtf8());
2179QFixed QFontEngineFT::emSquareSize()
const
2181 if (FT_IS_SCALABLE(freetype->face))
2182 return freetype->face->units_per_EM;
2184 return freetype->face->size->metrics.y_ppem;
2187bool QFontEngineFT::getSfntTableData(uint tag, uchar *buffer, uint *length)
const
2189 return ft_getSfntTable(freetype->face, tag, buffer, length);
2192int QFontEngineFT::synthesized()
const
2195 if ((fontDef.style != QFont::StyleNormal) && !(freetype->face->style_flags & FT_STYLE_FLAG_ITALIC))
2196 s = SynthesizedItalic;
2197 if ((fontDef.weight >= QFont::Bold) && !(freetype->face->style_flags & FT_STYLE_FLAG_BOLD))
2198 s |= SynthesizedBold;
2199 if (fontDef.stretch != 100 && FT_IS_SCALABLE(freetype->face))
2200 s |= SynthesizedStretch;
2204void QFontEngineFT::initializeHeightMetrics()
const
2206 m_ascent = QFixed::fromFixed(metrics.ascender);
2207 m_descent = QFixed::fromFixed(-metrics.descender);
2208 m_leading = QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender);
2210 QFontEngine::initializeHeightMetrics();
2212 if (scalableBitmapScaleFactor != 1) {
2213 m_ascent *= scalableBitmapScaleFactor;
2214 m_descent *= scalableBitmapScaleFactor;
2215 m_leading *= scalableBitmapScaleFactor;
2219QFixed QFontEngineFT::capHeight()
const
2221 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
2222 if (os2 && os2->version >= 2) {
2224 QFixed answer = QFixed::fromFixed(FT_MulFix(os2->sCapHeight, freetype->face->size->metrics.y_scale));
2228 return calculatedCapHeight();
2231QFixed QFontEngineFT::xHeight()
const
2233 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
2234 if (os2 && os2->sxHeight) {
2236 QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize();
2241 return QFontEngine::xHeight();
2244QFixed QFontEngineFT::averageCharWidth()
const
2246 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
2247 if (os2 && os2->xAvgCharWidth) {
2249 QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize();
2254 return QFontEngine::averageCharWidth();
2257qreal QFontEngineFT::maxCharWidth()
const
2259 QFixed max_advance = QFixed::fromFixed(metrics.max_advance);
2260 if (scalableBitmapScaleFactor != 1)
2261 max_advance *= scalableBitmapScaleFactor;
2262 return max_advance.toReal();
2265QFixed QFontEngineFT::lineThickness()
const
2267 return line_thickness;
2270QFixed QFontEngineFT::underlinePosition()
const
2272 return underline_position;
2275void QFontEngineFT::doKerning(QGlyphLayout *g, QFontEngine::ShaperFlags flags)
const
2277 if (!kerning_pairs_loaded) {
2278 kerning_pairs_loaded =
true;
2280 if (freetype->face->size->metrics.x_ppem != 0) {
2281 QFixed scalingFactor = emSquareSize() / QFixed(freetype->face->size->metrics.x_ppem);
2283 const_cast<QFontEngineFT *>(
this)->loadKerningPairs(scalingFactor);
2289 if (shouldUseDesignMetrics(flags))
2290 flags |= DesignMetrics;
2292 flags &= ~DesignMetrics;
2294 QFontEngine::doKerning(g, flags);
2301 m.xx = FT_Fixed(matrix.m11() * 65536);
2302 m.xy = FT_Fixed(-matrix.m21() * 65536);
2303 m.yx = FT_Fixed(-matrix.m12() * 65536);
2304 m.yy = FT_Fixed(matrix.m22() * 65536);
2309QFontEngineFT::QGlyphSet *QFontEngineFT::TransformedGlyphSets::findSet(
const QTransform &matrix,
const QFontDef &fontDef)
2311 FT_Matrix m = QTransformToFTMatrix(matrix);
2314 for (; i < nSets; ++i) {
2315 QGlyphSet *g = sets[i];
2318 if (g->transformationMatrix.xx == m.xx
2319 && g->transformationMatrix.xy == m.xy
2320 && g->transformationMatrix.yx == m.yx
2321 && g->transformationMatrix.yy == m.yy) {
2333 moveToFront(nSets - 1);
2335 sets[0] =
new QGlyphSet;
2336 QGlyphSet *gs = sets[0];
2337 Q_ASSERT(gs !=
nullptr);
2340 gs->transformationMatrix = m;
2341 const int maxCachedSize = maxCachedGlyphSize();
2342 gs->outline_drawing = fontDef.pixelSize * fontDef.pixelSize * qAbs(matrix.determinant()) > maxCachedSize * maxCachedSize;
2347void QFontEngineFT::TransformedGlyphSets::moveToFront(
int i)
2349 QGlyphSet *g = sets[i];
2351 sets[i] = sets[i - 1];
2358QFontEngineFT::QGlyphSet *QFontEngineFT::loadGlyphSet(
const QTransform &matrix)
2360 if (matrix.type() > QTransform::TxShear || !cacheEnabled)
2364 if (!FT_IS_SCALABLE(freetype->face))
2365 return matrix.type() <= QTransform::TxTranslate ? &defaultGlyphSet :
nullptr;
2367 return transformedGlyphSets.findSet(matrix, fontDef);
2370void QFontEngineFT::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics)
2372 FT_Face face = lockFace(Unscaled);
2373 FT_Set_Transform(face,
nullptr,
nullptr);
2374 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
2376 int left = face->glyph->metrics.horiBearingX;
2377 int right = face->glyph->metrics.horiBearingX + face->glyph->metrics.width;
2378 int top = face->glyph->metrics.horiBearingY;
2379 int bottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
2385 metrics->width = QFixed::fromFixed(right-left);
2386 metrics->height = QFixed::fromFixed(top-bottom);
2387 metrics->x = QFixed::fromFixed(left);
2388 metrics->y = QFixed::fromFixed(-top);
2389 metrics->xoff = QFixed::fromFixed(face->glyph->advance.x);
2391 if (!FT_IS_SCALABLE(freetype->face))
2392 QFreetypeFace::addBitmapToPath(face->glyph, p, path);
2394 QFreetypeFace::addGlyphToPath(face, face->glyph, p, path, face->units_per_EM << 6, face->units_per_EM << 6);
2396 FT_Set_Transform(face, &freetype->matrix,
nullptr);
2400bool QFontEngineFT::supportsTransformation(
const QTransform &transform)
const
2402 return transform.type() <= QTransform::TxRotate;
2405void QFontEngineFT::addOutlineToPath(qreal x, qreal y,
const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
2407 if (!glyphs.numGlyphs)
2410 if (FT_IS_SCALABLE(freetype->face)) {
2411 QFontEngine::addOutlineToPath(x, y, glyphs, path, flags);
2413 QVarLengthArray<QFixedPoint> positions;
2414 QVarLengthArray<glyph_t> positioned_glyphs;
2416 matrix.translate(x, y);
2417 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions);
2419 FT_Face face = lockFace(Unscaled);
2420 for (
int gl = 0; gl < glyphs.numGlyphs; gl++) {
2421 FT_UInt glyph = positioned_glyphs[gl];
2422 FT_Load_Glyph(face, glyph, FT_LOAD_TARGET_MONO);
2423 QFreetypeFace::addBitmapToPath(face->glyph, positions[gl], path);
2429void QFontEngineFT::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions,
int numGlyphs,
2430 QPainterPath *path, QTextItem::RenderFlags)
2432 FT_Face face = lockFace(Unscaled);
2434 for (
int gl = 0; gl < numGlyphs; gl++) {
2435 FT_UInt glyph = glyphs[gl];
2437 FT_Load_Glyph(face, glyph, FT_LOAD_NO_BITMAP);
2439 FT_GlyphSlot g = face->glyph;
2440 if (g->format != FT_GLYPH_FORMAT_OUTLINE)
2443 FT_GlyphSlot_Embolden(g);
2445 FT_GlyphSlot_Oblique(g);
2446 QFreetypeFace::addGlyphToPath(face, g, positions[gl], path, xsize, ysize);
2451glyph_t QFontEngineFT::glyphIndex(uint ucs4)
const
2453 glyph_t glyph = ucs4 < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[ucs4] : 0;
2455 FT_Face face = freetype->face;
2456 glyph = FT_Get_Char_Index(face, ucs4);
2460 if (ucs4 == QChar::Nbsp || ucs4 == QChar::Tabulation) {
2461 glyph = FT_Get_Char_Index(face, QChar::Space);
2462 }
else if (freetype->symbol_map) {
2469 FT_Set_Charmap(face, freetype->symbol_map);
2470 glyph = FT_Get_Char_Index(face, ucs4);
2471 FT_Set_Charmap(face, freetype->unicode_map);
2472 if (!glyph && symbol && ucs4 < 0x100)
2473 glyph = FT_Get_Char_Index(face, ucs4 + 0xf000);
2476 if (ucs4 < QFreetypeFace::cmapCacheSize)
2477 freetype->cmapCache[ucs4] = glyph;
2483QString QFontEngineFT::glyphName(glyph_t index)
const
2486 if (index >= glyph_t(glyphCount()))
2489 FT_Face face = freetype->face;
2490 if (face->face_flags & FT_FACE_FLAG_GLYPH_NAMES) {
2491 char glyphName[128] = {};
2492 if (FT_Get_Glyph_Name(face, index, glyphName,
sizeof(glyphName)) == 0)
2493 result = QString::fromUtf8(glyphName);
2496 return result.isEmpty() ? QFontEngine::glyphName(index) : result;
2499int QFontEngineFT::stringToCMap(
const QChar *str,
int len, QGlyphLayout *glyphs,
int *nglyphs,
2500 QFontEngine::ShaperFlags flags)
const
2502 Q_ASSERT(glyphs->numGlyphs >= *nglyphs);
2503 if (*nglyphs < len) {
2508 int mappedGlyphs = 0;
2510 if (freetype->symbol_map) {
2511 FT_Face face = freetype->face;
2512 QStringIterator it(str, str + len);
2513 while (it.hasNext()) {
2514 uint uc = it.next();
2515 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
2516 if ( !glyphs->glyphs[glyph_pos] ) {
2523 glyph_t glyph = FT_Get_Char_Index(face, uc);
2526 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
2528 glyph = FT_Get_Char_Index(face, uc);
2531 FT_Set_Charmap(face, freetype->symbol_map);
2532 glyph = FT_Get_Char_Index(face, uc);
2533 FT_Set_Charmap(face, freetype->unicode_map);
2534 if (!glyph && symbol && uc < 0x100)
2535 glyph = FT_Get_Char_Index(face, uc + 0xf000);
2537 glyphs->glyphs[glyph_pos] = glyph;
2538 if (uc < QFreetypeFace::cmapCacheSize)
2539 freetype->cmapCache[uc] = glyph;
2541 if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
2546 FT_Face face = freetype->face;
2547 QStringIterator it(str, str + len);
2548 while (it.hasNext()) {
2549 uint uc = it.next();
2550 glyphs->glyphs[glyph_pos] = uc < QFreetypeFace::cmapCacheSize ? freetype->cmapCache[uc] : 0;
2551 if (!glyphs->glyphs[glyph_pos]) {
2554 glyph_t glyph = FT_Get_Char_Index(face, uc);
2555 if (!glyph && (uc == 0xa0 || uc == 0x9)) {
2559 glyphs->glyphs[glyph_pos] = glyph;
2560 if (uc < QFreetypeFace::cmapCacheSize)
2561 freetype->cmapCache[uc] = glyph;
2564 if (glyphs->glyphs[glyph_pos] || isIgnorableChar(uc))
2570 *nglyphs = glyph_pos;
2571 glyphs->numGlyphs = glyph_pos;
2573 if (!(flags & GlyphIndicesOnly))
2574 recalcAdvances(glyphs, flags);
2576 return mappedGlyphs;
2579bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags)
const
2581 if (!FT_IS_SCALABLE(freetype->face))
2584 return supportsHorizontalSubPixelPositions() || (flags & DesignMetrics);
2587QFixed QFontEngineFT::scaledBitmapMetrics(QFixed m)
const
2589 return m * scalableBitmapScaleFactor;
2592glyph_metrics_t QFontEngineFT::scaledBitmapMetrics(
const glyph_metrics_t &m,
const QTransform &t)
const
2595 trans.setMatrix(t.m11(), t.m12(), t.m13(),
2596 t.m21(), t.m22(), t.m23(),
2598 const qreal scaleFactor = scalableBitmapScaleFactor.toReal();
2599 trans.scale(scaleFactor, scaleFactor);
2601 QRectF rect(m.x.toReal(), m.y.toReal(), m.width.toReal(), m.height.toReal());
2602 QPointF offset(m.xoff.toReal(), m.yoff.toReal());
2604 rect = trans.mapRect(rect);
2605 offset = trans.map(offset);
2607 glyph_metrics_t metrics;
2608 metrics.x = QFixed::fromReal(rect.x());
2609 metrics.y = QFixed::fromReal(rect.y());
2610 metrics.width = QFixed::fromReal(rect.width());
2611 metrics.height = QFixed::fromReal(rect.height());
2612 metrics.xoff = QFixed::fromReal(offset.x());
2613 metrics.yoff = QFixed::fromReal(offset.y());
2617void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags)
const
2619 FT_Face face =
nullptr;
2620 bool design = shouldUseDesignMetrics(flags);
2621 for (
int i = 0; i < glyphs->numGlyphs; i++) {
2622 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs->glyphs[i]) :
nullptr;
2624 GlyphFormat acceptableFormat = (defaultFormat != Format_None) ? defaultFormat : Format_Mono;
2625 if (g && g->format == acceptableFormat) {
2626 glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
2630 g = loadGlyph(cacheEnabled ? &defaultGlyphSet :
nullptr,
2637 glyphs->advances[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
2639 glyphs->advances[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10)
2640 : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round();
2641 if (!cacheEnabled && g != &emptyGlyph)
2645 if (scalableBitmapScaleFactor != 1)
2646 glyphs->advances[i] *= scalableBitmapScaleFactor;
2652glyph_metrics_t QFontEngineFT::boundingBox(
const QGlyphLayout &glyphs)
2654 FT_Face face =
nullptr;
2656 glyph_metrics_t overall;
2658 if (!isScalableBitmap()) {
2659 overall.y = -ascent();
2660 overall.height = ascent() + descent();
2662 overall.y = QFixed::fromFixed(-metrics.ascender);
2663 overall.height = QFixed::fromFixed(metrics.ascender - metrics.descender);
2668 for (
int i = 0; i < glyphs.numGlyphs; i++) {
2670 if (!glyphs.advances[i] || glyphs.attributes[i].dontPrint)
2672 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyphs.glyphs[i]) :
nullptr;
2676 g = loadGlyph(cacheEnabled ? &defaultGlyphSet :
nullptr,
2684 QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
2685 QFixed y = overall.yoff + glyphs.offsets[i].y - g->y;
2686 overall.x = qMin(overall.x, x);
2687 overall.y = qMin(overall.y, y);
2688 xmax = qMax(xmax, x.ceil() + g->width);
2689 ymax = qMax(ymax, y.ceil() + g->height);
2690 if (!cacheEnabled && g != &emptyGlyph)
2693 int left =
FLOOR(face->glyph->metrics.horiBearingX);
2694 int right =
CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
2695 int top =
CEIL(face->glyph->metrics.horiBearingY);
2696 int bottom =
FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
2698 QFixed x = overall.xoff + glyphs.offsets[i].x - (-
TRUNC(left));
2699 QFixed y = overall.yoff + glyphs.offsets[i].y -
TRUNC(top);
2700 overall.x = qMin(overall.x, x);
2701 overall.y = qMin(overall.y, y);
2702 xmax = qMax(xmax, x +
TRUNC(right - left));
2703 ymax = qMax(ymax, y +
TRUNC(top - bottom));
2705 overall.xoff += glyphs.effectiveAdvance(i);
2707 overall.height = qMax(overall.height, ymax - overall.y);
2708 overall.width = xmax - overall.x;
2713 if (isScalableBitmap())
2714 overall = scaledBitmapMetrics(overall, QTransform());
2718glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph)
2720 FT_Face face =
nullptr;
2721 glyph_metrics_t overall;
2722 Glyph *g = cacheEnabled ? defaultGlyphSet.getGlyph(glyph) :
nullptr;
2725 g = loadGlyph(cacheEnabled ? &defaultGlyphSet :
nullptr,
2735 overall.width = g->width;
2736 overall.height = g->height;
2737 overall.xoff = g->advance;
2738 if (!cacheEnabled && g != &emptyGlyph)
2741 int left =
FLOOR(face->glyph->metrics.horiBearingX);
2742 int right =
CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
2743 int top =
CEIL(face->glyph->metrics.horiBearingY);
2744 int bottom =
FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
2746 overall.width =
TRUNC(right-left);
2747 overall.height =
TRUNC(top-bottom);
2748 overall.x =
TRUNC(left);
2749 overall.y = -
TRUNC(top);
2750 overall.xoff =
TRUNC(
ROUND(face->glyph->advance.x));
2755 if (isScalableBitmap())
2756 overall = scaledBitmapMetrics(overall, QTransform());
2760glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph,
const QTransform &matrix)
2762 return alphaMapBoundingBox(glyph, QFixedPoint(), matrix, QFontEngine::Format_None);
2765glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph,
2766 const QFixedPoint &subPixelPosition,
2767 const QTransform &matrix,
2768 QFontEngine::GlyphFormat format)
2774 const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
2775 && matrix.type() > QTransform::TxTranslate;
2776 if (needsImageTransform && format == QFontEngine::Format_Mono)
2777 format = QFontEngine::Format_A8;
2778 Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, QColor(),
true,
true);
2780 glyph_metrics_t overall;
2784 overall.width = g->width;
2785 overall.height = g->height;
2786 overall.xoff = g->advance;
2787 if (!cacheEnabled && g != &emptyGlyph)
2790 FT_Face face = lockFace();
2791 int left =
FLOOR(face->glyph->metrics.horiBearingX);
2792 int right =
CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
2793 int top =
CEIL(face->glyph->metrics.horiBearingY);
2794 int bottom =
FLOOR(face->glyph->metrics.horiBearingY - face->glyph->metrics.height);
2796 overall.width =
TRUNC(right-left);
2797 overall.height =
TRUNC(top-bottom);
2798 overall.x =
TRUNC(left);
2799 overall.y = -
TRUNC(top);
2800 overall.xoff =
TRUNC(
ROUND(face->glyph->advance.x));
2804 if (isScalableBitmap() || needsImageTransform)
2805 overall = scaledBitmapMetrics(overall, matrix);
2811 if (glyph ==
nullptr || glyph->height == 0 || glyph->width == 0)
2814 QImage::Format format = QImage::Format_Invalid;
2815 int bytesPerLine = -1;
2816 switch (glyphFormat) {
2817 case QFontEngine::Format_Mono:
2818 format = QImage::Format_Mono;
2819 bytesPerLine = ((glyph->width + 31) & ~31) >> 3;
2821 case QFontEngine::Format_A8:
2822 format = QImage::Format_Alpha8;
2823 bytesPerLine = (glyph->width + 3) & ~3;
2825 case QFontEngine::Format_A32:
2826 format = QImage::Format_RGB32;
2827 bytesPerLine = glyph->width * 4;
2833 QImage img(
static_cast<
const uchar *>(glyph->data), glyph->width, glyph->height, bytesPerLine, format);
2834 if (format == QImage::Format_Mono)
2835 img.setColor(1, QColor(Qt::white).rgba());
2839QFontEngine::Glyph *QFontEngineFT::glyphData(glyph_t glyphIndex,
2840 const QFixedPoint &subPixelPosition,
2841 QFontEngine::GlyphFormat neededFormat,
2842 const QTransform &t)
2844 Q_ASSERT(cacheEnabled);
2847 neededFormat = Format_Mono;
2848 else if (neededFormat == Format_None && defaultFormat != Format_None)
2849 neededFormat = defaultFormat;
2850 else if (neededFormat == Format_None)
2851 neededFormat = Format_A8;
2853 Glyph *glyph = loadGlyphFor(glyphIndex, subPixelPosition, neededFormat, t, QColor());
2854 if (!glyph || !glyph->width || !glyph->height)
2862 return qFuzzyCompare(t.m11(), t.m22()) && qFuzzyCompare(t.m12(), -t.m21())
2863 && qFuzzyCompare(t.m11()*t.m22() - t.m12()*t.m21(), qreal(1.0));
2866QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g,
2867 const QFixedPoint &subPixelPosition,
2869 const QTransform &t,
2871 bool fetchBoundingBox,
2872 bool disableOutlineDrawing)
2874 QGlyphSet *glyphSet = loadGlyphSet(t);
2875 if (glyphSet !=
nullptr && glyphSet->outline_drawing && !disableOutlineDrawing && !fetchBoundingBox)
2878 Glyph *glyph = glyphSet !=
nullptr ? glyphSet->getGlyph(g, subPixelPosition) :
nullptr;
2879 if (!glyph || glyph->format != format || (!fetchBoundingBox && !glyph->data)) {
2880 QScopedValueRollback<HintStyle> saved_default_hint_style(default_hint_style);
2883 const qreal scaleX = qAbs(t.m11());
2884 const qreal scaleY = qAbs(t.m22());
2885 const bool hintAtDeviceSize = default_hint_style != HintNone
2886 && format != Format_ARGB
2887 && FT_IS_SCALABLE(freetype->face)
2888 && t.type() <= QTransform::TxScale
2889 && xsize * scaleX >= 64 && ysize * scaleY >= 64;
2891 FT_Matrix m =
this->matrix;
2892 if (hintAtDeviceSize) {
2895 setFaceSize(qRound(xsize * scaleX), qRound(ysize * scaleY));
2900 m.xy = qRound(obliqueSlant * (scaleX / scaleY - 1));
2912 if (t.type() >= QTransform::TxScale && !is2dRotation(t))
2913 default_hint_style = HintNone;
2916 FT_Matrix ftMatrix = glyphSet !=
nullptr ? glyphSet->transformationMatrix : QTransformToFTMatrix(t);
2917 FT_Matrix_Multiply(&ftMatrix, &m);
2919 freetype->matrix = m;
2920 glyph = loadGlyph(glyphSet, g, subPixelPosition, color, format,
false, disableOutlineDrawing);
2927QImage QFontEngineFT::alphaMapForGlyph(glyph_t g,
const QFixedPoint &subPixelPosition)
2929 return alphaMapForGlyph(g, subPixelPosition, QTransform());
2932QImage QFontEngineFT::alphaMapForGlyph(glyph_t g,
2933 const QFixedPoint &subPixelPosition,
2934 const QTransform &t)
2936 const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
2937 && t.type() > QTransform::TxTranslate;
2938 const GlyphFormat neededFormat = antialias || needsImageTransform ? Format_A8 : Format_Mono;
2940 Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, QColor(),
false,
true);
2942 QImage img = alphaMapFromGlyphData(glyph, neededFormat);
2943 if (needsImageTransform)
2944 img = img.transformed(t, Qt::FastTransformation);
2948 if (!cacheEnabled && glyph != &emptyGlyph)
2954QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g,
2955 const QFixedPoint &subPixelPosition,
2956 const QTransform &t)
2958 if (t.type() > QTransform::TxRotate)
2959 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
2961 const bool needsImageTransform = !FT_IS_SCALABLE(freetype->face)
2962 && t.type() > QTransform::TxTranslate;
2965 const GlyphFormat neededFormat = Format_A32;
2967 Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, QColor(),
false,
true);
2969 QImage img = alphaMapFromGlyphData(glyph, neededFormat);
2970 if (needsImageTransform)
2971 img = img.transformed(t, Qt::FastTransformation);
2975 if (!cacheEnabled && glyph != &emptyGlyph)
2981 return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
2984QImage QFontEngineFT::bitmapForGlyph(glyph_t g,
2985 const QFixedPoint &subPixelPosition,
2986 const QTransform &t,
2987 const QColor &color)
2989 Glyph *glyph = loadGlyphFor(g, subPixelPosition, defaultFormat, t, color);
2990 if (glyph ==
nullptr)
2994 if (defaultFormat == GlyphFormat::Format_ARGB)
2995 img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_ARGB32_Premultiplied).copy();
2996 else if (defaultFormat == GlyphFormat::Format_Mono)
2997 img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_Mono).copy();
2999 if (!img.isNull() && (scalableBitmapScaleFactor != 1 || (!t.isIdentity() && !isSmoothlyScalable))) {
3000 QTransform trans(t);
3001 const qreal scaleFactor = scalableBitmapScaleFactor.toReal();
3002 trans.scale(scaleFactor, scaleFactor);
3003 img = img.transformed(trans, Qt::SmoothTransformation);
3006 if (!cacheEnabled && glyph != &emptyGlyph)
3012void QFontEngineFT::removeGlyphFromCache(glyph_t glyph)
3014 defaultGlyphSet.removeGlyphFromCache(glyph, QFixedPoint());
3017int QFontEngineFT::glyphCount()
const
3020 FT_Face face = lockFace();
3022 count =
int(std::clamp(face->num_glyphs, FT_Long(0), FT_Long(INT_MAX)));
3028FT_Face QFontEngineFT::lockFace(Scaling scale)
const
3031 FT_Face face = freetype->face;
3032 if (scale == Unscaled) {
3033 if (FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0) == 0) {
3034 freetype->xsize = face->units_per_EM << 6;
3035 freetype->ysize = face->units_per_EM << 6;
3038 setFaceSize(xsize, ysize);
3040 if (freetype->matrix.xx != matrix.xx ||
3041 freetype->matrix.yy != matrix.yy ||
3042 freetype->matrix.xy != matrix.xy ||
3043 freetype->matrix.yx != matrix.yx) {
3044 freetype->matrix = matrix;
3045 FT_Set_Transform(face, &freetype->matrix,
nullptr);
3051void QFontEngineFT::setFaceSize(
int xsize,
int ysize)
const
3053 if (freetype->xsize != xsize || freetype->ysize != ysize) {
3054 FT_Set_Char_Size(freetype->face, xsize, ysize, 0, 0);
3055 freetype->xsize = xsize;
3056 freetype->ysize = ysize;
3060void QFontEngineFT::unlockFace()
const
3065FT_Face QFontEngineFT::non_locked_face()
const
3067 return freetype->face;
3071QFontEngineFT::QGlyphSet::QGlyphSet()
3072 : outline_drawing(
false)
3074 transformationMatrix.xx = 0x10000;
3075 transformationMatrix.yy = 0x10000;
3076 transformationMatrix.xy = 0;
3077 transformationMatrix.yx = 0;
3078 memset(fast_glyph_data, 0,
sizeof(fast_glyph_data));
3079 fast_glyph_count = 0;
3082QFontEngineFT::QGlyphSet::~QGlyphSet()
3087void QFontEngineFT::QGlyphSet::clear()
3089 if (fast_glyph_count > 0) {
3090 for (
int i = 0; i < 256; ++i) {
3091 if (fast_glyph_data[i]) {
3092 delete fast_glyph_data[i];
3093 fast_glyph_data[i] =
nullptr;
3096 fast_glyph_count = 0;
3098 qDeleteAll(glyph_data);
3102void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index,
3103 const QFixedPoint &subPixelPosition)
3105 if (useFastGlyphData(index, subPixelPosition)) {
3106 if (fast_glyph_data[index]) {
3107 delete fast_glyph_data[index];
3108 fast_glyph_data[index] =
nullptr;
3109 if (fast_glyph_count > 0)
3113 delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition));
3117void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index,
3118 const QFixedPoint &subPixelPosition,
3121 if (useFastGlyphData(index, subPixelPosition)) {
3122 if (!fast_glyph_data[index])
3124 fast_glyph_data[index] = glyph;
3126 glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph);
3130int QFontEngineFT::getPointInOutline(glyph_t glyph,
int flags, quint32 point, QFixed *xpos, QFixed *ypos, quint32 *nPoints)
3133 bool hsubpixel =
true;
3135 int load_flags = loadFlags(
nullptr, Format_A8, flags, hsubpixel, vfactor);
3136 int result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints);
3141bool QFontEngineFT::initFromFontEngine(
const QFontEngineFT *fe)
3143 if (!init(fe->faceId(), fe->antialias, fe->defaultFormat, fe->freetype))
3148 freetype->ref.ref();
3150 default_load_flags = fe->default_load_flags;
3151 default_hint_style = fe->default_hint_style;
3152 antialias = fe->antialias;
3153 transform = fe->transform;
3154 embolden = fe->embolden;
3155 obliquen = fe->obliquen;
3156 subpixelType = fe->subpixelType;
3157 lcdFilterType = fe->lcdFilterType;
3158 embeddedbitmap = fe->embeddedbitmap;
3159 verticalHintingOnly = fe->verticalHintingOnly;
3164QFontEngine *QFontEngineFT::cloneWithSize(qreal pixelSize)
const
3166 QFontDef fontDef(
this->fontDef);
3167 fontDef.pixelSize = pixelSize;
3168 QFontEngineFT *fe =
new QFontEngineFT(fontDef);
3169 if (!fe->initFromFontEngine(
this)) {
3177Qt::HANDLE QFontEngineFT::handle()
const
3179 return non_locked_face();
3182QList<QFontVariableAxis> QFontEngineFT::variableAxes()
const
3184 return freetype->variableAxes();
QHash< QFontEngine::FaceId, QFreetypeFace * > faces
QList< QFreetypeFace * > staleFaces
QHash< FaceStyle, int > faceIndices
Combined button and popup list for selecting options.
static bool hasTrueTypeInterpreterV40(FT_Library library)
static const QFontEngine::HintStyle ftInitialDefaultHintStyle
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)
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)
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 const FT_Fixed obliqueSlant
static void dont_delete(void *)
static bool areMetricsTooLarge(const QFontEngineFT::GlyphInfo &info)
FaceStyle(QString faceFileName, QString styleName)