From 5290c50b240abbcd0477df1db1808d33b96ca856 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 17 Aug 2009 15:29:02 +0200 Subject: Support transformations in drawStaticText() and optimize for space 1. Support transformations on the painter in drawStaticText(). Transforming the painter will cause the text layout to be recalculated, except for translations, which are handled by shifting the position of the text items. 2. Make const length arrays of the internal data in QStaticTextItem in order to minimize the memory consumption. --- src/gui/painting/qpaintengine_raster.cpp | 34 +++++----- src/gui/painting/qpaintengine_raster_p.h | 6 +- src/gui/painting/qpaintengineex.cpp | 2 +- src/gui/painting/qpainter.cpp | 31 +++++++-- src/gui/painting/qtextureglyphcache.cpp | 7 +- src/gui/painting/qtextureglyphcache_p.h | 5 +- src/gui/text/qstatictext.cpp | 77 +++++++++++++++++----- src/gui/text/qstatictext_p.h | 27 ++++---- .../gl2paintengineex/qpaintengineex_opengl2.cpp | 2 +- 9 files changed, 130 insertions(+), 61 deletions(-) diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 387d646..87f374c 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -3007,23 +3007,22 @@ void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx blend(current, spans, &s->penData); } -void QRasterPaintEngine::drawCachedGlyphs(const QVarLengthArray &glyphs, - const QVarLengthArray &positions, - QFontEngine *fontEngine, - const QTransform &matrix) +void 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; QImageTextureGlyphCache *cache = - static_cast(fontEngine->glyphCache(0, glyphType, matrix)); + static_cast(fontEngine->glyphCache(0, glyphType, s->matrix)); if (!cache) { - cache = new QImageTextureGlyphCache(glyphType, matrix); + cache = new QImageTextureGlyphCache(glyphType, s->matrix); fontEngine->setGlyphCache(0, cache); } - cache->populate(fontEngine, glyphs, positions); + cache->populate(fontEngine, numGlyphs, glyphs, positions); const QImage &image = cache->image(); int bpl = image.bytesPerLine(); @@ -3041,7 +3040,7 @@ void QRasterPaintEngine::drawCachedGlyphs(const QVarLengthArray &glyphs const QFixed offs = QFixed::fromReal(aliasedCoordinateDelta); const uchar *bits = image.bits(); - for (int i=0; icoords.value(glyphs[i]); int x = qFloor(positions[i].x + offs) + c.baseLineX - margin; int y = qFloor(positions[i].y + offs) - c.baseLineY - margin; @@ -3218,19 +3217,16 @@ void QRasterPaintEngine::drawStaticTextItem(const QPointF &p, QStaticTextItem *t ensurePen(); ensureState(); - QTransform matrix; - matrix.translate(p.x(), p.y()); - // Translate to actual position - QVarLengthArray glyphPositions = textItem->glyphPositions; - QFixed fx = QFixed::fromReal(p.x()); QFixed fy = QFixed::fromReal(p.y()); - for (int i=0; inumGlyphs; ++i) { + textItem->glyphPositions[i].x += fx; + textItem->glyphPositions[i].y += fy; } - drawCachedGlyphs(textItem->glyphs, glyphPositions, textItem->fontEngine, matrix); + + drawCachedGlyphs(textItem->numGlyphs, textItem->glyphs, textItem->glyphPositions, + textItem->fontEngine); } /*! @@ -3285,11 +3281,13 @@ void QRasterPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textIte QVarLengthArray positions; QVarLengthArray glyphs; + QTransform matrix = s->matrix; matrix.translate(p.x(), p.y()); + ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); - drawCachedGlyphs(glyphs, positions, ti.fontEngine, matrix); + drawCachedGlyphs(glyphs.size(), glyphs.constData(), positions.constData(), ti.fontEngine); return; } diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h index 0250c3d..0626a48 100644 --- a/src/gui/painting/qpaintengine_raster_p.h +++ b/src/gui/painting/qpaintengine_raster_p.h @@ -257,10 +257,8 @@ private: void fillRect(const QRectF &rect, QSpanData *data); void drawBitmap(const QPointF &pos, const QImage &image, QSpanData *fill); - void drawCachedGlyphs(const QVarLengthArray &glyphs, - const QVarLengthArray &positions, - QFontEngine *fontEngine, - const QTransform &matrix); + void drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions, + QFontEngine *fontEngine); void drawStaticTextItem(const QPointF &p, QStaticTextItem *textItem); diff --git a/src/gui/painting/qpaintengineex.cpp b/src/gui/painting/qpaintengineex.cpp index 15085f9..f167b348 100644 --- a/src/gui/painting/qpaintengineex.cpp +++ b/src/gui/painting/qpaintengineex.cpp @@ -592,7 +592,7 @@ void QPaintEngineEx::stroke(const QVectorPath &path, const QPen &pen) void QPaintEngineEx::drawStaticTextItem(const QPointF &position, QStaticTextItem *item) { - // ### fall back (we need to recalculate the text item and call drawTextItem()) + // ### Make this pure virtual after implementing in all subclasses } void QPaintEngineEx::draw(const QVectorPath &path) diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 1d7f57d..4eace16 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -5730,11 +5730,12 @@ void QPainter::drawStaticText(const QPointF &position, const QStaticText &static if (!d->engine || staticText.isEmpty() || pen().style() == Qt::NoPen) return; - const QStaticTextPrivate *staticText_d = QStaticTextPrivate::get(&staticText); + QStaticTextPrivate *staticText_d = + const_cast(QStaticTextPrivate::get(&staticText)); // If we don't have an extended paint engine, or if the painter is transformed, // we go through standard code path - if (d->extended == 0 || !d->state->matrix.isIdentity()) { + if (d->extended == 0) { if (staticText_d->size.isValid()) drawText(QRectF(position, staticText_d->size), staticText_d->text); else @@ -5742,14 +5743,33 @@ void QPainter::drawStaticText(const QPointF &position, const QStaticText &static return; } + // Don't recalculate entire layout because of translation, rather add the dx and dy + // into the position to move each text item the correct distance. + QPointF translatedPosition = position; + QTransform matrix = d->state->transform(); + if (matrix.isTranslating()) { + translatedPosition.rx() += matrix.dx(); + translatedPosition.ry() += matrix.dy(); + translate(-matrix.dx(), -matrix.dy()); + } + + // If the transform is not identical to the text transform, + // we have to relayout the text (for other transformations than plain translation) + if (staticText_d->matrix != d->state->transform()) { + staticText_d->matrix = d->state->transform(); + staticText_d->init(); + } + bool restoreWhenFinished = false; if (staticText_d->size.isValid()) { + save(); setClipRect(QRectF(position, staticText_d->size)); - save(); restoreWhenFinished = true; } + // ### Should we pick up the painter's font and recalculate the layout, like we do + // with the matrix? if (font() != staticText_d->font) { setFont(staticText_d->font); @@ -5759,11 +5779,14 @@ void QPainter::drawStaticText(const QPointF &position, const QStaticText &static for (int i=0; iitemCount; ++i) { QStaticTextItem *item = staticText_d->items + i; - d->extended->drawStaticTextItem(position, item); + d->extended->drawStaticTextItem(translatedPosition, item); } if (restoreWhenFinished) restore(); + + if (matrix.isTranslating()) + setTransform(matrix); } /*! diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index 46da88e..5a6a0d9 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -55,9 +55,8 @@ QT_BEGIN_NAMESPACE // #define CACHE_DEBUG -void QTextureGlyphCache::populate(QFontEngine *fontEngine, - const QVarLengthArray &glyphs, - const QVarLengthArray &) +void QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs, + const QFixedPoint *) { #ifdef CACHE_DEBUG printf("Populating with '%s'\n", QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data()); @@ -71,7 +70,7 @@ void QTextureGlyphCache::populate(QFontEngine *fontEngine, int rowHeight = 0; // check each glyph for its metrics and get the required rowHeight. - for (int i=0; i < glyphs.size(); ++i) { + for (int i=0; i < numGlyphs; ++i) { const glyph_t glyph = glyphs[i]; if (coords.contains(glyph)) continue; diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h index 2f7fc96..b8717b1 100644 --- a/src/gui/painting/qtextureglyphcache_p.h +++ b/src/gui/painting/qtextureglyphcache_p.h @@ -91,9 +91,8 @@ public: int baseLineY; }; - void populate(QFontEngine *fontEngine, - const QVarLengthArray &glyphs, - const QVarLengthArray &positions); + void populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs, + const QFixedPoint *positions); virtual void createTextureData(int width, int height) = 0; virtual void resizeTextureData(int width, int height) = 0; diff --git a/src/gui/text/qstatictext.cpp b/src/gui/text/qstatictext.cpp index c43eeb0..e6f2cc6 100644 --- a/src/gui/text/qstatictext.cpp +++ b/src/gui/text/qstatictext.cpp @@ -262,7 +262,7 @@ bool QStaticText::isEmpty() const } QStaticTextPrivate::QStaticTextPrivate() - : items(0), itemCount(0) + : items(0), itemCount(0), glyphPool(0), positionPool(0) { ref = 1; } @@ -279,6 +279,8 @@ QStaticTextPrivate::QStaticTextPrivate(const QStaticTextPrivate &other) QStaticTextPrivate::~QStaticTextPrivate() { delete[] items; + delete[] glyphPool; + delete[] positionPool; } QStaticTextPrivate *QStaticTextPrivate::get(const QStaticText *q) @@ -295,37 +297,58 @@ namespace { class DrawTextItemRecorder: public QPaintEngine { public: - DrawTextItemRecorder(int expectedItemCount, QStaticTextItem *items) + DrawTextItemRecorder(int expectedItemCount, QStaticTextItem *items, + int expectedGlyphCount, QFixedPoint *positionPool, glyph_t *glyphPool) : m_items(items), m_expectedItemCount(expectedItemCount), - m_itemCount(0) + m_expectedGlyphCount(expectedGlyphCount), + m_itemCount(0), m_glyphCount(0), + m_positionPool(positionPool), + m_glyphPool(glyphPool) { } virtual void drawTextItem(const QPointF &p, const QTextItem &textItem) { const QTextItemInt &ti = static_cast(textItem); - - Q_ASSERT(m_expectedItemCount < 0 || m_itemCount < m_expectedItemCount); - m_itemCount++; + m_itemCount++; + m_glyphCount += ti.glyphs.numGlyphs; if (m_items == 0) return; + Q_ASSERT(m_itemCount <= m_expectedItemCount); + Q_ASSERT(m_glyphCount <= m_expectedGlyphCount); + QStaticTextItem *currentItem = (m_items + (m_itemCount - 1)); currentItem->fontEngine = ti.fontEngine; currentItem->chars = ti.chars; currentItem->numChars = ti.num_chars; - ti.fontEngine->getGlyphPositions(ti.glyphs, QTransform(), ti.flags, currentItem->glyphs, - currentItem->glyphPositions); + currentItem->numGlyphs = ti.glyphs.numGlyphs; + currentItem->glyphs = m_glyphPool; + currentItem->glyphPositions = m_positionPool; + + QVarLengthArray glyphs; + QVarLengthArray positions; + ti.fontEngine->getGlyphPositions(ti.glyphs, state->transform(), ti.flags, + glyphs, positions); + + int size = glyphs.size(); + Q_ASSERT(size == ti.glyphs.numGlyphs); + Q_ASSERT(size == positions.size()); + + memmove(currentItem->glyphs, glyphs.constData(), sizeof(glyph_t) * size); + memmove(currentItem->glyphPositions, positions.constData(), sizeof(QFixedPoint) * size); QFixed fx = QFixed::fromReal(p.x()); QFixed fy = QFixed::fromReal(p.y()); - - for (int i=0; iglyphPositions.size(); ++i) { + for (int i=0; iglyphPositions[i].x += fx; currentItem->glyphPositions[i].y += fy; } + + m_glyphPool += size; + m_positionPool += size; } @@ -343,18 +366,31 @@ namespace { return m_itemCount; } + int glyphCount() const + { + return m_glyphCount; + } + private: QStaticTextItem *m_items; int m_itemCount; + int m_glyphCount; int m_expectedItemCount; + int m_expectedGlyphCount; + + glyph_t *m_glyphPool; + QFixedPoint *m_positionPool; }; class DrawTextItemDevice: public QPaintDevice { public: - DrawTextItemDevice(int expectedItemCount = -1, QStaticTextItem *items = 0) + DrawTextItemDevice(int expectedItemCount = -1, QStaticTextItem *items = 0, + int expectedGlyphCount = -1, QFixedPoint *positionPool = 0, + glyph_t *glyphPool = 0) { - m_paintEngine = new DrawTextItemRecorder(expectedItemCount, items); + m_paintEngine = new DrawTextItemRecorder(expectedItemCount, items, + expectedGlyphCount, positionPool, glyphPool); } ~DrawTextItemDevice() @@ -403,7 +439,10 @@ namespace { return m_paintEngine->itemCount(); } - + int glyphCount() const + { + return m_paintEngine->glyphCount(); + } private: DrawTextItemRecorder *m_paintEngine; @@ -414,6 +453,8 @@ namespace { void QStaticTextPrivate::init() { delete[] items; + delete[] glyphPool; + delete[] positionPool; // Draw once to count number of items and glyphs, so that we can use as little memory // as possible to store the data @@ -421,6 +462,7 @@ void QStaticTextPrivate::init() { QPainter painter(&counterDevice); painter.setFont(font); + painter.setTransform(matrix); if (size.isValid()) painter.drawText(QRectF(QPointF(0, 0), size), text); @@ -428,14 +470,19 @@ void QStaticTextPrivate::init() painter.drawText(0, 0, text); } - itemCount = counterDevice.itemCount(); + itemCount = counterDevice.itemCount(); items = new QStaticTextItem[itemCount]; + int glyphCount = counterDevice.glyphCount(); + glyphPool = new glyph_t[glyphCount]; + positionPool = new QFixedPoint[glyphCount]; + // Draw again to actually record the items and glyphs - DrawTextItemDevice recorderDevice(itemCount, items); + DrawTextItemDevice recorderDevice(itemCount, items, glyphCount, positionPool, glyphPool); { QPainter painter(&recorderDevice); painter.setFont(font); + painter.setTransform(matrix); if (size.isValid()) painter.drawText(QRectF(QPointF(0, 0), size), text); diff --git a/src/gui/text/qstatictext_p.h b/src/gui/text/qstatictext_p.h index 2a8d23a..e1ae80d 100644 --- a/src/gui/text/qstatictext_p.h +++ b/src/gui/text/qstatictext_p.h @@ -63,16 +63,18 @@ class QStaticTextItem public: QStaticTextItem() : chars(0), numChars(0), fontEngine(0) {} - QVarLengthArray glyphPositions; // 8 bytes per glyph - QVarLengthArray glyphs; // 4 bytes per glyph + // ### Use constant length arrays here to minimize memory consumption + QFixedPoint *glyphPositions; // 8 bytes per glyph + glyph_t *glyphs; // 4 bytes per glyph const QChar *chars; // 2 bytes per glyph // ================= // 14 bytes per glyph + int numGlyphs; // 4 bytes per item int numChars; // 4 bytes per item QFontEngine *fontEngine; // 4 bytes per item // ================ - // 8 bytes per item + // 12 bytes per item }; class QStaticText; @@ -85,16 +87,19 @@ public: void init(); - QAtomicInt ref; // 4 bytes per text + QAtomicInt ref; // 4 bytes per text - QString text; // 4 bytes per text - QFont font; // 8 bytes per text - QSizeF size; // 16 bytes per text + QString text; // 4 bytes per text + QFont font; // 8 bytes per text + QSizeF size; // 16 bytes per text - QStaticTextItem *items; // 4 bytes per text - int itemCount; // 4 bytes per text - // ================ - // 40 bytes per text + QTransform matrix; // 80 bytes per text + QStaticTextItem *items; // 4 bytes per text + int itemCount; // 4 bytes per text + glyph_t *glyphPool; // 4 bytes per text + QFixedPoint *positionPool; // 4 bytes per text + // ================ + // 128 bytes per text static QStaticTextPrivate *get(const QStaticText *q); }; diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 02a5246..e4fe84f 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1272,7 +1272,7 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly } cache->setPaintEnginePrivate(this); - cache->populate(ti.fontEngine, glyphs, positions); + cache->populate(ti.fontEngine, glyphs.size(), glyphs.constData(), positions.constData()); if (cache->width() == 0 || cache->height() == 0) return; -- cgit v0.12