diff options
Diffstat (limited to 'src/gui/painting/qtextureglyphcache.cpp')
-rw-r--r-- | src/gui/painting/qtextureglyphcache.cpp | 204 |
1 files changed, 164 insertions, 40 deletions
diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index 2c1da24..3973abd 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -65,8 +65,60 @@ static inline int qt_next_power_of_two(int v) return v; } +int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const +{ + // Test 12 different subpixel positions since it factors into 3*4 so it gives + // the coverage we need. + + QList<QImage> images; + for (int i=0; i<12; ++i) { + QImage img = textureMapForGlyph(glyph, QFixed::fromReal(i / 12.0)); + + if (images.isEmpty()) { + QPainterPath path; + QFixedPoint point; + m_current_fontengine->addGlyphsToPath(&glyph, &point, 1, &path, QTextItem::RenderFlags()); + + // Glyph is space, return 0 to indicate that we need to keep trying + if (path.isEmpty()) + break; + + images.append(img); + } else { + bool found = false; + for (int j=0; j<images.size(); ++j) { + if (images.at(j) == img) { + found = true; + break; + } + } + if (!found) + images.append(img); + } + } + + return images.size(); +} + +QFixed QTextureGlyphCache::subPixelPositionForX(QFixed x) const +{ + if (m_subPixelPositionCount <= 1) + return QFixed(); + + QFixed subPixelPosition; + if (x != 0) { + subPixelPosition = x - x.floor(); + QFixed fraction = (subPixelPosition / QFixed::fromReal(1.0 / m_subPixelPositionCount)).floor(); + + // Compensate for precision loss in fixed point to make sure we are always drawing at a subpixel position over + // the lower boundary for the selected rasterization by adding 1/64. + subPixelPosition = fraction / QFixed(m_subPixelPositionCount) + QFixed::fromReal(0.015625); + } + return subPixelPosition; +} + bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs, - const QFixedPoint *) + const QFixedPoint *positions) { #ifdef CACHE_DEBUG printf("Populating with %d glyphs\n", numGlyphs); @@ -77,17 +129,46 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const const int margin = glyphMargin(); const int paddingDoubled = glyphPadding() * 2; - QHash<glyph_t, Coord> listItemCoordinates; + bool supportsSubPixelPositions = fontEngine->supportsSubPixelPositions(); + if (m_subPixelPositionCount == 0) { + 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 + } + } + + QHash<GlyphAndSubPixelPosition, Coord> listItemCoordinates; int rowHeight = 0; + QFontEngine::GlyphFormat format; + switch (m_type) { + case Raster_A8: format = QFontEngine::Format_A8; break; + case Raster_RGBMask: format = QFontEngine::Format_A32; break; + default: format = QFontEngine::Format_Mono; break; + } + // check each glyph for its metrics and get the required rowHeight. for (int i=0; i < numGlyphs; ++i) { const glyph_t glyph = glyphs[i]; - if (coords.contains(glyph)) + + QFixed subPixelPosition; + if (supportsSubPixelPositions) { + QFixed x = positions != 0 ? positions[i].x : QFixed(); + subPixelPosition = subPixelPositionForX(x); + } + + if (coords.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition))) continue; - if (listItemCoordinates.contains(glyph)) + if (listItemCoordinates.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition))) continue; - glyph_metrics_t metrics = fontEngine->boundingBox(glyph, m_transform); + glyph_metrics_t metrics = fontEngine->alphaMapBoundingBox(glyph, subPixelPosition, m_transform, format); #ifdef CACHE_DEBUG printf("(%4x): w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f\n", @@ -98,11 +179,16 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const metrics.yoff.toReal(), metrics.x.toReal(), metrics.y.toReal()); -#endif +#endif + GlyphAndSubPixelPosition key(glyph, subPixelPosition); int glyph_width = metrics.width.ceil().toInt(); int glyph_height = metrics.height.ceil().toInt(); - if (glyph_height == 0 || glyph_width == 0) + if (glyph_height == 0 || glyph_width == 0) { + // Avoid multiple calls to boundingBox() for non-printable characters + Coord c = { 0, 0, 0, 0, 0, 0 }; + coords.insert(key, c); continue; + } glyph_width += margin * 2 + 4; glyph_height += margin * 2 + 4; // align to 8-bit boundary @@ -112,33 +198,38 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const Coord c = { 0, 0, // will be filled in later glyph_width, glyph_height, // texture coords - metrics.x.round().truncate(), + metrics.x.truncate(), -metrics.y.truncate() }; // baseline for horizontal scripts - listItemCoordinates.insert(glyph, c); + listItemCoordinates.insert(key, c); rowHeight = qMax(rowHeight, glyph_height); } if (listItemCoordinates.isEmpty()) return true; rowHeight += margin * 2 + paddingDoubled; - if (isNull()) - createCache(QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH, qt_next_power_of_two(rowHeight)); + + if (m_w == 0) { + if (fontEngine->maxCharWidth() <= QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH) + m_w = QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH; + else + m_w = qt_next_power_of_two(fontEngine->maxCharWidth()); + } // now actually use the coords and paint the wanted glyps into cache. - QHash<glyph_t, Coord>::iterator iter = listItemCoordinates.begin(); + QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = listItemCoordinates.begin(); + int requiredWidth = m_w; while (iter != listItemCoordinates.end()) { Coord c = iter.value(); m_currentRowHeight = qMax(m_currentRowHeight, c.h + margin * 2); - if (m_cx + c.w > m_w) { - int new_width = m_w*2; + if (m_cx + c.w > requiredWidth) { + int new_width = requiredWidth*2; while (new_width < m_cx + c.w) new_width *= 2; if (new_width <= maxTextureWidth()) { - resizeTextureData(new_width, m_h); - m_w = new_width; + requiredWidth = new_width; } else { // no room on the current line, start new glyph strip m_cx = 0; @@ -146,36 +237,63 @@ bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const m_currentRowHeight = c.h + margin * 2; // New row } } - if (m_cy + c.h > m_h) { - int new_height = m_h*2; - while (new_height < m_cy + c.h) - new_height *= 2; - - if (maxTextureHeight() > 0 && new_height > maxTextureHeight()) { - // We can't make a new texture of the required size, so - // bail out - return false; - } - // if no room in the current texture - realloc a larger texture - resizeTextureData(m_w, new_height); - m_h = new_height; + 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; - fillTexture(c, iter.key()); coords.insert(iter.key(), c); + m_pendingGlyphs.insert(iter.key(), c); m_cx += c.w + paddingDoubled; ++iter; } - return true; + } -QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g) const +void QTextureGlyphCache::fillInPendingGlyphs() +{ + if (m_pendingGlyphs.isEmpty()) + return; + + int requiredHeight = m_h; + int requiredWidth = m_w; // Use a minimum size to avoid a lot of initial reallocations + { + QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = m_pendingGlyphs.begin(); + while (iter != m_pendingGlyphs.end()) { + Coord c = iter.value(); + requiredHeight = qMax(requiredHeight, c.y + c.h); + requiredWidth = qMax(requiredWidth, c.x + c.w); + ++iter; + } + } + + if (isNull() || requiredHeight > m_h || requiredWidth > m_w) { + if (isNull()) + createCache(qt_next_power_of_two(requiredWidth), qt_next_power_of_two(requiredHeight)); + else + resizeCache(qt_next_power_of_two(requiredWidth), qt_next_power_of_two(requiredHeight)); + } + + { + QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = m_pendingGlyphs.begin(); + while (iter != m_pendingGlyphs.end()) { + GlyphAndSubPixelPosition key = iter.key(); + fillTexture(iter.value(), key.glyph, key.subPixelPosition); + + ++iter; + } + } + + m_pendingGlyphs.clear(); +} + +QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g, QFixed subPixelPosition) const { #if defined(Q_WS_X11) if (m_type != Raster_RGBMask && m_transform.type() > QTransform::TxTranslate && m_current_fontengine->type() == QFontEngine::Freetype) { @@ -190,13 +308,19 @@ QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g) const format = QFontEngineFT::Format_Mono; imageFormat = QImage::Format_Mono; break; + case Raster_RGBMask: + // impossible condition (see the if-clause above) + // this option is here only to silence a compiler warning + break; }; QFontEngineFT *ft = static_cast<QFontEngineFT*> (m_current_fontengine); QFontEngineFT::QGlyphSet *gset = ft->loadTransformedGlyphSet(m_transform); + QFixedPoint positions[1]; + positions[0].x = subPixelPosition; - if (gset && ft->loadGlyphs(gset, &g, 1, format)) { - QFontEngineFT::Glyph *glyph = gset->getGlyph(g); + 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); @@ -204,9 +328,9 @@ QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g) const } else #endif if (m_type == QFontEngineGlyphCache::Raster_RGBMask) - return m_current_fontengine->alphaRGBMapForGlyph(g, glyphMargin(), m_transform); + return m_current_fontengine->alphaRGBMapForGlyph(g, subPixelPosition, glyphMargin(), m_transform); else - return m_current_fontengine->alphaMapForGlyph(g, m_transform); + return m_current_fontengine->alphaMapForGlyph(g, subPixelPosition, m_transform); return QImage(); } @@ -243,16 +367,16 @@ 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; #endif } -void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g) +void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g, QFixed subPixelPosition) { - QImage mask = textureMapForGlyph(g); + QImage mask = textureMapForGlyph(g, subPixelPosition); #ifdef CACHE_DEBUG printf("fillTexture of %dx%d at %d,%d in the cache of %dx%d\n", c.w, c.h, c.x, c.y, m_image.width(), m_image.height()); @@ -337,7 +461,7 @@ void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g) QPoint base(c.x + glyphMargin(), c.y + glyphMargin() + c.baseLineY-1); if (m_image.rect().contains(base)) m_image.setPixel(base, 255); - m_image.save(QString::fromLatin1("cache-%1.png").arg(int(this))); + m_image.save(QString::fromLatin1("cache-%1.png").arg(qint64(this))); #endif } |