summaryrefslogtreecommitdiffstats
path: root/src/gui/text/qfontengine_ft.cpp
diff options
context:
space:
mode:
authorJiang Jiang <jiang.jiang@nokia.com>2011-02-22 13:15:43 (GMT)
committerJiang Jiang <jiang.jiang@nokia.com>2011-03-22 11:03:51 (GMT)
commit037e632d4b3884d06bf9e92de77d726c75fe7898 (patch)
tree6834662159359112e2b2b8acf43f574990dda0e3 /src/gui/text/qfontengine_ft.cpp
parent23098630c94e6655a33042bee0caa6c933c0c7d9 (diff)
downloadQt-037e632d4b3884d06bf9e92de77d726c75fe7898.zip
Qt-037e632d4b3884d06bf9e92de77d726c75fe7898.tar.gz
Qt-037e632d4b3884d06bf9e92de77d726c75fe7898.tar.bz2
Implement subpixel positioning with FreeType
QFontEngineFT are used in raster/OpenGL paint engine and QGLWidget, also in Symbian, etc. We want to make sure it works well in raster and QGLWidget first. Regardless subpixel antialiasing (LCD filtering) is enabled or not (though it does look better when subpixel antialiasing is on). We also need to support transformations. The tricky part here is that, under X11, we have a different code path for QFontEngineFT and other font engines in raster engine, which uses QFontEngineFT's own glyph cache system. While in other platforms (Windows and Mac) and QGLWidget, we will use the generic QTextureGlyphCache. The generic QTextureGlyphCache already has support for subpixel positions, this solution is ported to QFontEngineFT for its QGlyphSet: the key for QGlyphSet hash table has been extended from glyph_t to <glyph_t, QFixed subPixelPosition> pair. The real work to enable subpixel positioning with FreeType is in fact really simple, we just set the horizontal translation to the subpixel position * 64 (FreeType uses a coordinate system of 1/64 of a pixel resolution internally) immediately before loading the glyph. A slight tweek to bitmap width is applied when we are getting the bitmap ourselves instead of using FT_Render_Glyph to accommodate the subpixel translation, which will only be used in non-LCD filtering cases (grayscale antialiasing). From what we have observed, FreeType can generate different bitmaps for at least 12 different subpixel positions (each with a slight difference). To limit the memory consumption, we restrict the number of subpixel positions to be 4 (hardcoded), which should be good enough for most low resolution displays. Subpixel positioning (and fractional glyph advances) will only be enabled when the hintingPreference for the font being used is PreferNoHinting or PreferVerticalHinting. We will use fontconfig hintstyle setting by default when no hintingPreference is set for the font. Task-number: QTBUG-12279 Reviewed-by: Eskil
Diffstat (limited to 'src/gui/text/qfontengine_ft.cpp')
-rw-r--r--src/gui/text/qfontengine_ft.cpp175
1 files changed, 92 insertions, 83 deletions
diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp
index f5e9e1c..a4211a9 100644
--- a/src/gui/text/qfontengine_ft.cpp
+++ b/src/gui/text/qfontengine_ft.cpp
@@ -174,9 +174,7 @@ int QFreetypeFace::fsType() const
HB_Error QFreetypeFace::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
{
- int load_flags = (flags & HB_ShaperFlag_UseDesignMetrics) ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT;
-
- if (HB_Error error = (HB_Error)FT_Load_Glyph(face, glyph, load_flags))
+ if (HB_Error error = (HB_Error)FT_Load_Glyph(face, glyph, flags))
return error;
if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
@@ -760,12 +758,9 @@ void QFontEngineFT::setDefaultHintStyle(HintStyle style)
default_hint_style = style;
}
-QFontEngineFT::Glyph *QFontEngineFT::loadGlyphMetrics(QGlyphSet *set, uint glyph, GlyphFormat format) const
+int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags,
+ bool &hsubpixel, int &vfactor) const
{
- Glyph *g = set->getGlyph(glyph);
- if (g && g->format == format)
- return g;
-
int load_flags = FT_LOAD_DEFAULT | default_load_flags;
int load_target = default_hint_style == HintLight
? FT_LOAD_TARGET_LIGHT
@@ -777,20 +772,35 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyphMetrics(QGlyphSet *set, uint glyph
if (subpixelType == QFontEngineFT::Subpixel_RGB || subpixelType == QFontEngineFT::Subpixel_BGR) {
if (default_hint_style == HintFull)
load_target = FT_LOAD_TARGET_LCD;
+ hsubpixel = true;
} else if (subpixelType == QFontEngineFT::Subpixel_VRGB || subpixelType == QFontEngineFT::Subpixel_VBGR) {
if (default_hint_style == HintFull)
load_target = FT_LOAD_TARGET_LCD_V;
+ vfactor = 3;
}
}
- if (set->outline_drawing)
+ if (set && set->outline_drawing)
load_flags = FT_LOAD_NO_BITMAP;
- if (default_hint_style == HintNone)
+ if (default_hint_style == HintNone || (flags & HB_ShaperFlag_UseDesignMetrics))
load_flags |= FT_LOAD_NO_HINTING;
else
load_flags |= load_target;
+ return load_flags;
+}
+
+QFontEngineFT::Glyph *QFontEngineFT::loadGlyphMetrics(QGlyphSet *set, uint glyph, GlyphFormat format) const
+{
+ Glyph *g = set->getGlyph(glyph);
+ if (g && g->format == format)
+ return g;
+
+ bool hsubpixel = false;
+ int vfactor = 1;
+ int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor);
+
// apply our matrix to this, but note that the metrics will not be affected by this.
FT_Face face = lockFace();
FT_Matrix matrix = this->matrix;
@@ -881,7 +891,10 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyphMetrics(QGlyphSet *set, uint glyph
return g;
}
-QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, GlyphFormat format, bool fetchMetricsOnly) const
+QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph,
+ QFixed subPixelPosition,
+ GlyphFormat format,
+ bool fetchMetricsOnly) const
{
// Q_ASSERT(freetype->lock == 1);
@@ -896,10 +909,10 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, Glyph
}
}
- Glyph *g = set->getGlyph(glyph);
+ Glyph *g = set->getGlyph(glyph, subPixelPosition);
if (g && g->format == format) {
if (uploadToServer && !g->uploadedToServer) {
- set->setGlyph(glyph, 0);
+ set->setGlyph(glyph, subPixelPosition, 0);
delete g;
g = 0;
} else {
@@ -912,33 +925,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, Glyph
Q_ASSERT(format != Format_None);
bool hsubpixel = false;
int vfactor = 1;
- int load_flags = FT_LOAD_DEFAULT | default_load_flags;
-
- int load_target = default_hint_style == HintLight
- ? FT_LOAD_TARGET_LIGHT
- : FT_LOAD_TARGET_NORMAL;
-
- if (set->outline_drawing)
- load_flags |= FT_LOAD_NO_BITMAP;
-
- if (format == Format_Mono) {
- load_target = FT_LOAD_TARGET_MONO;
- } else if (format == Format_A32) {
- if (subpixelType == QFontEngineFT::Subpixel_RGB || subpixelType == QFontEngineFT::Subpixel_BGR) {
- if (default_hint_style == HintFull)
- load_target = FT_LOAD_TARGET_LCD;
- hsubpixel = true;
- } else if (subpixelType == QFontEngineFT::Subpixel_VRGB || subpixelType == QFontEngineFT::Subpixel_VBGR) {
- if (default_hint_style == HintFull)
- load_target = FT_LOAD_TARGET_LCD_V;
- vfactor = 3;
- }
- }
-
- if (default_hint_style == HintNone)
- load_flags |= FT_LOAD_NO_HINTING;
- else
- load_flags |= load_target;
+ int load_flags = loadFlags(set, format, 0, hsubpixel, vfactor);
#ifndef Q_WS_QWS
if (format != Format_Mono && !embeddedbitmap)
@@ -955,6 +942,12 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, Glyph
load_flags |= FT_LOAD_NO_BITMAP;
FT_Face face = freetype->face;
+
+ FT_Vector v;
+ v.x = FT_Pos((subPixelPosition).toReal() * 64);
+ v.y = 0;
+ FT_Set_Transform(face, &freetype->matrix, &v);
+
FT_Error err = FT_Load_Glyph(face, glyph, load_flags);
if (err && (load_flags & FT_LOAD_NO_BITMAP)) {
load_flags &= ~FT_LOAD_NO_BITMAP;
@@ -1055,6 +1048,10 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, Glyph
top = CEIL(top);
int hpixels = TRUNC(right - left);
+ // subpixel position requires one more pixel
+ if (subPixelPosition > 0)
+ hpixels++;
+
if (hsubpixel)
hpixels = hpixels*3 + 8;
info.width = hpixels;
@@ -1197,7 +1194,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, Glyph
uploadGlyphToServer(set, glyph, g, &info, glyph_buffer_size);
}
- set->setGlyph(glyph, g);
+ set->setGlyph(glyph, subPixelPosition, g);
return g;
}
@@ -1430,12 +1427,28 @@ QFontEngineFT::QGlyphSet *QFontEngineFT::loadTransformedGlyphSet(const QTransfor
return gs;
}
-bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, GlyphFormat format)
+QFixed QFontEngineFT::subPixelPositionForX(QFixed x)
+{
+ int m_subPixelPositionCount = 4;
+
+ QFixed subPixelPosition;
+ if (x != 0) {
+ subPixelPosition = x - x.floor();
+ QFixed fraction = (subPixelPosition / QFixed::fromReal(1.0 / m_subPixelPositionCount)).floor();
+ subPixelPosition = fraction / QFixed(m_subPixelPositionCount);
+ }
+ return subPixelPosition;
+}
+
+bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs,
+ QVarLengthArray<QFixedPoint> &positions,
+ GlyphFormat format)
{
FT_Face face = 0;
for (int i = 0; i < num_glyphs; ++i) {
- Glyph *glyph = gs->getGlyph(glyphs[i]);
+ QFixed spp = subPixelPositionForX(positions[i].x);
+ Glyph *glyph = gs->getGlyph(glyphs[i], spp);
if (glyph == 0 || glyph->format != format) {
if (!face) {
face = lockFace();
@@ -1444,7 +1457,7 @@ bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, G
FT_Set_Transform(face, &m, 0);
freetype->matrix = m;
}
- if (!loadGlyph(gs, glyphs[i], format)) {
+ if (!loadGlyph(gs, glyphs[i], spp, format)) {
unlockFace();
return false;
}
@@ -1668,35 +1681,26 @@ bool QFontEngineFT::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs
void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const
{
FT_Face face = 0;
- if (flags & QTextEngine::DesignMetrics) {
- for (int i = 0; i < glyphs->numGlyphs; i++) {
- Glyph *g = defaultGlyphSet.getGlyph(glyphs->glyphs[i]);
- if (g) {
- glyphs->advances_x[i] = QFixed::fromFixed(g->linearAdvance);
- } else {
- if (!face)
- face = lockFace();
- g = loadGlyph(glyphs->glyphs[i], Format_None, true);
- glyphs->advances_x[i] = QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10);
- }
- glyphs->advances_y[i] = 0;
- }
- } else {
- for (int i = 0; i < glyphs->numGlyphs; i++) {
- Glyph *g = defaultGlyphSet.getGlyph(glyphs->glyphs[i]);
- if (g) {
- glyphs->advances_x[i] = QFixed(g->advance);
- } else {
- if (!face)
- face = lockFace();
- g = loadGlyph(glyphs->glyphs[i], Format_None, true);
- glyphs->advances_x[i] = QFixed::fromFixed(face->glyph->metrics.horiAdvance).round();
- }
- glyphs->advances_y[i] = 0;
+ bool design = (default_hint_style == HintNone ||
+ default_hint_style == HintLight ||
+ (flags & HB_ShaperFlag_UseDesignMetrics));
+ for (int i = 0; i < glyphs->numGlyphs; i++) {
+ Glyph *g = defaultGlyphSet.getGlyph(glyphs->glyphs[i]);
+ if (g) {
+ glyphs->advances_x[i] = design ? QFixed::fromFixed(g->linearAdvance) : QFixed(g->advance);
+ } else {
+ if (!face)
+ face = lockFace();
+ g = loadGlyph(glyphs->glyphs[i], 0, Format_None, true);
+ glyphs->advances_x[i] = design ? QFixed::fromFixed(face->glyph->linearHoriAdvance >> 10)
+ : QFixed::fromFixed(face->glyph->metrics.horiAdvance).round();
+ if (face)
+ unlockFace();
}
+ if (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ glyphs->advances_x[i] = glyphs->advances_x[i].round();
+ glyphs->advances_y[i] = 0;
}
- if (face)
- unlockFace();
}
glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
@@ -1716,7 +1720,7 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs)
if (!g) {
if (!face)
face = lockFace();
- g = loadGlyph(glyphs.glyphs[i], Format_None, true);
+ g = loadGlyph(glyphs.glyphs[i], 0, Format_None, true);
}
if (g) {
QFixed x = overall.xoff + glyphs.offsets[i].x + g->x;
@@ -1757,7 +1761,7 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph)
Glyph *g = defaultGlyphSet.getGlyph(glyph);
if (!g) {
face = lockFace();
- g = loadGlyph(glyph, Format_None, true);
+ g = loadGlyph(glyph, 0, Format_None, true);
}
if (g) {
overall.x = g->x;
@@ -1765,6 +1769,8 @@ glyph_metrics_t QFontEngineFT::boundingBox(glyph_t glyph)
overall.width = g->width;
overall.height = g->height;
overall.xoff = g->advance;
+ if (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
+ overall.xoff = overall.xoff.round();
} else {
int left = FLOOR(face->glyph->metrics.horiBearingX);
int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width);
@@ -1866,7 +1872,7 @@ QImage QFontEngineFT::alphaMapForGlyph(glyph_t g)
GlyphFormat glyph_format = antialias ? Format_A8 : Format_Mono;
- Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, glyph_format);
+ Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, 0, glyph_format);
if (!glyph) {
unlockFace();
return QFontEngine::alphaMapForGlyph(g);
@@ -1905,7 +1911,7 @@ QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, in
GlyphFormat glyph_format = Format_A32;
- Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, glyph_format);
+ Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, subPixelPosition, glyph_format);
if (!glyph) {
unlockFace();
return QFontEngine::alphaRGBMapForGlyph(g, subPixelPosition, margin, t);
@@ -1920,7 +1926,7 @@ QImage QFontEngineFT::alphaRGBMapForGlyph(glyph_t g, QFixed subPixelPosition, in
void QFontEngineFT::removeGlyphFromCache(glyph_t glyph)
{
- defaultGlyphSet.removeGlyphFromCache(glyph);
+ defaultGlyphSet.removeGlyphFromCache(glyph, 0);
}
int QFontEngineFT::glyphCount() const
@@ -2000,9 +2006,9 @@ void QFontEngineFT::QGlyphSet::clear()
glyph_data.clear();
}
-void QFontEngineFT::QGlyphSet::removeGlyphFromCache(int index)
+void QFontEngineFT::QGlyphSet::removeGlyphFromCache(glyph_t index, QFixed subPixelPosition)
{
- if (index < 256) {
+ if (useFastGlyphData(index, subPixelPosition)) {
if (fast_glyph_data[index]) {
delete fast_glyph_data[index];
fast_glyph_data[index] = 0;
@@ -2010,18 +2016,18 @@ void QFontEngineFT::QGlyphSet::removeGlyphFromCache(int index)
--fast_glyph_count;
}
} else {
- delete glyph_data.take(index);
+ delete glyph_data.take(GlyphAndSubPixelPosition(index, subPixelPosition));
}
}
-void QFontEngineFT::QGlyphSet::setGlyph(int index, Glyph *glyph)
+void QFontEngineFT::QGlyphSet::setGlyph(glyph_t index, QFixed subPixelPosition, Glyph *glyph)
{
- if (index < 256) {
+ if (useFastGlyphData(index, subPixelPosition)) {
if (!fast_glyph_data[index])
++fast_glyph_count;
fast_glyph_data[index] = glyph;
} else {
- glyph_data.insert(index, glyph);
+ glyph_data.insert(GlyphAndSubPixelPosition(index, subPixelPosition), glyph);
}
}
@@ -2038,7 +2044,10 @@ void QFontEngineFT::freeServerGlyphSet(unsigned long id)
HB_Error QFontEngineFT::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
{
lockFace();
- HB_Error result = freetype->getPointInOutline(glyph, flags, point, xpos, ypos, nPoints);
+ bool hsubpixel = true;
+ int vfactor = 1;
+ int load_flags = loadFlags(0, Format_A8, flags, hsubpixel, vfactor);
+ HB_Error result = freetype->getPointInOutline(glyph, load_flags, point, xpos, ypos, nPoints);
unlockFace();
return result;
}