summaryrefslogtreecommitdiffstats
path: root/src/gui/text/qfontengine_ft.cpp
diff options
context:
space:
mode:
authorJian Liang <jianliang79@gmail.com>2016-04-01 22:03:15 +0800
committerjian liang <jianliang79@gmail.com>2016-05-03 22:58:49 +0000
commit347832d7593e2369f8597bbd06213b80b3087433 (patch)
tree1fa749d3ee7d4bc2f5349457c990669b796ed84f /src/gui/text/qfontengine_ft.cpp
parent324e2776874e6d09fe53078f4f0da3cab3831322 (diff)
Support color font rendering for freetype engine
This patch mainly do two things: 1) Support color bitmap font for freetype fontengine. This partially based on Corentin Jabot's patch 2) Support ARGB opengl glyph cache when workaround_brokenFBOReadBack is true (It is always true under Android). Some code refactor has been done in QOpenGLTextureGlyphCache. This patch also bump the minimal required freetype version to 2.2 [ChangeLog][General][Freetype] Support color font rendering Task-number: QTBUG-35156 Change-Id: I35aae5f98ba9a27b70a48db3f2647fc070c39c33 Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
Diffstat (limited to 'src/gui/text/qfontengine_ft.cpp')
-rw-r--r--src/gui/text/qfontengine_ft.cpp234
1 files changed, 186 insertions, 48 deletions
diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp
index ec995d5cc6..e89522f5af 100644
--- a/src/gui/text/qfontengine_ft.cpp
+++ b/src/gui/text/qfontengine_ft.cpp
@@ -52,6 +52,7 @@
#include <qscopedvaluerollback.h>
#include "qthreadstorage.h"
#include <qmath.h>
+#include <qendian.h>
#include <ft2build.h>
#include FT_FREETYPE_H
@@ -192,6 +193,15 @@ int QFreetypeFace::getPointInOutline(glyph_t glyph, int flags, quint32 point, QF
return Err_Ok;
}
+bool QFreetypeFace::isScalableBitmap() const
+{
+#ifdef FT_HAS_COLOR
+ return !FT_IS_SCALABLE(face) && FT_HAS_COLOR(face);
+#else
+ return false;
+#endif
+}
+
extern QByteArray qt_fontdata_from_index(int);
/*
@@ -249,6 +259,7 @@ QFreetypeFace *QFreetypeFace::getFace(const QFontEngine::FaceId &face_id,
newFreetype->ref.store(1);
newFreetype->xsize = 0;
newFreetype->ysize = 0;
+ newFreetype->scalableBitmapScaleFactor = 1;
newFreetype->matrix.xx = 0x10000;
newFreetype->matrix.yy = 0x10000;
newFreetype->matrix.xy = 0;
@@ -330,36 +341,46 @@ void QFreetypeFace::computeSize(const QFontDef &fontDef, int *xsize, int *ysize,
*xsize = *ysize * fontDef.stretch / 100;
*outline_drawing = false;
- /*
- * Bitmap only faces must match exactly, so find the closest
- * one (height dominant search)
- */
if (!(face->face_flags & FT_FACE_FLAG_SCALABLE)) {
int best = 0;
- for (int i = 1; i < face->num_fixed_sizes; i++) {
- if (qAbs(*ysize - face->available_sizes[i].y_ppem) <
- qAbs(*ysize - face->available_sizes[best].y_ppem) ||
- (qAbs(*ysize - face->available_sizes[i].y_ppem) ==
- qAbs(*ysize - face->available_sizes[best].y_ppem) &&
- qAbs(*xsize - face->available_sizes[i].x_ppem) <
- qAbs(*xsize - face->available_sizes[best].x_ppem))) {
- best = i;
+ if (!isScalableBitmap()) {
+ /*
+ * Bitmap only faces must match exactly, so find the closest
+ * one (height dominant search)
+ */
+ for (int i = 1; i < face->num_fixed_sizes; i++) {
+ if (qAbs(*ysize - face->available_sizes[i].y_ppem) <
+ qAbs(*ysize - face->available_sizes[best].y_ppem) ||
+ (qAbs(*ysize - face->available_sizes[i].y_ppem) ==
+ qAbs(*ysize - face->available_sizes[best].y_ppem) &&
+ qAbs(*xsize - face->available_sizes[i].x_ppem) <
+ qAbs(*xsize - face->available_sizes[best].x_ppem))) {
+ best = i;
+ }
+ }
+ } else {
+ // Select the shortest bitmap strike whose height is larger than the desired height
+ for (int i = 1; i < face->num_fixed_sizes; i++) {
+ if (face->available_sizes[i].y_ppem < *ysize) {
+ if (face->available_sizes[i].y_ppem > face->available_sizes[best].y_ppem)
+ best = i;
+ } else if (face->available_sizes[best].y_ppem < *ysize) {
+ best = i;
+ } else if (face->available_sizes[i].y_ppem < face->available_sizes[best].y_ppem) {
+ best = i;
+ }
}
}
- if (FT_Set_Char_Size(face, face->available_sizes[best].x_ppem, face->available_sizes[best].y_ppem, 0, 0) == 0) {
+
+ // According to freetype documentation we must use FT_Select_Size
+ // to make sure we can select the desired bitmap strike index
+ if (FT_Select_Size(face, best) == 0) {
+ if (isScalableBitmap())
+ scalableBitmapScaleFactor = QFixed::fromReal((qreal)fontDef.pixelSize / face->available_sizes[best].height);
*xsize = face->available_sizes[best].x_ppem;
*ysize = face->available_sizes[best].y_ppem;
} else {
- int err = 1;
- if (!(face->face_flags & FT_FACE_FLAG_SCALABLE) && ysize == 0 && face->num_fixed_sizes >= 1) {
- // work around FT 2.1.10 problem with BDF without PIXEL_SIZE property
- err = FT_Set_Pixel_Sizes(face, face->available_sizes[0].width, face->available_sizes[0].height);
- if (err && face->num_fixed_sizes == 1)
- err = 0; //even more of a workaround...
- }
-
- if (err)
- *xsize = *ysize = 0;
+ *xsize = *ysize = 0;
}
} else {
*outline_drawing = (*xsize > (QT_MAX_CACHED_GLYPH_SIZE<<6) || *ysize > (QT_MAX_CACHED_GLYPH_SIZE<<6));
@@ -736,6 +757,11 @@ bool QFontEngineFT::init(FaceId faceId, bool antialias, GlyphFormat format,
if (line_thickness < 2 && score >= 1050)
line_thickness = 2;
underline_position = ((line_thickness * 2) + 3) / 6;
+
+ if (isScalableBitmap()) {
+ glyphFormat = defaultFormat = GlyphFormat::Format_ARGB;
+ cacheEnabled = false;
+ }
}
if (line_thickness < 1)
line_thickness = 1;
@@ -829,6 +855,10 @@ int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags,
load_target = FT_LOAD_TARGET_LCD_V;
vfactor = 3;
}
+ } else if (format == Format_ARGB) {
+#ifdef FT_LOAD_COLOR
+ load_flags |= FT_LOAD_COLOR;
+#endif
}
if (set && set->outline_drawing)
@@ -890,7 +920,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
|| matrix.xy != 0
|| matrix.yx != 0;
- if (transform || (format != Format_Mono && !embeddedbitmap))
+ if (transform || (format != Format_Mono && !isScalableBitmap()))
load_flags |= FT_LOAD_NO_BITMAP;
FT_Error err = FT_Load_Glyph(face, glyph, load_flags);
@@ -1121,7 +1151,11 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
if (bitmap.buffer != glyph_buffer.data())
delete [] bitmap.buffer;
} else if (slot->format == FT_GLYPH_FORMAT_BITMAP) {
+#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100) >= 20500)
+ Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO || slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA);
+#else
Q_ASSERT(slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO);
+#endif
uchar *src = slot->bitmap.buffer;
uchar *dst = glyph_buffer.data();
int h = slot->bitmap.rows;
@@ -1132,7 +1166,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
dst += pitch;
src += slot->bitmap.pitch;
}
- } else {
+ } else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
if (hsubpixel) {
while (h--) {
uint *dd = (uint *)dst;
@@ -1166,6 +1200,29 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
}
}
}
+#if ((FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100) >= 20500)
+ else if (slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)
+ {
+ while (h--) {
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ const quint32 *srcPixel = (const quint32 *)src;
+ quint32 *dstPixel = (quint32 *)dst;
+ for (int x = 0; x < static_cast<int>(slot->bitmap.width); x++, srcPixel++, dstPixel++) {
+ const quint32 pixel = *srcPixel;
+ *dstPixel = qbswap(pixel);
+ }
+#else
+ memcpy(dst, src, slot->bitmap.width * 4);
+#endif
+ dst += slot->bitmap.pitch;
+ src += slot->bitmap.pitch;
+ }
+ info.width = info.linearAdvance = info.xOff = slot->bitmap.width;
+ info.height = slot->bitmap.rows;
+ info.x = slot->bitmap_left;
+ info.y = slot->bitmap_top;
+ }
+#endif
} else {
qWarning("QFontEngine: Glyph neither outline nor bitmap format=%d", slot->format);
return 0;
@@ -1236,46 +1293,68 @@ int QFontEngineFT::synthesized() const
QFixed QFontEngineFT::ascent() const
{
- return QFixed::fromFixed(metrics.ascender);
+ QFixed v = QFixed::fromFixed(metrics.ascender);
+ if (freetype->scalableBitmapScaleFactor != 1)
+ v *= freetype->scalableBitmapScaleFactor;
+ return v;
}
QFixed QFontEngineFT::descent() const
{
- return QFixed::fromFixed(-metrics.descender);
+ QFixed v = QFixed::fromFixed(-metrics.descender);
+ if (freetype->scalableBitmapScaleFactor != 1)
+ v *= freetype->scalableBitmapScaleFactor;
+ return v;
}
QFixed QFontEngineFT::leading() const
{
- return QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender);
+ QFixed v = QFixed::fromFixed(metrics.height - metrics.ascender + metrics.descender);
+ if (freetype->scalableBitmapScaleFactor != 1)
+ v *= freetype->scalableBitmapScaleFactor;
+ return v;
}
QFixed QFontEngineFT::xHeight() const
{
- TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
- if (os2 && os2->sxHeight) {
- lockFace();
- QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize();
- unlockFace();
- return answer;
+ if (!isScalableBitmap()) {
+ TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
+ if (os2 && os2->sxHeight) {
+ lockFace();
+ QFixed answer = QFixed(os2->sxHeight * freetype->face->size->metrics.y_ppem) / emSquareSize();
+ unlockFace();
+ return answer;
+ }
+ } else {
+ return QFixed(freetype->face->size->metrics.y_ppem) * freetype->scalableBitmapScaleFactor;
}
return QFontEngine::xHeight();
}
QFixed QFontEngineFT::averageCharWidth() const
{
- TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
- if (os2 && os2->xAvgCharWidth) {
- lockFace();
- QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize();
- unlockFace();
- return answer;
- }
- return QFontEngine::averageCharWidth();
+ if (!isScalableBitmap()) {
+ TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(freetype->face, ft_sfnt_os2);
+ if (os2 && os2->xAvgCharWidth) {
+ lockFace();
+ QFixed answer = QFixed(os2->xAvgCharWidth * freetype->face->size->metrics.x_ppem) / emSquareSize();
+ unlockFace();
+ return answer;
+ }
+ } else {
+ const qreal aspectRatio = (qreal)xsize / ysize;
+ return QFixed::fromReal(fontDef.pixelSize * aspectRatio);
+ }
+
+ return QFontEngine::averageCharWidth();
}
qreal QFontEngineFT::maxCharWidth() const
{
- return metrics.max_advance >> 6;
+ QFixed max_advance = QFixed::fromFixed(metrics.max_advance);
+ if (freetype->scalableBitmapScaleFactor != 1)
+ max_advance *= freetype->scalableBitmapScaleFactor;
+ return max_advance.toReal();
}
QFixed QFontEngineFT::lineThickness() const
@@ -1558,6 +1637,23 @@ bool QFontEngineFT::shouldUseDesignMetrics(QFontEngine::ShaperFlags flags) const
return default_hint_style == HintNone || default_hint_style == HintLight || (flags & DesignMetrics);
}
+QFixed QFontEngineFT::scaledBitmapMetrics(QFixed m) const
+{
+ return m * freetype->scalableBitmapScaleFactor;
+}
+
+glyph_metrics_t QFontEngineFT::scaledBitmapMetrics(const glyph_metrics_t &m) const
+{
+ glyph_metrics_t metrics;
+ metrics.x = scaledBitmapMetrics(m.x);
+ metrics.y = scaledBitmapMetrics(m.y);
+ metrics.width = scaledBitmapMetrics(m.width);
+ metrics.height = scaledBitmapMetrics(m.height);
+ metrics.xoff = scaledBitmapMetrics(m.xoff);
+ metrics.yoff = scaledBitmapMetrics(m.yoff);
+ return metrics;
+}
+
void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlags flags) const
{
FT_Face face = 0;
@@ -1580,6 +1676,9 @@ void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QFontEngine::ShaperFlag
if (!cacheEnabled && g != &emptyGlyph)
delete g;
}
+
+ if (freetype->scalableBitmapScaleFactor != 1)
+ glyphs->advances[i] *= freetype->scalableBitmapScaleFactor;
}
if (face)
unlockFace();
@@ -1596,8 +1695,13 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
glyph_metrics_t overall;
// initialize with line height, we get the same behaviour on all platforms
- overall.y = -ascent();
- overall.height = ascent() + descent();
+ if (!isScalableBitmap()) {
+ overall.y = -ascent();
+ overall.height = ascent() + descent();
+ } else {
+ overall.y = QFixed::fromFixed(-metrics.ascender);
+ overall.height = QFixed::fromFixed(metrics.ascender - metrics.descender);
+ }
QFixed ymax = 0;
QFixed xmax = 0;
@@ -1639,6 +1743,8 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
if (face)
unlockFace();
+ if (isScalableBitmap())
+ overall = scaledBitmapMetrics(overall);
return overall;
}
@@ -1675,6 +1781,9 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph)
}
if (face)
unlockFace();
+
+ if (isScalableBitmap())
+ overall = scaledBitmapMetrics(overall);
return overall;
}
@@ -1710,6 +1819,9 @@ glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, QFixed subPixe
overall.xoff = TRUNC(ROUND(face->glyph->advance.x));
unlockFace();
}
+
+ if (isScalableBitmap())
+ overall = scaledBitmapMetrics(overall);
return overall;
}
@@ -1857,6 +1969,31 @@ QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, co
return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, t);
}
+QImage QFontEngineFT::bitmapForGlyph(glyph_t g, QFixed subPixelPosition, const QTransform &t)
+{
+ Glyph *glyph = loadGlyphFor(g, subPixelPosition, defaultFormat, t);
+ if (glyph == Q_NULLPTR)
+ return QImage();
+
+ QImage img;
+ if (defaultFormat == GlyphFormat::Format_ARGB)
+ img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_ARGB32_Premultiplied).copy();
+ else if (defaultFormat == GlyphFormat::Format_Mono)
+ img = QImage(glyph->data, glyph->width, glyph->height, QImage::Format_Mono).copy();
+
+ if (!img.isNull() && (!t.isIdentity() || freetype->scalableBitmapScaleFactor != 1)) {
+ QTransform trans(t);
+ const qreal scaleFactor = freetype->scalableBitmapScaleFactor.toReal();
+ trans.scale(scaleFactor, scaleFactor);
+ img = img.transformed(trans, Qt::SmoothTransformation);
+ }
+
+ if (!cacheEnabled && glyph != &emptyGlyph)
+ delete glyph;
+
+ return img;
+}
+
void QFontEngineFT::removeGlyphFromCache(glyph_t glyph)
{
defaultGlyphSet.removeGlyphFromCache(glyph, 0);
@@ -1878,9 +2015,10 @@ FT_Face QFontEngineFT::lockFace(Scaling scale) const
freetype->lock();
FT_Face face = freetype->face;
if (scale == Unscaled) {
- FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0);
- freetype->xsize = face->units_per_EM << 6;
- freetype->ysize = face->units_per_EM << 6;
+ if (FT_Set_Char_Size(face, face->units_per_EM << 6, face->units_per_EM << 6, 0, 0) == 0) {
+ freetype->xsize = face->units_per_EM << 6;
+ freetype->ysize = face->units_per_EM << 6;
+ }
} else if (freetype->xsize != xsize || freetype->ysize != ysize) {
FT_Set_Char_Size(face, xsize, ysize, 0, 0);
freetype->xsize = xsize;