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 From 254e14a81708213312d8b64d73fbc1dfc362b0d0 Mon Sep 17 00:00:00 2001 From: aavit Date: Wed, 23 Mar 2011 13:40:50 +0100 Subject: Avoid repeatedly trying to load unloadable plugins, causing slowness In certain uncommon situations, where different Qt versions are used on the same system, the plugin caching optimization may identify a dll as a valid plugin, even though it will later fail to load. This fix will avoid that Qt repeatdly tries to reopen such dlls, something which caused a significant performance hit in these cases. E.g. where Qt would unsuccessfully try to load a number of KDE image format plugins for every image loading operation. Task-number: QTBUG-10066 Reviewed-by: thiago Reviewed-by: janarve --- src/corelib/plugin/qlibrary.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index ecd1aac..80e927b 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -516,6 +516,8 @@ bool QLibraryPrivate::loadPlugin() libraryUnloadCount.ref(); return true; } + if (pluginState == IsNotAPlugin) + return false; if (load()) { instance = (QtPluginInstanceFunction)resolve("qt_plugin_instance"); #if defined(Q_OS_SYMBIAN) @@ -528,6 +530,9 @@ bool QLibraryPrivate::loadPlugin() #endif return instance; } + if (qt_debug_component()) + qWarning() << "QLibraryPrivate::loadPlugin failed on" << fileName << ":" << errorString; + pluginState = IsNotAPlugin; return false; } @@ -687,7 +692,7 @@ bool QLibraryPrivate::isPlugin(QSettings *settings) .arg((QT_VERSION & 0xff00) >> 8) .arg(QLIBRARY_AS_DEBUG ? QLatin1String("debug") : QLatin1String("false")) .arg(fileName); -#ifdef Q_WS_MAC +#ifdef Q_WS_MAC // On Mac, add the application arch to the reg key in order to // cache plugin information separately for each arch. This prevents // Qt from wrongly caching plugin load failures when the archs @@ -702,7 +707,7 @@ bool QLibraryPrivate::isPlugin(QSettings *settings) regkey += QLatin1String("-ppc"); #endif #endif // Q_WS_MAC - + QStringList reg; #ifndef QT_NO_SETTINGS if (!settings) { -- cgit v0.12 From c6646102288ccb3619895405f3ec42c5803a13b7 Mon Sep 17 00:00:00 2001 From: Jiang Jiang Date: Thu, 24 Mar 2011 13:33:31 +0100 Subject: Let QTextLine decide its own x position in QPainter So that it can take trailing space width into account when doing right aligned text drawing. Task-number: QTBUG-18303 Reviewed-by: Eskil --- src/gui/painting/qpainter.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 2c25e70..b1f5f4c 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -8149,6 +8149,10 @@ start_lengthVariant: engine.option.setTextDirection(layout_direction); if (tf & Qt::AlignJustify) engine.option.setAlignment(Qt::AlignJustify); + else if (tf & Qt::AlignRight) + engine.option.setAlignment(Qt::AlignRight); + else if (tf & Qt::AlignHCenter) + engine.option.setAlignment(Qt::AlignHCenter); else engine.option.setAlignment(Qt::AlignLeft); // do not do alignment twice @@ -8244,14 +8248,7 @@ start_lengthVariant: for (int i = 0; i < textLayout.lineCount(); i++) { QTextLine line = textLayout.lineAt(i); - - qreal advance = line.horizontalAdvance(); - if (tf & Qt::AlignRight) - xoff = r.width() - advance; - else if (tf & Qt::AlignHCenter) - xoff = (r.width() - advance)/2; - - line.draw(painter, QPointF(r.x() + xoff + line.x(), r.y() + yoff)); + line.draw(painter, QPointF(r.x(), r.y() + yoff)); } if (restore) { -- cgit v0.12 From d2ab40f8c3ba0f13dc15ca8286afea291140d8ec Mon Sep 17 00:00:00 2001 From: Jiang Jiang Date: Wed, 23 Mar 2011 16:51:41 +0100 Subject: qFloor the decoration line width before painting It's a special case when enabling design (fractional) metrics, we need to floor it to keep consistency with integer metrics. Reviewed-by: Eskil --- src/gui/painting/qpainter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index b1f5f4c..14fb772 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -6463,7 +6463,7 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const pen.setWidthF(fe->lineThickness().toReal()); pen.setCapStyle(Qt::FlatCap); - QLineF line(pos.x(), pos.y(), pos.x() + width, pos.y()); + QLineF line(pos.x(), pos.y(), pos.x() + qFloor(width), pos.y()); const qreal underlineOffset = fe->underlinePosition().toReal(); // deliberately ceil the offset to avoid the underline coming too close to -- cgit v0.12 From dcdc7eccf7ff32396816059dc932c358f73cf12b Mon Sep 17 00:00:00 2001 From: Jiang Jiang Date: Wed, 23 Mar 2011 16:54:01 +0100 Subject: Fix QTransform debug output Reviewed-by: Eskil --- src/gui/painting/qtransform.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp index 4441b30..7d11e2f 100644 --- a/src/gui/painting/qtransform.cpp +++ b/src/gui/painting/qtransform.cpp @@ -1085,8 +1085,11 @@ QDebug operator<<(QDebug dbg, const QTransform &m) "TxNone", "TxTranslate", "TxScale", + 0, "TxRotate", + 0, 0, 0, "TxShear", + 0, 0, 0, 0, 0, 0, 0, "TxProject" }; -- cgit v0.12 From 47ce3672058d4cc6ca479f8b40d6cb821c38dd51 Mon Sep 17 00:00:00 2001 From: Jiang Jiang Date: Wed, 23 Mar 2011 18:35:47 +0100 Subject: Fixed remaining issues in subpixel positioning with FreeType 1. Merge QStaticText and QGlyphs drawing code of raster engine into glyph drawing code for normal QTextItem, it simplify the caching system (we don't use QImageTextureGlyphCache for raster anymore, just glyph set cache in FreeType engine is enough), and fix some regressions in QStaticText and QGlyphs drawing. 2. Fix subpixel positioning support for OpenGL texture glyph cache. Including a transform handling regression introduced by the initial patch. 3. Disable subpixel positioning for bitmap fonts (mono format). After this change, we only have two code paths for glyph rendering with FreeType: raster will always use the simple code path (with QFontEngine_FT cache) while GL will always use QTextureGlyphCache. Reviewed-by: Eskil --- src/gui/painting/qpaintengine_raster.cpp | 250 +++++++++++++++---------------- src/gui/painting/qpaintengine_raster_p.h | 2 +- src/gui/painting/qpaintengine_x11.cpp | 2 +- src/gui/painting/qtextureglyphcache.cpp | 10 +- src/gui/text/qfontengine_ft.cpp | 14 +- src/gui/text/qfontengine_ft_p.h | 6 +- 6 files changed, 142 insertions(+), 142 deletions(-) diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 3af98d6..d6cd98b 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -3076,65 +3076,147 @@ void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx blend(current, spans, &s->penData); } -void QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, +bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions, QFontEngine *fontEngine) { Q_D(QRasterPaintEngine); QRasterPaintEngineState *s = state(); - QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType; +#if !defined(QT_NO_FREETYPE) + if (fontEngine->type() == QFontEngine::Freetype) { + QFontEngineFT *fe = static_cast(fontEngine); + QFontEngineFT::GlyphFormat neededFormat = fe->defaultGlyphFormat(); - QImageTextureGlyphCache *cache = - static_cast(fontEngine->glyphCache(0, glyphType, s->matrix)); - if (!cache) { - cache = new QImageTextureGlyphCache(glyphType, s->matrix); - fontEngine->setGlyphCache(0, cache); - } + if (d_func()->mono_surface + || fe->isBitmapFont() // alphaPenBlt can handle mono, too + ) + neededFormat = QFontEngineFT::Format_Mono; - cache->populate(fontEngine, numGlyphs, glyphs, positions); - cache->fillInPendingGlyphs(); + if (neededFormat == QFontEngineFT::Format_None) + neededFormat = QFontEngineFT::Format_A8; - const QImage &image = cache->image(); - int bpl = image.bytesPerLine(); + QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs(); + if (s->matrix.type() >= QTransform::TxScale) { + if (s->matrix.isAffine()) + gset = fe->loadTransformedGlyphSet(s->matrix); + else + gset = 0; + } - int depth = image.depth(); - int rightShift = 0; - int leftShift = 0; - if (depth == 32) - leftShift = 2; // multiply by 4 - else if (depth == 1) - rightShift = 3; // divide by 8 + if (!gset || gset->outline_drawing + || !fe->loadGlyphs(gset, glyphs, numGlyphs, positions, neededFormat)) + return false; - int margin = cache->glyphMargin(); + FT_Face lockedFace = 0; - bool supportsSubPixelPositions = fontEngine->supportsSubPixelPositions(); + int depth; + switch (neededFormat) { + case QFontEngineFT::Format_Mono: + depth = 1; + break; + case QFontEngineFT::Format_A8: + depth = 8; + break; + case QFontEngineFT::Format_A32: + depth = 32; + break; + default: + Q_ASSERT(false); + depth = 0; + }; - const uchar *bits = image.bits(); - for (int i=0; isubPixelPositionForX(positions[i].x); + QFontEngineFT::Glyph *glyph = gset->getGlyph(glyphs[i], spp); - QFixed subPixelPosition; - if (supportsSubPixelPositions) - subPixelPosition = cache->subPixelPositionForX(positions[i].x); - QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition); - const QTextureGlyphCache::Coord &c = cache->coords[glyph]; - if (c.isNull()) - continue; + if (!glyph || glyph->format != neededFormat) { + if (!lockedFace) + lockedFace = fe->lockFace(); + glyph = fe->loadGlyph(gset, glyphs[i], spp, neededFormat); + } + + if (!glyph || !glyph->data) + continue; + + int pitch; + switch (neededFormat) { + case QFontEngineFT::Format_Mono: + pitch = ((glyph->width + 31) & ~31) >> 3; + break; + case QFontEngineFT::Format_A8: + pitch = (glyph->width + 3) & ~3; + break; + case QFontEngineFT::Format_A32: + pitch = glyph->width * 4; + break; + default: + Q_ASSERT(false); + pitch = 0; + }; + + alphaPenBlt(glyph->data, pitch, depth, + qFloor(positions[i].x) + glyph->x, + qFloor(positions[i].y) - glyph->y, + glyph->width, glyph->height); + } + if (lockedFace) + fe->unlockFace(); + } else +#endif + { + QFontEngineGlyphCache::Type glyphType = fontEngine->glyphFormat >= 0 ? QFontEngineGlyphCache::Type(fontEngine->glyphFormat) : d->glyphCacheType; - int x = qFloor(positions[i].x) + c.baseLineX - margin; - int y = qFloor(positions[i].y) - c.baseLineY - margin; + QImageTextureGlyphCache *cache = + static_cast(fontEngine->glyphCache(0, glyphType, s->matrix)); + if (!cache) { + cache = new QImageTextureGlyphCache(glyphType, s->matrix); + fontEngine->setGlyphCache(0, cache); + } -// printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n", -// c.x, c.y, -// c.w, c.h, -// c.baseLineX, c.baseLineY, -// glyphs[i], -// x, y, -// positions[i].x.toInt(), positions[i].y.toInt()); + cache->populate(fontEngine, numGlyphs, glyphs, positions); + cache->fillInPendingGlyphs(); - alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h); - } + const QImage &image = cache->image(); + int bpl = image.bytesPerLine(); - return; + int depth = image.depth(); + int rightShift = 0; + int leftShift = 0; + if (depth == 32) + leftShift = 2; // multiply by 4 + else if (depth == 1) + rightShift = 3; // divide by 8 + + int margin = cache->glyphMargin(); + + bool supportsSubPixelPositions = fontEngine->supportsSubPixelPositions(); + + const uchar *bits = image.bits(); + for (int i=0; isubPixelPositionForX(positions[i].x); + QTextureGlyphCache::GlyphAndSubPixelPosition glyph(glyphs[i], subPixelPosition); + const QTextureGlyphCache::Coord &c = cache->coords[glyph]; + if (c.isNull()) + continue; + + int x = qFloor(positions[i].x) + c.baseLineX - margin; + int y = qFloor(positions[i].y) - c.baseLineY - margin; + + // printf("drawing [%d %d %d %d] baseline [%d %d], glyph: %d, to: %d %d, pos: %d %d\n", + // c.x, c.y, + // c.w, c.h, + // c.baseLineX, c.baseLineY, + // glyphs[i], + // x, y, + // positions[i].x.toInt(), positions[i].y.toInt()); + + alphaPenBlt(bits + ((c.x << leftShift) >> rightShift) + c.y * bpl, bpl, depth, x, y, c.w, c.h); + } + } + return true; } #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) @@ -3445,92 +3527,10 @@ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte if (glyphs.size() == 0) return; - // only use subpixel antialiasing when drawing to widgets - QFontEngineFT::GlyphFormat neededFormat = - painter()->device()->devType() == QInternal::Widget - ? fe->defaultGlyphFormat() - : QFontEngineFT::Format_A8; - - if (d_func()->mono_surface - || fe->isBitmapFont() // alphaPenBlt can handle mono, too - ) - neededFormat = QFontEngineFT::Format_Mono; - - if (neededFormat == QFontEngineFT::Format_None) - neededFormat = QFontEngineFT::Format_A8; - - QFontEngineFT::QGlyphSet *gset = fe->defaultGlyphs(); - if (s->matrix.type() >= QTransform::TxScale) { - if (s->matrix.isAffine()) - gset = fe->loadTransformedGlyphSet(s->matrix); - else - gset = 0; - - } - - if (!gset || gset->outline_drawing - || !fe->loadGlyphs(gset, glyphs.data(), glyphs.size(), positions, neededFormat)) - { + if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine)) QPaintEngine::drawTextItem(p, ti); - return; - } - - FT_Face lockedFace = 0; - - int depth; - switch (neededFormat) { - case QFontEngineFT::Format_Mono: - depth = 1; - break; - case QFontEngineFT::Format_A8: - depth = 8; - break; - case QFontEngineFT::Format_A32: - depth = 32; - break; - default: - Q_ASSERT(false); - depth = 0; - }; - for(int i = 0; i < glyphs.size(); 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], spp, neededFormat); - } - - if (!glyph || !glyph->data) - continue; - - int pitch; - switch (neededFormat) { - case QFontEngineFT::Format_Mono: - pitch = ((glyph->width + 31) & ~31) >> 3; - break; - case QFontEngineFT::Format_A8: - pitch = (glyph->width + 3) & ~3; - break; - case QFontEngineFT::Format_A32: - pitch = glyph->width * 4; - break; - default: - Q_ASSERT(false); - pitch = 0; - }; - - alphaPenBlt(glyph->data, pitch, depth, - qFloor(positions[i].x) + glyph->x, - qFloor(positions[i].y) - glyph->y, - glyph->width, glyph->height); - } - if (lockedFace) - fe->unlockFace(); return; - #endif #endif diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h index 7f902a8..52f51fa 100644 --- a/src/gui/painting/qpaintengine_raster_p.h +++ b/src/gui/painting/qpaintengine_raster_p.h @@ -261,7 +261,7 @@ private: void fillRect(const QRectF &rect, QSpanData *data); void drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fill); - void drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions, + bool drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions, QFontEngine *fontEngine); #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) diff --git a/src/gui/painting/qpaintengine_x11.cpp b/src/gui/painting/qpaintengine_x11.cpp index 16ef09b..94828fb 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(), positions, QFontEngineFT::Format_Render)) + || !ft->loadGlyphs(set, glyphs.constData(), glyphs.size(), positions.constData(), QFontEngineFT::Format_Render)) { QPaintEngine::drawTextItem(p, ti); return; diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index eab02e1..f0db805 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -316,11 +316,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); + QFixedPoint positions[1]; + positions[0].x = subPixelPosition; - if (glyph) { + if (gset && ft->loadGlyphs(gset, &g, 1, positions, format)) { + QFontEngineFT::Glyph *glyph = gset->getGlyph(g, subPixelPosition); 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); @@ -367,7 +367,7 @@ void QImageTextureGlyphCache::createTextureData(int width, int height) int QImageTextureGlyphCache::glyphMargin() const { -#if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA) +#if (defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)) || defined(Q_WS_X11) return 0; #else return m_type == QFontEngineGlyphCache::Raster_RGBMask ? 2 : 0; diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index e90a12f..6c6781c 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -944,7 +944,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, FT_Face face = freetype->face; FT_Vector v; - v.x = FT_Pos((subPixelPosition).toReal() * 64); + v.x = format == Format_Mono ? 0 : FT_Pos(subPixelPosition.toReal() * 64); v.y = 0; FT_Set_Transform(face, &freetype->matrix, &v); @@ -1049,7 +1049,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadGlyph(QGlyphSet *set, uint glyph, int hpixels = TRUNC(right - left); // subpixel position requires one more pixel - if (subPixelPosition > 0) + if (subPixelPosition > 0 && format != Format_Mono) hpixels++; if (hsubpixel) @@ -1440,8 +1440,8 @@ QFixed QFontEngineFT::subPixelPositionForX(QFixed x) return subPixelPosition; } -bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, - QVarLengthArray &positions, +bool QFontEngineFT::loadGlyphs(QGlyphSet *gs, const glyph_t *glyphs, int num_glyphs, + const QFixedPoint *positions, GlyphFormat format) { FT_Face face = 0; @@ -1866,16 +1866,16 @@ glyph_metrics_t QFontEngineFT::alphaMapBoundingBox(glyph_t glyph, const QTransfo return overall; } -QImage QFontEngineFT::alphaMapForGlyph(glyph_t g) +QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition) { lockFace(); GlyphFormat glyph_format = antialias ? Format_A8 : Format_Mono; - Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, 0, glyph_format); + Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, subPixelPosition, glyph_format); if (!glyph) { unlockFace(); - return QFontEngine::alphaMapForGlyph(g); + return QFontEngine::alphaMapForGlyph(g, subPixelPosition); } const int pitch = antialias ? (glyph->width + 3) & ~3 : ((glyph->width + 31)/32) * 4; diff --git a/src/gui/text/qfontengine_ft_p.h b/src/gui/text/qfontengine_ft_p.h index 63fd9a7..054a57e 100644 --- a/src/gui/text/qfontengine_ft_p.h +++ b/src/gui/text/qfontengine_ft_p.h @@ -254,7 +254,7 @@ private: virtual glyph_metrics_t boundingBox(glyph_t glyph, const QTransform &matrix); virtual void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const; - virtual QImage alphaMapForGlyph(glyph_t); + virtual QImage alphaMapForGlyph(glyph_t, QFixed); virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); virtual glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, const QTransform &matrix, QFontEngine::GlyphFormat format); @@ -286,8 +286,8 @@ private: QGlyphSet *loadTransformedGlyphSet(const QTransform &matrix); QFixed subPixelPositionForX(QFixed x); - bool loadGlyphs(QGlyphSet *gs, glyph_t *glyphs, int num_glyphs, - QVarLengthArray &positions, + bool loadGlyphs(QGlyphSet *gs, const glyph_t *glyphs, int num_glyphs, + const QFixedPoint *positions, GlyphFormat format = Format_Render); #if defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) -- cgit v0.12 From 5c46d9a4c85abbcc0b5db2bbbafded3efd784cd9 Mon Sep 17 00:00:00 2001 From: Jiang Jiang Date: Thu, 25 Nov 2010 17:30:32 +0100 Subject: Take Xft.hintstyle by default to match the behavior of GTK+ For Qt apps running in GNOME, we use the GTK+ settings by default (instead of fontconfig settings). Task-number: QTBUG-13800 Reviewed-by: Samuel --- src/gui/kernel/qapplication_x11.cpp | 4 ++++ src/gui/text/qfontengine_x11.cpp | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/gui/kernel/qapplication_x11.cpp b/src/gui/kernel/qapplication_x11.cpp index 589b12e..31f70c7 100644 --- a/src/gui/kernel/qapplication_x11.cpp +++ b/src/gui/kernel/qapplication_x11.cpp @@ -1587,6 +1587,9 @@ static void getXDefault(const char *group, const char *key, int *val) int v = strtol(str, &end, 0); if (str != end) *val = v; + // otherwise use fontconfig to convert the string to integer + else + FcNameConstant((FcChar8 *) str, val); } } @@ -2237,6 +2240,7 @@ void qt_init(QApplicationPrivate *priv, int, } getXDefault("Xft", FC_ANTIALIAS, &X11->fc_antialias); #ifdef FC_HINT_STYLE + X11->fc_hint_style = -1; getXDefault("Xft", FC_HINT_STYLE, &X11->fc_hint_style); #endif #if 0 diff --git a/src/gui/text/qfontengine_x11.cpp b/src/gui/text/qfontengine_x11.cpp index e442471..9f3f8d3 100644 --- a/src/gui/text/qfontengine_x11.cpp +++ b/src/gui/text/qfontengine_x11.cpp @@ -1029,7 +1029,12 @@ QFontEngineX11FT::QFontEngineX11FT(FcPattern *pattern, const QFontDef &fd, int s #ifdef FC_HINT_STYLE else { int hint_style = 0; - if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch) + // Try to use Xft.hintstyle from XDefaults first if running in GNOME, to match + // the behavior of cairo + if (X11->fc_hint_style > -1 && X11->desktopEnvironment == DE_GNOME) + hint_style = X11->fc_hint_style; + else if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch + && X11->fc_hint_style > -1) hint_style = X11->fc_hint_style; switch (hint_style) { -- cgit v0.12 From fc7b63d4ca216d4256185aa5d522d02dd27b224c Mon Sep 17 00:00:00 2001 From: Jiang Jiang Date: Fri, 25 Mar 2011 16:59:11 +0100 Subject: Fix remaining regressions in QWS Reviewed-by: TrustMe --- src/gui/painting/qpaintengine_raster.cpp | 7 +++++-- src/gui/text/qfontengine_ft_p.h | 1 + src/gui/text/qfontengine_qpf.cpp | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index d6cd98b..9f3b9b9 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -3085,7 +3085,10 @@ bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, #if !defined(QT_NO_FREETYPE) if (fontEngine->type() == QFontEngine::Freetype) { QFontEngineFT *fe = static_cast(fontEngine); - QFontEngineFT::GlyphFormat neededFormat = fe->defaultGlyphFormat(); + QFontEngineFT::GlyphFormat neededFormat = + painter()->device()->devType() == QInternal::Widget + ? fe->defaultGlyphFormat() + : QFontEngineFT::Format_A8; if (d_func()->mono_surface || fe->isBitmapFont() // alphaPenBlt can handle mono, too @@ -3527,7 +3530,7 @@ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte if (glyphs.size() == 0) return; - if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine)) + if (!drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), fontEngine)) QPaintEngine::drawTextItem(p, ti); return; diff --git a/src/gui/text/qfontengine_ft_p.h b/src/gui/text/qfontengine_ft_p.h index 054a57e..fc3ac82 100644 --- a/src/gui/text/qfontengine_ft_p.h +++ b/src/gui/text/qfontengine_ft_p.h @@ -254,6 +254,7 @@ private: virtual glyph_metrics_t boundingBox(glyph_t glyph, const QTransform &matrix); virtual void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const; + virtual QImage alphaMapForGlyph(glyph_t g) { return alphaMapForGlyph(g, 0); } virtual QImage alphaMapForGlyph(glyph_t, QFixed); virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t); virtual glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, const QTransform &matrix, diff --git a/src/gui/text/qfontengine_qpf.cpp b/src/gui/text/qfontengine_qpf.cpp index 2fd4716..bfd04e6 100644 --- a/src/gui/text/qfontengine_qpf.cpp +++ b/src/gui/text/qfontengine_qpf.cpp @@ -306,6 +306,7 @@ QFontEngineQPF::QFontEngineQPF(const QFontDef &def, int fileDescriptor, QFontEng glyphMapEntries = 0; glyphDataOffset = 0; glyphDataSize = 0; + glyphFormat = renderingFontEngine->glyphFormat; kerning_pairs_loaded = false; readOnly = true; -- cgit v0.12 From c61d6de8de9aa43ccdd49419b9a03af61cefde73 Mon Sep 17 00:00:00 2001 From: Jiang Jiang Date: Mon, 28 Mar 2011 09:03:27 +0200 Subject: Fix crash and infinite recursion caused by previous commits Reviewed-by: TrustMe --- src/gui/text/qfontengine_ft.cpp | 2 +- src/gui/text/qfontengine_qpf.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index 6c6781c..db0156a 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -1875,7 +1875,7 @@ QImage QFontEngineFT::alphaMapForGlyph(glyph_t g, QFixed subPixelPosition) Glyph *glyph = defaultGlyphSet.outline_drawing ? 0 : loadGlyph(g, subPixelPosition, glyph_format); if (!glyph) { unlockFace(); - return QFontEngine::alphaMapForGlyph(g, subPixelPosition); + return QFontEngine::alphaMapForGlyph(g); } const int pitch = antialias ? (glyph->width + 3) & ~3 : ((glyph->width + 31)/32) * 4; diff --git a/src/gui/text/qfontengine_qpf.cpp b/src/gui/text/qfontengine_qpf.cpp index bfd04e6..d35bbe5 100644 --- a/src/gui/text/qfontengine_qpf.cpp +++ b/src/gui/text/qfontengine_qpf.cpp @@ -306,7 +306,8 @@ QFontEngineQPF::QFontEngineQPF(const QFontDef &def, int fileDescriptor, QFontEng glyphMapEntries = 0; glyphDataOffset = 0; glyphDataSize = 0; - glyphFormat = renderingFontEngine->glyphFormat; + if (renderingFontEngine) + glyphFormat = renderingFontEngine->glyphFormat; kerning_pairs_loaded = false; readOnly = true; -- cgit v0.12