From 037e632d4b3884d06bf9e92de77d726c75fe7898 Mon Sep 17 00:00:00 2001 From: Jiang Jiang Date: Tue, 22 Feb 2011 14:15:43 +0100 Subject: 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 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 --- src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp | 8 +- src/gui/painting/qpaintengine_raster.cpp | 7 +- src/gui/painting/qpaintengine_x11.cpp | 2 +- src/gui/painting/qtextureglyphcache.cpp | 10 +- src/gui/text/qfontdatabase_x11.cpp | 1 + src/gui/text/qfontengine_ft.cpp | 175 ++++++++++++++------------ src/gui/text/qfontengine_ft_p.h | 52 ++++++-- src/gui/text/qfontengine_x11.cpp | 16 ++- 8 files changed, 166 insertions(+), 105 deletions(-) diff --git a/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp b/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp index 6c4d9f1..c202e1f 100644 --- a/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp +++ b/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp @@ -1052,16 +1052,16 @@ HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *fe { HB_Script script = shaper_item->item.script; - if (!shaper_item->face->supported_scripts[script]) - return false; - HB_Face face = shaper_item->face; if (face->current_script == script && face->current_flags == shaper_item->shaperFlags) - return true; + return shaper_item->face->supported_scripts[script] ? true : false; face->current_script = script; face->current_flags = shaper_item->shaperFlags; + if (!shaper_item->face->supported_scripts[script]) + return false; + assert(script < HB_ScriptCount); // find script in our list of supported scripts. unsigned int tag = ot_scripts[script].tag; diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 9242fb6..3af98d6 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -3469,7 +3469,7 @@ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte } if (!gset || gset->outline_drawing - || !fe->loadGlyphs(gset, glyphs.data(), glyphs.size(), neededFormat)) + || !fe->loadGlyphs(gset, glyphs.data(), glyphs.size(), positions, neededFormat)) { QPaintEngine::drawTextItem(p, ti); return; @@ -3494,12 +3494,13 @@ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte }; for(int i = 0; i < glyphs.size(); i++) { - QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i]); + QFixed spp = fe->subPixelPositionForX(positions[i].x); + QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp); if (!glyph || glyph->format != neededFormat) { if (!lockedFace) lockedFace = fe->lockFace(); - glyph = fe->loadGlyph(gset, glyphs[i], neededFormat); + glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat); } if (!glyph || !glyph->data) diff --git a/src/gui/painting/qpaintengine_x11.cpp b/src/gui/painting/qpaintengine_x11.cpp index 8b71b83..16ef09b 100644 --- a/src/gui/painting/qpaintengine_x11.cpp +++ b/src/gui/painting/qpaintengine_x11.cpp @@ -2385,7 +2385,7 @@ void QX11PaintEngine::drawFreetype(const QPointF &p, const QTextItemInt &ti) set = ft->loadTransformedGlyphSet(d->matrix); if (!set || set->outline_drawing - || !ft->loadGlyphs(set, glyphs.data(), glyphs.size(), QFontEngineFT::Format_Render)) + || !ft->loadGlyphs(set, glyphs.data(), glyphs.size(), positions, QFontEngineFT::Format_Render)) { QPaintEngine::drawTextItem(p, ti); return; diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index e75c0f5..b83dbc0 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -134,9 +134,13 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const if (!supportsSubPixelPositions) { m_subPixelPositionCount = 1; } else { +#if !defined(Q_WS_X11) int i = 0; while (m_subPixelPositionCount == 0 && i < numGlyphs) m_subPixelPositionCount = calculateSubPixelPositionCount(glyphs[i++]); +#else + m_subPixelPositionCount = 4; +#endif } } @@ -307,9 +311,11 @@ QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g, QFixed subPixelPosition QFontEngineFT *ft = static_cast (m_current_fontengine); QFontEngineFT::QGlyphSet *gset = ft->loadTransformedGlyphSet(m_transform); + QFontEngineFT::Glyph *glyph = NULL; + if (gset) + glyph = ft->loadGlyph(gset, g, subPixelPosition, format); - if (gset && ft->loadGlyphs(gset, &g, 1, format)) { - QFontEngineFT::Glyph *glyph = gset->getGlyph(g); + if (glyph) { const int bytesPerLine = (format == QFontEngineFT::Format_Mono ? ((glyph->width + 31) & ~31) >> 3 : (glyph->width + 3) & ~3); return QImage(glyph->data, glyph->width, glyph->height, bytesPerLine, imageFormat); diff --git a/src/gui/text/qfontdatabase_x11.cpp b/src/gui/text/qfontdatabase_x11.cpp index 02b0148..0c0c4c8 100644 --- a/src/gui/text/qfontdatabase_x11.cpp +++ b/src/gui/text/qfontdatabase_x11.cpp @@ -746,6 +746,7 @@ QFontDef qt_FcPatternToQFontDef(FcPattern *pattern, const QFontDef &request) QFontDef fontDef; fontDef.styleStrategy = request.styleStrategy; + fontDef.hintingPreference = request.hintingPreference; FcChar8 *value = 0; if (FcPatternGetString(pattern, FC_FAMILY, 0, &value) == FcResultMatch) { fontDef.family = QString::fromUtf8(reinterpret_cast(value)); 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 &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; } diff --git a/src/gui/text/qfontengine_ft_p.h b/src/gui/text/qfontengine_ft_p.h index abdbd20..63fd9a7 100644 --- a/src/gui/text/qfontengine_ft_p.h +++ b/src/gui/text/qfontengine_ft_p.h @@ -166,6 +166,19 @@ public: }; #endif + struct GlyphAndSubPixelPosition + { + GlyphAndSubPixelPosition(glyph_t g, QFixed spp) : glyph(g), subPixelPosition(spp) {} + + bool operator==(const GlyphAndSubPixelPosition &other) const + { + return glyph == other.glyph && subPixelPosition == other.subPixelPosition; + } + + glyph_t glyph; + QFixed subPixelPosition; + }; + struct QGlyphSet { QGlyphSet(); @@ -174,18 +187,21 @@ public: unsigned long id; // server sided id, GlyphSet for X11 bool outline_drawing; - void removeGlyphFromCache(int index); + void removeGlyphFromCache(glyph_t index, QFixed subPixelPosition); void clear(); - inline Glyph *getGlyph(int index) const + inline bool useFastGlyphData(glyph_t index, QFixed subPixelPosition) const { + return (index < 256 && subPixelPosition == 0); + } + inline Glyph *getGlyph(glyph_t index, QFixed subPixelPosition = 0) const { - if (index < 256) + if (useFastGlyphData(index, subPixelPosition)) return fast_glyph_data[index]; - return glyph_data.value(index); + return glyph_data.value(GlyphAndSubPixelPosition(index, subPixelPosition)); } - void setGlyph(int index, Glyph *glyph); + void setGlyph(glyph_t index, QFixed spp, Glyph *glyph); private: - mutable QHash glyph_data; // maps from glyph index to glyph data + mutable QHash glyph_data; // maps from glyph index to glyph data mutable Glyph *fast_glyph_data[256]; // for fast lookup of glyphs < 256 mutable int fast_glyph_count; }; @@ -193,6 +209,11 @@ private: virtual QFontEngine::FaceId faceId() const; virtual QFontEngine::Properties properties() const; virtual QFixed emSquareSize() const; + virtual bool supportsSubPixelPositions() const + { + return default_hint_style == HintLight || + default_hint_style == HintNone; + } virtual bool getSfntTableData(uint tag, uchar *buffer, uint *length) const; virtual int synthesized() const; @@ -254,17 +275,20 @@ private: inline bool invalid() const { return xsize == 0 && ysize == 0; } inline bool isBitmapFont() const { return defaultFormat == Format_Mono; } - inline Glyph *loadGlyph(uint glyph, GlyphFormat format = Format_None, bool fetchMetricsOnly = false) const - { return loadGlyph(&defaultGlyphSet, glyph, format, fetchMetricsOnly); } - Glyph *loadGlyph(QGlyphSet *set, uint glyph, GlyphFormat = Format_None, bool fetchMetricsOnly = false) const; + inline Glyph *loadGlyph(uint glyph, QFixed subPixelPosition, GlyphFormat format = Format_None, bool fetchMetricsOnly = false) const + { return loadGlyph(&defaultGlyphSet, glyph, subPixelPosition, format, fetchMetricsOnly); } + Glyph *loadGlyph(QGlyphSet *set, uint glyph, QFixed subPixelPosition, GlyphFormat = Format_None, bool fetchMetricsOnly = false) const; QGlyphSet *defaultGlyphs() { return &defaultGlyphSet; } GlyphFormat defaultGlyphFormat() const { return defaultFormat; } - inline Glyph *cachedGlyph(glyph_t g) const { return defaultGlyphSet.getGlyph(g); } + inline Glyph *cachedGlyph(glyph_t g) const { return defaultGlyphSet.getGlyph(g, 0); } QGlyphSet *loadTransformedGlyphSet(const QTransform &matrix); - bool loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, GlyphFormat format = Format_Render); + QFixed subPixelPositionForX(QFixed x); + bool loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, + QVarLengthArray &positions, + GlyphFormat format = Format_Render); #if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) virtual void draw(QPaintEngine * /*p*/, qreal /*x*/, qreal /*y*/, const QTextItemInt & /*si*/) {} @@ -309,6 +333,7 @@ protected: private: QFontEngineFT::Glyph *loadGlyphMetrics(QGlyphSet *set, uint glyph, GlyphFormat format) const; + int loadFlags(QGlyphSet *set, GlyphFormat format, int flags, bool &hsubpixel, int &vfactor) const; GlyphFormat defaultFormat; FT_Matrix matrix; @@ -330,6 +355,11 @@ private: mutable bool kerning_pairs_loaded; }; +inline uint qHash(const QFontEngineFT::GlyphAndSubPixelPosition &g) +{ + return (g.glyph << 8) | (g.subPixelPosition * 10).round().toInt(); +} + QT_END_NAMESPACE #endif // QT_NO_FREETYPE diff --git a/src/gui/text/qfontengine_x11.cpp b/src/gui/text/qfontengine_x11.cpp index d5ec584..e442471 100644 --- a/src/gui/text/qfontengine_x11.cpp +++ b/src/gui/text/qfontengine_x11.cpp @@ -1012,8 +1012,22 @@ QFontEngineX11FT::QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int s } } + if (fd.hintingPreference != QFont::PreferDefaultHinting) { + switch (fd.hintingPreference) { + case QFont::PreferNoHinting: + default_hint_style = HintNone; + break; + case QFont::PreferVerticalHinting: + default_hint_style = HintLight; + break; + case QFont::PreferFullHinting: + default: + default_hint_style = HintFull; + break; + } + } #ifdef FC_HINT_STYLE - { + else { int hint_style = 0; if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch) hint_style = X11->fc_hint_style; -- cgit v0.12 From 8582d3b1ad7d879dcb55124933d0d93edb47b66b Mon Sep 17 00:00:00 2001 From: Jiang Jiang Date: Tue, 22 Mar 2011 17:06:14 +0100 Subject: unlockFace was put in the wrong place in previous patch Should only unlock once after the loop. Reviewed-by: TrustMe --- src/gui/text/qfontengine_ft.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index a4211a9..e90a12f 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -1694,13 +1694,13 @@ void QFontEngineFT::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlag 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) -- cgit v0.12 From 1e6e5fdd02757b9f8b5abf23973cbdc76ae67333 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Wed, 23 Mar 2011 10:54:44 +0100 Subject: Reset GL glyph cache when texture limit is reached This is a port of 72f161739b270b01807f97cd853030440f0fd430 which was reverted in integration to master, since it was incompatible with the refactored glyph cache. When the GL glyph cache reached its texture limit, we have to reset it and start over. Added a new cleanup() function in QGLContextGroupResourceBase which deletes the resource for a given context and removes itself from that context group. Task-number: QTBUG-13784 Reviewed-by: Samuel --- src/gui/painting/qtextureglyphcache.cpp | 5 +++++ src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp | 9 +++++++-- src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp | 15 +++++++++++++++ src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h | 2 ++ src/opengl/qgl.cpp | 15 +++++++++++++++ src/opengl/qgl_p.h | 1 + 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index b83dbc0..eab02e1 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -238,6 +238,11 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const } } + if (maxTextureHeight() > 0 && m_cy + c.h > maxTextureHeight()) { + // We can't make a cache of the required size, so we bail out + return false; + } + c.x = m_cx; c.y = m_cy; diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index a134078..1fa02e1 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1581,8 +1581,13 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp // cache so this text is performed before we test if the cache size has changed. if (recreateVertexArrays) { cache->setPaintEnginePrivate(this); - cache->populate(staticTextItem->fontEngine(), staticTextItem->numGlyphs, - staticTextItem->glyphs, staticTextItem->glyphPositions); + if (!cache->populate(staticTextItem->fontEngine(), staticTextItem->numGlyphs, + staticTextItem->glyphs, staticTextItem->glyphPositions)) { + // No space for glyphs in cache. We need to reset it and try again. + cache->clear(); + cache->populate(staticTextItem->fontEngine(), staticTextItem->numGlyphs, + staticTextItem->glyphs, staticTextItem->glyphPositions); + } cache->fillInPendingGlyphs(); } diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp index 9e8e828..c867d60 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp @@ -378,4 +378,19 @@ int QGLTextureGlyphCache::maxTextureHeight() const else return ctx->d_ptr->maxTextureSize(); } + +void QGLTextureGlyphCache::clear() +{ + if (ctx != 0) { + m_textureResource.cleanup(ctx); + + m_w = 0; + m_h = 0; + m_cx = 0; + m_cy = 0; + m_currentRowHeight = 0; + coords.clear(); + } +} + QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h index cc6de28..133289e 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h @@ -142,6 +142,8 @@ public: FilterMode filterMode() const { return m_filterMode; } void setFilterMode(FilterMode m) { m_filterMode = m; } + void clear(); + void freeResource(void *) { ctx = 0; } private: diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 19858e7..4698a5c 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -5606,6 +5606,21 @@ void *QGLContextGroupResourceBase::value(const QGLContext *context) return group->m_resources.value(this, 0); } +void QGLContextGroupResourceBase::cleanup(const QGLContext *ctx) +{ + void *resource = value(ctx); + + if (resource != 0) { + QGLShareContextScope scope(ctx); + freeResource(resource); + + QGLContextGroup *group = QGLContextPrivate::contextGroup(ctx); + group->m_resources.remove(this); + m_groups.removeOne(group); + active.deref(); + } +} + void QGLContextGroupResourceBase::cleanup(const QGLContext *ctx, void *value) { #ifdef QT_GL_CONTEXT_RESOURCE_DEBUG diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 5a5e5cc..b2261a2 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -687,6 +687,7 @@ public: virtual ~QGLContextGroupResourceBase(); void insert(const QGLContext *context, void *value); void *value(const QGLContext *context); + void cleanup(const QGLContext *context); void cleanup(const QGLContext *context, void *value); virtual void freeResource(void *value) = 0; -- cgit v0.12