diff options
author | Aaron Kennedy <aaron.kennedy@nokia.com> | 2010-10-01 00:36:34 (GMT) |
---|---|---|
committer | Aaron Kennedy <aaron.kennedy@nokia.com> | 2010-10-01 00:39:22 (GMT) |
commit | 394651a222b9b200b793dbf86fadc52b5a546915 (patch) | |
tree | bb91a38e1a3785ab969276afafc1c83dd3004b2e /src/declarative/graphicsitems | |
parent | 1e2958e7a12de7cba30b02657c02320945bb1e3d (diff) | |
download | Qt-394651a222b9b200b793dbf86fadc52b5a546915.zip Qt-394651a222b9b200b793dbf86fadc52b5a546915.tar.gz Qt-394651a222b9b200b793dbf86fadc52b5a546915.tar.bz2 |
Apply the QStaticText text-caching strategy for QML
QML cannot use QStaticText directly as it doesn't support caching a
QTextLayout. QDeclarativeTextLayout is basically a copy of QStaticText
that adds this support.
Currently this feature is disabled by default (enable with
QML_DISABLE_IMAGE_CACHE=1) while we double check the perf consequences
of this change.
Task-number: QTBUG-14050
Diffstat (limited to 'src/declarative/graphicsitems')
5 files changed, 365 insertions, 28 deletions
diff --git a/src/declarative/graphicsitems/graphicsitems.pri b/src/declarative/graphicsitems/graphicsitems.pri index d420595..ffdeb29 100644 --- a/src/declarative/graphicsitems/graphicsitems.pri +++ b/src/declarative/graphicsitems/graphicsitems.pri @@ -49,7 +49,8 @@ HEADERS += \ $$PWD/qdeclarativelistview_p.h \ $$PWD/qdeclarativelayoutitem_p.h \ $$PWD/qdeclarativeitemchangelistener_p.h \ - $$PWD/qdeclarativegraphicswidget_p.h + $$PWD/qdeclarativegraphicswidget_p.h \ + $$PWD/qdeclarativetextlayout_p.h SOURCES += \ $$PWD/qdeclarativeitemsmodule.cpp \ @@ -81,4 +82,6 @@ SOURCES += \ $$PWD/qdeclarativevisualitemmodel.cpp \ $$PWD/qdeclarativelistview.cpp \ $$PWD/qdeclarativelayoutitem.cpp \ - $$PWD/qdeclarativegraphicswidget.cpp + $$PWD/qdeclarativegraphicswidget.cpp \ + $$PWD/qdeclarativetextlayout.cpp + diff --git a/src/declarative/graphicsitems/qdeclarativetext.cpp b/src/declarative/graphicsitems/qdeclarativetext.cpp index 1f4c1c7..c2dd850 100644 --- a/src/declarative/graphicsitems/qdeclarativetext.cpp +++ b/src/declarative/graphicsitems/qdeclarativetext.cpp @@ -82,6 +82,19 @@ private: static QSet<QUrl> errors; }; +DEFINE_BOOL_CONFIG_OPTION(disableImageCache, QML_DISABLE_IMAGE_CACHE); + +QDeclarativeTextPrivate::QDeclarativeTextPrivate() +: color((QRgb)0), style(QDeclarativeText::Normal), + hAlign(QDeclarativeText::AlignLeft), vAlign(QDeclarativeText::AlignTop), elideMode(QDeclarativeText::ElideNone), + imgDirty(true), dirty(true), richText(false), singleline(false), cache(true), internalWidthUpdate(false), doc(0), + format(QDeclarativeText::AutoText), wrapMode(QDeclarativeText::NoWrap) +{ + cache = !disableImageCache(); + QGraphicsItemPrivate::acceptedMouseButtons = Qt::LeftButton; + QGraphicsItemPrivate::flags = QGraphicsItemPrivate::flags & ~QGraphicsItem::ItemHasNoContents; +} + QTextDocumentWithImageResources::QTextDocumentWithImageResources(QDeclarativeText *parent) : QTextDocument(parent), outstanding(0) { @@ -145,7 +158,6 @@ void QTextDocumentWithImageResources::requestFinished() void QTextDocumentWithImageResources::setText(const QString &text) { if (!m_resources.isEmpty()) { - qWarning("CLEAR"); qDeleteAll(m_resources); m_resources.clear(); outstanding = 0; @@ -792,7 +804,7 @@ void QDeclarativeTextPrivate::updateSize() //setup instance of QTextLayout for all cases other than richtext if (!richText) { - size = setupTextLayout(&layout); + size = setupTextLayout(); if (cachedLayoutSize != size) { q->prepareGeometryChange(); cachedLayoutSize = size; @@ -907,10 +919,10 @@ void QDeclarativeTextPrivate::drawOutline(int yOffset) imgCache = img; } -QSize QDeclarativeTextPrivate::setupTextLayout(QTextLayout *layout) +QSize QDeclarativeTextPrivate::setupTextLayout() { Q_Q(QDeclarativeText); - layout->setCacheEnabled(true); + layout.setCacheEnabled(true); int height = 0; qreal widthUsed = 0; @@ -920,25 +932,25 @@ QSize QDeclarativeTextPrivate::setupTextLayout(QTextLayout *layout) if ((wrapMode != QDeclarativeText::NoWrap || elideMode != QDeclarativeText::ElideNone) && q->widthValid()) lineWidth = q->width(); - QTextOption textOption = layout->textOption(); + QTextOption textOption = layout.textOption(); textOption.setWrapMode(QTextOption::WrapMode(wrapMode)); - layout->setTextOption(textOption); + layout.setTextOption(textOption); - layout->beginLayout(); + layout.beginLayout(); while (1) { - QTextLine line = layout->createLine(); + QTextLine line = layout.createLine(); if (!line.isValid()) break; if ((wrapMode != QDeclarativeText::NoWrap || elideMode != QDeclarativeText::ElideNone) && q->widthValid()) line.setLineWidth(lineWidth); } - layout->endLayout(); + layout.endLayout(); int x = 0; - for (int i = 0; i < layout->lineCount(); ++i) { - QTextLine line = layout->lineAt(i); + for (int i = 0; i < layout.lineCount(); ++i) { + QTextLine line = layout.lineAt(i); widthUsed = qMax(widthUsed, line.naturalTextWidth()); line.setPosition(QPointF(0, height)); height += int(line.height()); @@ -1000,7 +1012,7 @@ void QDeclarativeTextPrivate::drawWrappedText(QPainter *p, const QPointF &pos, b else p->setPen(color); p->setFont(font); - layout.draw(p, pos); + layout.draw(p , pos); } QPixmap QDeclarativeTextPrivate::richTextImage(bool drawStyle) diff --git a/src/declarative/graphicsitems/qdeclarativetext_p_p.h b/src/declarative/graphicsitems/qdeclarativetext_p_p.h index 48552a7..ba1ec78 100644 --- a/src/declarative/graphicsitems/qdeclarativetext_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativetext_p_p.h @@ -55,6 +55,7 @@ #include "qdeclarativeitem.h" #include "private/qdeclarativeitem_p.h" +#include "private/qdeclarativetextlayout_p.h" #include <qdeclarative.h> @@ -65,22 +66,12 @@ QT_BEGIN_NAMESPACE class QTextLayout; class QTextDocumentWithImageResources; +#define QML_NO_TEXT_CACHE class QDeclarativeTextPrivate : public QDeclarativeItemPrivate { Q_DECLARE_PUBLIC(QDeclarativeText) public: - QDeclarativeTextPrivate() - : color((QRgb)0), style(QDeclarativeText::Normal), - hAlign(QDeclarativeText::AlignLeft), vAlign(QDeclarativeText::AlignTop), elideMode(QDeclarativeText::ElideNone), - imgDirty(true), dirty(true), richText(false), singleline(false), cache(true), internalWidthUpdate(false), doc(0), - format(QDeclarativeText::AutoText), wrapMode(QDeclarativeText::NoWrap) - { -#if defined(QML_NO_TEXT_CACHE) - cache = false; -#endif - QGraphicsItemPrivate::acceptedMouseButtons = Qt::LeftButton; - QGraphicsItemPrivate::flags = QGraphicsItemPrivate::flags & ~QGraphicsItem::ItemHasNoContents; - } + QDeclarativeTextPrivate(); ~QDeclarativeTextPrivate(); @@ -101,7 +92,7 @@ public: QPixmap wrappedTextImage(bool drawStyle); void drawWrappedText(QPainter *p, const QPointF &pos, bool drawStyle); QPixmap richTextImage(bool drawStyle); - QSize setupTextLayout(QTextLayout *layout); + QSize setupTextLayout(); QString text; QFont font; @@ -121,7 +112,7 @@ public: bool cache:1; bool internalWidthUpdate:1; QTextDocumentWithImageResources *doc; - QTextLayout layout; + QDeclarativeTextLayout layout; QSize cachedLayoutSize; QDeclarativeText::TextFormat format; QDeclarativeText::WrapMode wrapMode; diff --git a/src/declarative/graphicsitems/qdeclarativetextlayout.cpp b/src/declarative/graphicsitems/qdeclarativetextlayout.cpp new file mode 100644 index 0000000..5972f92 --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetextlayout.cpp @@ -0,0 +1,308 @@ +#include "qdeclarativetextlayout_p.h" +#include <private/qstatictext_p.h> +#include <private/qfontengine_p.h> +#include <private/qtextengine_p.h> +#include <private/qpainter_p.h> +#include <private/qpaintengineex_p.h> + +class QDeclarativeTextLayoutPrivate +{ +public: + QDeclarativeTextLayoutPrivate() + : cached(false) {} + + QPointF position; + + bool cached; + QVector<QStaticTextItem> items; + QVector<QFixedPoint> positions; + QVector<glyph_t> glyphs; + QVector<QChar> chars; +}; + +Q_GUI_EXPORT extern int qt_defaultDpiX(); +Q_GUI_EXPORT extern int qt_defaultDpiY(); + +namespace { +class DrawTextItemRecorder: public QPaintEngine +{ + public: + DrawTextItemRecorder(bool untransformedCoordinates, bool useBackendOptimizations) + : m_inertText(0), m_dirtyPen(false), m_useBackendOptimizations(useBackendOptimizations), + m_untransformedCoordinates(untransformedCoordinates) + { + } + + virtual void updateState(const QPaintEngineState &newState) + { + if (newState.state() & QPaintEngine::DirtyPen) + m_dirtyPen = true; + } + + virtual void drawTextItem(const QPointF &position, const QTextItem &textItem) + { + int glyphOffset = m_inertText->glyphs.size(); // Store offset into glyph pool + int positionOffset = m_inertText->glyphs.size(); // Offset into position pool + int charOffset = m_inertText->chars.size(); + + const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); + + bool needFreshCurrentItem = true; + if (!m_inertText->items.isEmpty()) { + QStaticTextItem &last = m_inertText->items[m_inertText->items.count() - 1]; + + if (last.fontEngine == ti.fontEngine && last.font == ti.font() && + (!m_dirtyPen || last.color == state->pen().color())) { + needFreshCurrentItem = false; + + last.numChars += ti.num_chars; + last.numGlyphs += ti.glyphs.numGlyphs; + + } + } + + if (needFreshCurrentItem) { + QStaticTextItem currentItem; + + currentItem.fontEngine = ti.fontEngine; + currentItem.font = ti.font(); + currentItem.charOffset = charOffset; + currentItem.numChars = ti.num_chars; + currentItem.numGlyphs = ti.glyphs.numGlyphs; + currentItem.glyphOffset = glyphOffset; + currentItem.positionOffset = positionOffset; + currentItem.useBackendOptimizations = m_useBackendOptimizations; + if (m_dirtyPen) + currentItem.color = state->pen().color(); + + m_inertText->items.append(currentItem); + } + + QTransform matrix = m_untransformedCoordinates ? QTransform() : state->transform(); + matrix.translate(position.x(), position.y()); + + QVarLengthArray<glyph_t> glyphs; + QVarLengthArray<QFixedPoint> positions; + ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); + + int size = glyphs.size(); + Q_ASSERT(size == ti.glyphs.numGlyphs); + Q_ASSERT(size == positions.size()); + + m_inertText->glyphs.resize(m_inertText->glyphs.size() + size); + m_inertText->positions.resize(m_inertText->glyphs.size()); + m_inertText->chars.resize(m_inertText->chars.size() + ti.num_chars); + + glyph_t *glyphsDestination = m_inertText->glyphs.data() + glyphOffset; + qMemCopy(glyphsDestination, glyphs.constData(), sizeof(glyph_t) * ti.glyphs.numGlyphs); + + QFixedPoint *positionsDestination = m_inertText->positions.data() + positionOffset; + qMemCopy(positionsDestination, positions.constData(), sizeof(QFixedPoint) * ti.glyphs.numGlyphs); + + QChar *charsDestination = m_inertText->chars.data() + charOffset; + qMemCopy(charsDestination, ti.chars, sizeof(QChar) * ti.num_chars); + + } + + virtual void drawPolygon(const QPointF *, int , PolygonDrawMode ) + { + /* intentionally empty */ + } + + virtual bool begin(QPaintDevice *) { return true; } + virtual bool end() { return true; } + virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {} + virtual Type type() const + { + return User; + } + + void begin(QDeclarativeTextLayoutPrivate *t) { + m_inertText = t; + m_dirtyPen = false; + } + + private: + QDeclarativeTextLayoutPrivate *m_inertText; + + bool m_dirtyPen; + bool m_useBackendOptimizations; + bool m_untransformedCoordinates; +}; + +class DrawTextItemDevice: public QPaintDevice +{ + public: + DrawTextItemDevice(bool untransformedCoordinates, bool useBackendOptimizations) + { + m_paintEngine = new DrawTextItemRecorder(untransformedCoordinates, + useBackendOptimizations); + } + + ~DrawTextItemDevice() + { + delete m_paintEngine; + } + + void begin(QDeclarativeTextLayoutPrivate *t) { + m_paintEngine->begin(t); + } + + int metric(PaintDeviceMetric m) const + { + int val; + switch (m) { + case PdmWidth: + case PdmHeight: + case PdmWidthMM: + case PdmHeightMM: + val = 0; + break; + case PdmDpiX: + case PdmPhysicalDpiX: + val = qt_defaultDpiX(); + break; + case PdmDpiY: + case PdmPhysicalDpiY: + val = qt_defaultDpiY(); + break; + case PdmNumColors: + val = 16777216; + break; + case PdmDepth: + val = 24; + break; + default: + val = 0; + qWarning("DrawTextItemDevice::metric: Invalid metric command"); + } + return val; + } + + virtual QPaintEngine *paintEngine() const + { + return m_paintEngine; + } + + private: + DrawTextItemRecorder *m_paintEngine; +}; + +struct InertTextPainter { + InertTextPainter() + : device(true, true), painter(&device) {} + + DrawTextItemDevice device; + QPainter painter; +}; +} + +Q_GLOBAL_STATIC(InertTextPainter, inertTextPainter); + +/*! +\class QDeclarativeTextLayout +\brief The QDeclarativeTextLayout class is a version of QStaticText that works with QTextLayouts. +\internal + +This class is basically a copy of the QStaticText code, but it is adapted to source its text from +QTextLayout. + +It is also considerably faster to create a QDeclarativeTextLayout than a QStaticText because it uses +a single, shared QPainter instance. QStaticText by comparison creates a new QPainter per instance. +As a consequence this means that QDeclarativeTextLayout is not re-enterant. Adding a lock around +the shared painter solves this, and only introduces a minor performance penalty, but is unnecessary +for QDeclarativeTextLayout's current use (QDeclarativeText is already tied to the GUI thread). +*/ + +QDeclarativeTextLayout::QDeclarativeTextLayout() +: d(0) +{ +} + +QDeclarativeTextLayout::QDeclarativeTextLayout(const QString &text) +: QTextLayout(text), d(0) +{ +} + +QDeclarativeTextLayout::~QDeclarativeTextLayout() +{ + if (d) delete d; +} + +void QDeclarativeTextLayout::beginLayout() +{ + if (d && d->cached) { + d->cached = false; + d->items.clear(); + d->positions.clear(); + d->glyphs.clear(); + d->chars.clear(); + d->position = QPointF(); + } + QTextLayout::beginLayout(); +} + +void QDeclarativeTextLayout::prepare() +{ + if (!d || !d->cached) { + + if (!d) + d = new QDeclarativeTextLayoutPrivate; + + InertTextPainter *itp = inertTextPainter(); + itp->device.begin(d); + QTextLayout::draw(&itp->painter, QPointF(0, 0)); + + glyph_t *glyphPool = d->glyphs.data(); + QFixedPoint *positionPool = d->positions.data(); + QChar *charPool = d->chars.data(); + + int itemCount = d->items.count(); + for (int ii = 0; ii < itemCount; ++ii) { + QStaticTextItem &item = d->items[ii]; + item.glyphs = glyphPool + item.glyphOffset; + item.glyphPositions = positionPool + item.positionOffset; + item.chars = charPool + item.charOffset; + } + + d->cached = true; + } +} + +void QDeclarativeTextLayout::draw(QPainter *painter, const QPointF &p) +{ + QPainterPrivate *priv = QPainterPrivate::get(painter); + + if (!priv->extended) { + QTextLayout::draw(painter, p); + return; + } + + prepare(); + + int itemCount = d->items.count(); + + if (p != d->position) { + QFixed fx = QFixed::fromReal(p.x()); + QFixed fy = QFixed::fromReal(p.y()); + QFixed oldX = QFixed::fromReal(d->position.x()); + QFixed oldY = QFixed::fromReal(d->position.y()); + for (int item = 0; item < itemCount; ++item) { + QStaticTextItem &textItem = d->items[item]; + + for (int ii = 0; ii < textItem.numGlyphs; ++ii) { + textItem.glyphPositions[ii].x += fx - oldX; + textItem.glyphPositions[ii].y += fy - oldY; + } + textItem.userDataNeedsUpdate = true; + } + + d->position = p; + } + + for (int ii = 0; ii < itemCount; ++ii) { + QStaticTextItem &item = d->items[ii]; + priv->extended->drawStaticTextItem(&item); + } +} + diff --git a/src/declarative/graphicsitems/qdeclarativetextlayout_p.h b/src/declarative/graphicsitems/qdeclarativetextlayout_p.h new file mode 100644 index 0000000..1a4473d --- /dev/null +++ b/src/declarative/graphicsitems/qdeclarativetextlayout_p.h @@ -0,0 +1,23 @@ +#ifndef QDECLARATIVETEXTLAYOUT_P_H +#define QDECLARATIVETEXTLAYOUT_P_H + +#include <QtGui/qtextlayout.h> + +class QDeclarativeTextLayoutPrivate; +class QDeclarativeTextLayout : public QTextLayout +{ +public: + QDeclarativeTextLayout(); + QDeclarativeTextLayout(const QString &); + ~QDeclarativeTextLayout(); + + void beginLayout(); + + void prepare(); + void draw(QPainter *, const QPointF & = QPointF()); + +private: + QDeclarativeTextLayoutPrivate *d; +}; + +#endif // QDECLARATIVETEXTLAYOUT_P_H |