From 29a1c57183958b3951db4e7ba9504918a3caa761 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 3 Dec 2024 14:52:59 +0100 Subject: [PATCH] Implement COLRv0 support in Freetype engine COLRv0 fonts are both scalable and in color, which was not previously handled in the Freetype backend because it only supported color fonts with embedded bitmaps. This patch simply correctly detects the scalable color fonts and render them through the same mechanism as other color fonts. [ChangeLog][Freetype] Added support for COLRv0 format color fonts. Task-number: QTBUG-131116 Change-Id: I290e7525c24b4cf2ec872a366e5193f54712a3a4 Reviewed-by: Allan Sandfeld Jensen (cherry picked from commit 39df9e1858a4115bc19b6a4dee5d687c5ee00d21) Reviewed-by: Qt Cherry-pick Bot (cherry picked from commit 37c389c779188e05bb057d2f7164cba2827d33b6) --- src/gui/text/freetype/qfontengine_ft.cpp | 79 +++++++++++++++++------- src/gui/text/freetype/qfontengine_ft_p.h | 12 +++- 2 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/gui/text/freetype/qfontengine_ft.cpp b/src/gui/text/freetype/qfontengine_ft.cpp index 35f5a544402..124328c73dc 100644 --- a/src/gui/text/freetype/qfontengine_ft.cpp +++ b/src/gui/text/freetype/qfontengine_ft.cpp @@ -495,7 +495,12 @@ void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize, *xsize = *ysize = 0; } } else { - *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6)); +#if defined FT_HAS_COLOR + if (FT_HAS_COLOR(face)) + *outline_drawing = false; + else +#endif + *outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6)); } } @@ -506,7 +511,11 @@ QFontEngine::Properties QFreetypeFace::properties() const PS_FontInfoRec font_info; if (FT_Get_PS_Font_Info(face, &font_info) == 0) p.copyright = font_info.notice; - if (FT_IS_SCALABLE(face)) { + if (FT_IS_SCALABLE(face) +#if defined(FT_HAS_COLOR) + && !FT_HAS_COLOR(face) +#endif + ) { p.ascent = face->ascender; p.descent = -face->descender; p.leading = face->height - face->ascender + face->descender; @@ -889,7 +898,11 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, FT_Face face = lockFace(); - if (FT_IS_SCALABLE(face)) { + if (FT_IS_SCALABLE(face) +#if defined(FT_HAS_COLOR) + && !FT_HAS_COLOR(face) +#endif + ) { bool isItalic = calculateActualItalic(freetype, face, faceId); bool fake_oblique = (fontDef.style != QFont::StyleNormal) && !isItalic && !qEnvironmentVariableIsSet("QT_NO_SYNTHESIZED_ITALIC"); if (fake_oblique) @@ -918,8 +931,10 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format, underline_position = ((line_thickness * 2) + 3) / 6; cacheEnabled = false; - if (isScalableBitmap()) +#if defined(FT_HAS_COLOR) + if (FT_HAS_COLOR(face)) glyphFormat = defaultFormat = GlyphFormat::Format_ARGB; +#endif } if (line_thickness < 1) line_thickness = 1; @@ -1094,6 +1109,7 @@ static inline void transformBoundingBox(int *left, int *top, int *right, int *bo QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, const QFixedPoint &subPixelPosition, + QColor color, GlyphFormat format, bool fetchMetricsOnly, bool disableOutlineDrawing) const @@ -1326,15 +1342,32 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, } info.linearAdvance = info.xOff = slot->bitmap.width; } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { - Q_ASSERT(format == Format_A8); - uchar *src = slot->bitmap.buffer; - uchar *dst = glyph_buffer.get(); - int h = slot->bitmap.rows; - int bytes = info.width; - while (h--) { - memcpy (dst, src, bytes); - dst += pitch; - src += slot->bitmap.pitch; + if (format == Format_A8) { + uchar *src = slot->bitmap.buffer; + uchar *dst = glyph_buffer.get(); + int h = slot->bitmap.rows; + int bytes = info.width; + while (h--) { + memcpy (dst, src, bytes); + dst += pitch; + src += slot->bitmap.pitch; + } + } else if (format == Format_ARGB) { + uchar *src = slot->bitmap.buffer; + quint32 *dstPixel = reinterpret_cast(glyph_buffer.get()); + int h = slot->bitmap.rows; + while (h--) { + for (int x = 0; x < static_cast(slot->bitmap.width); ++x) { + uchar alpha = src[x]; + float alphaF = alpha / 255.0; + dstPixel[x] = qRgba(qRound(alphaF * color.red()), + qRound(alphaF * color.green()), + qRound(alphaF * color.blue()), + alpha); + } + src += slot->bitmap.pitch; + dstPixel += info.width; + } } } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) { Q_ASSERT(format == Format_A32); @@ -1819,6 +1852,7 @@ void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlag g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr, glyphs->glyphs[i], QFixedPoint(), + QColor(), Format_None, true); if (g) @@ -1864,6 +1898,7 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr, glyphs.glyphs[i], QFixedPoint(), + QColor(), Format_None, true); } @@ -1912,6 +1947,7 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph) g = loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr, glyph, QFixedPoint(), + QColor(), Format_None, true); } @@ -1961,7 +1997,7 @@ glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, && matrix.type() > QTransform::TxTranslate; if (needsImageTransform && format == QFontEngine::Format_Mono) format = QFontEngine::Format_A8; - Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, true, true); + Glyph *g = loadGlyphFor(glyph, subPixelPosition, format, matrix, QColor(), true, true); glyph_metrics_t overall; if (g) { @@ -2036,7 +2072,7 @@ QFontEngine::Glyph *QFontEngineFT::glyphData(glyph_t glyphIndex, else if (neededFormat == Format_None) neededFormat = Format_A8; - Glyph *glyph = loadGlyphFor(glyphIndex, subPixelPosition, neededFormat, t); + Glyph *glyph = loadGlyphFor(glyphIndex, subPixelPosition, neededFormat, t, QColor()); if (!glyph || !glyph->width || !glyph->height) return nullptr; @@ -2053,6 +2089,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g, const QFixedPoint &subPixelPosition, GlyphFormat format, const QTransform &t, + QColor color, bool fetchBoundingBox, bool disableOutlineDrawing) { @@ -2071,7 +2108,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyphFor(glyph_t g, FT_Matrix ftMatrix = glyphSet != nullptr ? glyphSet->transformationMatrix : QTransformToFTMatrix(t); FT_Matrix_Multiply(&ftMatrix, &m); freetype->matrix = m; - glyph = loadGlyph(glyphSet, g, subPixelPosition, format, false, disableOutlineDrawing); + glyph = loadGlyph(glyphSet, g, subPixelPosition, color, format, false, disableOutlineDrawing); unlockFace(); } @@ -2091,7 +2128,7 @@ QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, && t.type() > QTransform::TxTranslate; const GlyphFormat neededFormat = antialias || needsImageTransform ? Format_A8 : Format_Mono; - Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true); + Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, QColor(), false, true); QImage img = alphaMapFromGlyphData(glyph, neededFormat); if (needsImageTransform) @@ -2118,7 +2155,7 @@ QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, const GlyphFormat neededFormat = Format_A32; - Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, false, true); + Glyph *glyph = loadGlyphFor(g, subPixelPosition, neededFormat, t, QColor(), false, true); QImage img = alphaMapFromGlyphData(glyph, neededFormat); if (needsImageTransform) @@ -2140,9 +2177,7 @@ QImage QFontEngineFT::bitmapForGlyph(glyph_t g, const QTransform &t, const QColor &color) { - Q_UNUSED(color); - - Glyph *glyph = loadGlyphFor(g, subPixelPosition, defaultFormat, t); + Glyph *glyph = loadGlyphFor(g, subPixelPosition, defaultFormat, t, color); if (glyph == nullptr) return QImage(); @@ -2152,7 +2187,7 @@ QImage QFontEngineFT::bitmapForGlyph(glyph_t g, else if (defaultFormat == GlyphFormat::Format_Mono) img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_Mono).copy(); - if (!img.isNull() && (!t.isIdentity() || scalableBitmapScaleFactor != 1)) { + if (!img.isNull() && (scalableBitmapScaleFactor != 1 || (!t.isIdentity() && !isSmoothlyScalable))) { QTransform trans(t); const qreal scaleFactor = scalableBitmapScaleFactor.toReal(); trans.scale(scaleFactor, scaleFactor); diff --git a/src/gui/text/freetype/qfontengine_ft_p.h b/src/gui/text/freetype/qfontengine_ft_p.h index b73b7261be6..9021c8d3926 100644 --- a/src/gui/text/freetype/qfontengine_ft_p.h +++ b/src/gui/text/freetype/qfontengine_ft_p.h @@ -233,10 +233,19 @@ private: GlyphFormat format = Format_None, bool fetchMetricsOnly = false, bool disableOutlineDrawing = false) const - { return loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr, glyph, subPixelPosition, format, fetchMetricsOnly, disableOutlineDrawing); } + { + return loadGlyph(cacheEnabled ? &defaultGlyphSet : nullptr, + glyph, + subPixelPosition, + QColor(), + format, + fetchMetricsOnly, + disableOutlineDrawing); + } Glyph *loadGlyph(QGlyphSet *set, uint glyph, const QFixedPoint &subPixelPosition, + QColor color, GlyphFormat = Format_None, bool fetchMetricsOnly = false, bool disableOutlineDrawing = false) const; @@ -244,6 +253,7 @@ private: const QFixedPoint &subPixelPosition, GlyphFormat format, const QTransform &t, + QColor color, bool fetchBoundingBox = false, bool disableOutlineDrawing = false);