From 1336f8268512a4ad29732acf75f2e6c4d41683ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Nilsen?= Date: Tue, 7 Jul 2009 10:31:52 +0200 Subject: Reduce QTransform operations in Graphics View. Use the item's cached scene transform directly instead of always making a copy of it. Moreover, we don't have to use the transform at all if the scene transform is a "translate only" transform :-). QTransform already use the same cut-offs, but it must update the type() before it can find out. We don't have to check the type() because that is already stored (and we usually don't call type() when we store the value either). All auto-tests pass. --- src/gui/graphicsview/qgraphicsitem.cpp | 140 +++++++++++++++++++++++++------- src/gui/graphicsview/qgraphicsitem_p.h | 16 +++- src/gui/graphicsview/qgraphicsscene.cpp | 86 +++++++++++++------- src/gui/graphicsview/qgraphicsview.cpp | 7 +- 4 files changed, 188 insertions(+), 61 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 9a27ef5..85d76d4 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -821,6 +821,42 @@ void QGraphicsItemPrivate::combineTransformFromParent(QTransform *x, const QTran } } +void QGraphicsItemPrivate::updateSceneTransformFromParent() +{ + if (parent) { + Q_ASSERT(!parent->d_ptr->dirtySceneTransform); + if (parent->d_ptr->sceneTransformTranslateOnly) { + sceneTransform = QTransform::fromTranslate(parent->d_ptr->sceneTransform.dx() + pos.x(), + parent->d_ptr->sceneTransform.dy() + pos.y()); + } else { + sceneTransform = parent->d_ptr->sceneTransform; + sceneTransform.translate(pos.x(), pos.y()); + } + if (transformData) { + sceneTransform = transformData->computedFullTransform(&sceneTransform); + sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate); + } else { + sceneTransformTranslateOnly = parent->d_ptr->sceneTransformTranslateOnly; + } + } else if (!transformData) { + sceneTransform = QTransform::fromTranslate(pos.x(), pos.y()); + sceneTransformTranslateOnly = 1; + } else if (transformData->onlyTransform) { + sceneTransform = transformData->transform; + if (!pos.isNull()) + sceneTransform *= QTransform::fromTranslate(pos.x(), pos.y()); + sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate); + } else if (pos.isNull()) { + sceneTransform = transformData->computedFullTransform(); + sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate); + } else { + sceneTransform = QTransform::fromTranslate(pos.x(), pos.y()); + sceneTransform = transformData->computedFullTransform(&sceneTransform); + sceneTransformTranslateOnly = (sceneTransform.type() <= QTransform::TxTranslate); + } + dirtySceneTransform = 0; +} + /*! \internal @@ -3145,7 +3181,8 @@ void QGraphicsItem::setTransformOrigin(const QPointF &origin) */ QMatrix QGraphicsItem::sceneMatrix() const { - return sceneTransform().toAffine(); + d_ptr->ensureSceneTransform(); + return d_ptr->sceneTransform.toAffine(); } @@ -3168,16 +3205,7 @@ QMatrix QGraphicsItem::sceneMatrix() const */ QTransform QGraphicsItem::sceneTransform() const { - if (d_ptr->dirtySceneTransform) { - // This item and all its descendants have dirty scene transforms. - // We're about to validate this item's scene transform, so we have to - // invalidate all the children; otherwise there's no way for the descendants - // to detect that the ancestor has changed. - d_ptr->invalidateChildrenSceneTransform(); - } - - QGraphicsItem *that = const_cast(this); - d_ptr->ensureSceneTransformRecursive(&that); + d_ptr->ensureSceneTransform(); return d_ptr->sceneTransform; } @@ -3207,8 +3235,10 @@ QTransform QGraphicsItem::sceneTransform() const QTransform QGraphicsItem::deviceTransform(const QTransform &viewportTransform) const { // Ensure we return the standard transform if we're not untransformable. - if (!d_ptr->itemIsUntransformable()) - return sceneTransform() * viewportTransform; + if (!d_ptr->itemIsUntransformable()) { + d_ptr->ensureSceneTransform(); + return d_ptr->sceneTransform * viewportTransform; + } // Find the topmost item that ignores view transformations. const QGraphicsItem *untransformedAncestor = this; @@ -3227,7 +3257,8 @@ QTransform QGraphicsItem::deviceTransform(const QTransform &viewportTransform) c } // First translate the base untransformable item. - QPointF mappedPoint = (untransformedAncestor->sceneTransform() * viewportTransform).map(QPointF(0, 0)); + untransformedAncestor->d_ptr->ensureSceneTransform(); + QPointF mappedPoint = (untransformedAncestor->d_ptr->sceneTransform * viewportTransform).map(QPointF(0, 0)); // COMBINE QTransform matrix = QTransform::fromTranslate(mappedPoint.x(), mappedPoint.y()); @@ -3320,8 +3351,11 @@ QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) co // Find the closest common ancestor. If the two items don't share an // ancestor, then the only way is to combine their scene transforms. const QGraphicsItem *commonAncestor = commonAncestorItem(other); - if (!commonAncestor) - return sceneTransform() * other->sceneTransform().inverted(ok); + if (!commonAncestor) { + d_ptr->ensureSceneTransform(); + other->d_ptr->ensureSceneTransform(); + return d_ptr->sceneTransform * other->d_ptr->sceneTransform.inverted(ok); + } // If the two items are cousins (in sibling branches), map both to the // common ancestor, and combine the two transforms. @@ -3716,7 +3750,13 @@ QRectF QGraphicsItem::sceneBoundingRect() const QRectF br = boundingRect(); br.translate(offset); - return !parentItem ? br : parentItem->sceneTransform().mapRect(br); + if (!parentItem) + return br; + if (parentItem->d_ptr->hasTranslateOnlySceneTransform()) { + br.translate(parentItem->d_ptr->sceneTransform.dx(), parentItem->d_ptr->sceneTransform.dy()); + return br; + } + return parentItem->d_ptr->sceneTransform.mapRect(br); } /*! @@ -4484,9 +4524,22 @@ void QGraphicsItemPrivate::ensureSceneTransformRecursive(QGraphicsItem **topMost } // COMBINE my transform with the parent's scene transform. - sceneTransform = parent ? parent->d_ptr->sceneTransform : QTransform(); - combineTransformFromParent(&sceneTransform); - dirtySceneTransform = 0; + updateSceneTransformFromParent(); + Q_ASSERT(!dirtySceneTransform); +} + +void QGraphicsItemPrivate::ensureSceneTransform() +{ + if (dirtySceneTransform) { + // This item and all its descendants have dirty scene transforms. + // We're about to validate this item's scene transform, so we have to + // invalidate all the children; otherwise there's no way for the descendants + // to detect that the ancestor has changed. + invalidateChildrenSceneTransform(); + } + + QGraphicsItem *that = q_func(); + ensureSceneTransformRecursive(&that); } /*! @@ -4827,7 +4880,9 @@ QPointF QGraphicsItem::mapToParent(const QPointF &point) const */ QPointF QGraphicsItem::mapToScene(const QPointF &point) const { - return sceneTransform().map(point); + if (d_ptr->hasTranslateOnlySceneTransform()) + return QPointF(point.x() + d_ptr->sceneTransform.dx(), point.y() + d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.map(point); } /*! @@ -4894,7 +4949,9 @@ QPolygonF QGraphicsItem::mapToParent(const QRectF &rect) const */ QPolygonF QGraphicsItem::mapToScene(const QRectF &rect) const { - return sceneTransform().map(rect); + if (d_ptr->hasTranslateOnlySceneTransform()) + return rect.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.map(rect); } /*! @@ -4967,7 +5024,9 @@ QRectF QGraphicsItem::mapRectToParent(const QRectF &rect) const */ QRectF QGraphicsItem::mapRectToScene(const QRectF &rect) const { - return sceneTransform().mapRect(rect); + if (d_ptr->hasTranslateOnlySceneTransform()) + return rect.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.mapRect(rect); } /*! @@ -5041,7 +5100,9 @@ QRectF QGraphicsItem::mapRectFromParent(const QRectF &rect) const */ QRectF QGraphicsItem::mapRectFromScene(const QRectF &rect) const { - return sceneTransform().inverted().mapRect(rect); + if (d_ptr->hasTranslateOnlySceneTransform()) + return rect.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().mapRect(rect); } /*! @@ -5093,7 +5154,9 @@ QPolygonF QGraphicsItem::mapToParent(const QPolygonF &polygon) const */ QPolygonF QGraphicsItem::mapToScene(const QPolygonF &polygon) const { - return sceneTransform().map(polygon); + if (d_ptr->hasTranslateOnlySceneTransform()) + return polygon.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.map(polygon); } /*! @@ -5138,7 +5201,9 @@ QPainterPath QGraphicsItem::mapToParent(const QPainterPath &path) const */ QPainterPath QGraphicsItem::mapToScene(const QPainterPath &path) const { - return sceneTransform().map(path); + if (d_ptr->hasTranslateOnlySceneTransform()) + return path.translated(d_ptr->sceneTransform.dx(), d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.map(path); } /*! @@ -5199,7 +5264,9 @@ QPointF QGraphicsItem::mapFromParent(const QPointF &point) const */ QPointF QGraphicsItem::mapFromScene(const QPointF &point) const { - return sceneTransform().inverted().map(point); + if (d_ptr->hasTranslateOnlySceneTransform()) + return QPointF(point.x() - d_ptr->sceneTransform.dx(), point.y() - d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().map(point); } /*! @@ -5267,7 +5334,9 @@ QPolygonF QGraphicsItem::mapFromParent(const QRectF &rect) const */ QPolygonF QGraphicsItem::mapFromScene(const QRectF &rect) const { - return sceneTransform().inverted().map(rect); + if (d_ptr->hasTranslateOnlySceneTransform()) + return rect.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().map(rect); } /*! @@ -5317,7 +5386,9 @@ QPolygonF QGraphicsItem::mapFromParent(const QPolygonF &polygon) const */ QPolygonF QGraphicsItem::mapFromScene(const QPolygonF &polygon) const { - return sceneTransform().inverted().map(polygon); + if (d_ptr->hasTranslateOnlySceneTransform()) + return polygon.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().map(polygon); } /*! @@ -5360,7 +5431,9 @@ QPainterPath QGraphicsItem::mapFromParent(const QPainterPath &path) const */ QPainterPath QGraphicsItem::mapFromScene(const QPainterPath &path) const { - return sceneTransform().inverted().map(path); + if (d_ptr->hasTranslateOnlySceneTransform()) + return path.translated(-d_ptr->sceneTransform.dx(), -d_ptr->sceneTransform.dy()); + return d_ptr->sceneTransform.inverted().map(path); } /*! @@ -6492,7 +6565,12 @@ void QGraphicsItem::prepareGeometryChange() // _q_processDirtyItems is called before _q_emitUpdated. if ((scenePrivate->connectedSignals & scenePrivate->changedSignalMask) || scenePrivate->views.isEmpty()) { - d_ptr->scene->update(sceneTransform().mapRect(boundingRect())); + if (d_ptr->hasTranslateOnlySceneTransform()) { + d_ptr->scene->update(boundingRect().translated(d_ptr->sceneTransform.dx(), + d_ptr->sceneTransform.dy())); + } else { + d_ptr->scene->update(d_ptr->sceneTransform.mapRect(boundingRect())); + } } } diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 99865b0..3ba77b7 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -164,6 +164,7 @@ public: ignoreOpacity(0), acceptTouchEvents(0), acceptedTouchBeginEvent(0), + sceneTransformTranslateOnly(0), globalStackingOrder(-1), q_ptr(0) { @@ -194,6 +195,7 @@ public: void combineTransformToParent(QTransform *x, const QTransform *viewTransform = 0) const; void combineTransformFromParent(QTransform *x, const QTransform *viewTransform = 0) const; + void updateSceneTransformFromParent(); // ### Qt 5: Remove. Workaround for reimplementation added after Qt 4.4. virtual QVariant inputMethodQueryHelper(Qt::InputMethodQuery query) const; @@ -303,6 +305,13 @@ public: void invalidateCachedClipPathRecursively(bool childrenOnly = false, const QRectF &emptyIfOutsideThisRect = QRectF()); void updateCachedClipPathFromSetPosHelper(const QPointF &newPos); void ensureSceneTransformRecursive(QGraphicsItem **topMostDirtyItem); + void ensureSceneTransform(); + + inline bool hasTranslateOnlySceneTransform() + { + ensureSceneTransform(); + return sceneTransformTranslateOnly; + } inline void invalidateChildrenSceneTransform() { @@ -469,7 +478,8 @@ public: quint32 ignoreOpacity : 1; quint32 acceptTouchEvents : 1; quint32 acceptedTouchBeginEvent : 1; - quint32 unused : 10; // feel free to use + quint32 sceneTransformTranslateOnly : 1; + quint32 unused : 9; // feel free to use // Optional stacking order int globalStackingOrder; @@ -500,6 +510,10 @@ struct QGraphicsItemPrivate::TransformData { if (onlyTransform) { if (!postmultiplyTransform) return transform; + if (postmultiplyTransform->isIdentity()) + return transform; + if (transform.isIdentity()) + return *postmultiplyTransform; QTransform x(transform); x *= *postmultiplyTransform; return x; diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index bae1afd..ae6c53c 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -4228,6 +4228,7 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * QTransform transform(Qt::Uninitialized); QTransform *transformPtr = 0; + bool translateOnlyTransform = false; #define ENSURE_TRANSFORM_PTR \ if (!transformPtr) { \ Q_ASSERT(!itemIsUntransformable); \ @@ -4237,6 +4238,7 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * transformPtr = &transform; \ } else { \ transformPtr = &item->d_ptr->sceneTransform; \ + translateOnlyTransform = item->d_ptr->sceneTransformTranslateOnly; \ } \ } @@ -4248,10 +4250,8 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * transform = item->deviceTransform(viewTransform ? *viewTransform : QTransform()); transformPtr = &transform; } else if (item->d_ptr->dirtySceneTransform) { - item->d_ptr->sceneTransform = item->d_ptr->parent ? item->d_ptr->parent->d_ptr->sceneTransform - : QTransform(); - item->d_ptr->combineTransformFromParent(&item->d_ptr->sceneTransform); - item->d_ptr->dirtySceneTransform = 0; + item->d_ptr->updateSceneTransformFromParent(); + Q_ASSERT(!item->d_ptr->dirtySceneTransform); wasDirtyParentSceneTransform = true; } @@ -4260,7 +4260,8 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * if (drawItem) { const QRectF brect = adjustedItemBoundingRect(item); ENSURE_TRANSFORM_PTR - QRect viewBoundingRect = transformPtr->mapRect(brect).toRect(); + QRect viewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()).toRect() + : transformPtr->mapRect(brect).toRect(); item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect); viewBoundingRect.adjust(-1, -1, 1, 1); drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect) : !viewBoundingRect.isEmpty(); @@ -4416,13 +4417,45 @@ void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, b } static inline bool updateHelper(QGraphicsViewPrivate *view, QGraphicsItemPrivate *item, - const QRectF &rect, const QTransform &xform) + const QRectF &rect, bool itemIsUntransformable) { Q_ASSERT(view); Q_ASSERT(item); - if (item->hasBoundingRegionGranularity) + + QGraphicsItem *itemq = static_cast(item->q_ptr); + QGraphicsView *viewq = static_cast(view->q_ptr); + + if (itemIsUntransformable) { + const QTransform xform = itemq->deviceTransform(viewq->viewportTransform()); + if (!item->hasBoundingRegionGranularity) + return view->updateRect(xform.mapRect(rect).toRect()); return view->updateRegion(xform.map(QRegion(rect.toRect()))); - return view->updateRect(xform.mapRect(rect).toRect()); + } + + if (item->sceneTransformTranslateOnly && view->identityMatrix) { + const qreal dx = item->sceneTransform.dx(); + const qreal dy = item->sceneTransform.dy(); + if (!item->hasBoundingRegionGranularity) { + QRectF r(rect); + r.translate(dx - view->horizontalScroll(), dy - view->verticalScroll()); + return view->updateRect(r.toRect()); + } + QRegion r(rect.toRect()); + r.translate(qRound(dx) - view->horizontalScroll(), qRound(dy) - view->verticalScroll()); + return view->updateRegion(r); + } + + if (!viewq->isTransformed()) { + if (!item->hasBoundingRegionGranularity) + return view->updateRect(item->sceneTransform.mapRect(rect).toRect()); + return view->updateRegion(item->sceneTransform.map(QRegion(rect.toRect()))); + } + + QTransform xform = item->sceneTransform; + xform *= viewq->viewportTransform(); + if (!item->hasBoundingRegionGranularity) + return view->updateRect(xform.mapRect(rect).toRect()); + return view->updateRegion(xform.map(QRegion(rect.toRect()))); } void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool dirtyAncestorContainsChildren, @@ -4460,11 +4493,8 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool bool wasDirtyParentSceneTransform = item->d_ptr->dirtySceneTransform; const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable(); if (wasDirtyParentSceneTransform && !itemIsUntransformable) { - // Calculate the full scene transform for this item. - item->d_ptr->sceneTransform = item->d_ptr->parent ? item->d_ptr->parent->d_ptr->sceneTransform - : QTransform(); - item->d_ptr->combineTransformFromParent(&item->d_ptr->sceneTransform); - item->d_ptr->dirtySceneTransform = 0; + item->d_ptr->updateSceneTransformFromParent(); + Q_ASSERT(!item->d_ptr->dirtySceneTransform); } const bool wasDirtyParentViewBoundingRects = item->d_ptr->paintedViewBoundingRectsNeedRepaint; @@ -4479,8 +4509,14 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool if (item->d_ptr->geometryChanged) { // Update growingItemsBoundingRect. - if (!hasSceneRect) - growingItemsBoundingRect |= item->d_ptr->sceneTransform.mapRect(item->boundingRect()); + if (!hasSceneRect) { + if (item->d_ptr->sceneTransformTranslateOnly) { + growingItemsBoundingRect |= item->boundingRect().translated(item->d_ptr->sceneTransform.dx(), + item->d_ptr->sceneTransform.dy()); + } else { + growingItemsBoundingRect |= item->d_ptr->sceneTransform.mapRect(item->boundingRect()); + } + } item->d_ptr->geometryChanged = 0; } @@ -4493,7 +4529,12 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool // This block of code is kept for compatibility. Since 4.5, by default // QGraphicsView does not connect the signal and we use the below // method of delivering updates. - q->update(item->d_ptr->sceneTransform.mapRect(itemBoundingRect)); + if (item->d_ptr->sceneTransformTranslateOnly) { + q->update(itemBoundingRect.translated(item->d_ptr->sceneTransform.dx(), + item->d_ptr->sceneTransform.dy())); + } else { + q->update(item->d_ptr->sceneTransform.mapRect(itemBoundingRect)); + } } else { QRectF dirtyRect; bool uninitializedDirtyRect = true; @@ -4536,18 +4577,7 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool if (dirtyRect.isEmpty()) continue; // Discard updates outside the bounding rect. - bool valid = false; - if (itemIsUntransformable) { - valid = updateHelper(viewPrivate, item->d_ptr, dirtyRect, - item->deviceTransform(view->viewportTransform())); - } else if (!view->isTransformed()) { - valid = updateHelper(viewPrivate, item->d_ptr, dirtyRect, item->d_ptr->sceneTransform); - } else { - QTransform deviceTransform = item->d_ptr->sceneTransform; - deviceTransform *= view->viewportTransform(); - valid = updateHelper(viewPrivate, item->d_ptr, dirtyRect, deviceTransform); - } - if (!valid) + if (!updateHelper(viewPrivate, item->d_ptr, dirtyRect, itemIsUntransformable)) paintedViewBoundingRect = QRect(); } } diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 995f3f3..1e34320 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -1864,7 +1864,12 @@ void QGraphicsView::fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRati void QGraphicsView::fitInView(const QGraphicsItem *item, Qt::AspectRatioMode aspectRatioMode) { QPainterPath path = item->isClipped() ? item->clipPath() : item->shape(); - fitInView(item->sceneTransform().map(path).boundingRect(), aspectRatioMode); + if (item->d_ptr->hasTranslateOnlySceneTransform()) { + path.translate(item->d_ptr->sceneTransform.dx(), item->d_ptr->sceneTransform.dy()); + fitInView(path.boundingRect(), aspectRatioMode); + } else { + fitInView(item->d_ptr->sceneTransform.map(path).boundingRect(), aspectRatioMode); + } } /*! -- cgit v0.12