diff options
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.cpp | 156 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem_p.h | 22 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene.cpp | 212 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene.h | 4 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene_p.h | 30 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsview.cpp | 194 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsview.h | 1 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsview_p.h | 9 | ||||
-rw-r--r-- | tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp | 5 |
9 files changed, 253 insertions, 380 deletions
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index e09d3f0..ec6b35b 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -863,8 +863,11 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, bool de parent->d_ptr->addChild(q); parent->itemChange(QGraphicsItem::ItemChildAddedChange, thisPointerVariant); - if (!implicitUpdate) - updateHelper(QRectF(), false, true); + if (!implicitUpdate && scene) { + scene->d_func()->markDirty(q_ptr, QRect(), + /*invalidateChildren=*/false, + /*maybeDirtyClipPath=*/true); + } // Inherit ancestor flags from the new parent. updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); @@ -895,7 +898,11 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, bool de setEnabledHelper(true, /* explicit = */ false); // If the item is being deleted, the whole scene will be updated. - updateHelper(QRectF(), false, true); + if (scene) { + scene->d_func()->markDirty(q_ptr, QRect(), + /*invalidateChildren=*/false, + /*maybeDirtyClipPath=*/true); + } } } @@ -1358,7 +1365,7 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags) const quint32 geomChangeFlagsMask = (ItemClipsChildrenToShape | ItemClipsToShape | ItemIgnoresTransformations); bool fullUpdate = (quint32(flags) & geomChangeFlagsMask) != (d_ptr->flags & geomChangeFlagsMask); if (fullUpdate) - d_ptr->fullUpdateHelper(false, true); + d_ptr->paintedViewBoundingRectsNeedRepaint = 1; // Keep the old flags to compare the diff. GraphicsItemFlags oldFlags = this->flags(); @@ -1398,8 +1405,11 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags) d_ptr->updateAncestorFlag(ItemIgnoresTransformations); } - // ### Why updateHelper? - d_ptr->updateHelper(QRectF(), false, true); + if (d_ptr->scene) { + d_ptr->scene->d_func()->markDirty(this, QRectF(), + /*invalidateChildren=*/true, + /*maybeDirtyClipPath*/true); + } // Notify change. itemChange(ItemFlagsHaveChanged, quint32(flags)); @@ -1665,7 +1675,12 @@ void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bo QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); if (c) c->purge(); - updateHelper(QRectF(), /* force = */ true); + if (scene) { + scene->d_func()->markDirty(q_ptr, QRectF(), + /*invalidateChildren=*/false, + /*maybeDirtyClipPath=*/false, + /*force=*/true); + } } // Certain properties are dropped as an item becomes invisible. @@ -1828,8 +1843,8 @@ void QGraphicsItemPrivate::setEnabledHelper(bool newEnabled, bool explicitly, bo enabled = newEnabledVariant.toBool(); // Schedule redraw. - if (update) - updateHelper(); + if (update && scene) + scene->d_func()->markDirty(q_ptr); foreach (QGraphicsItem *child, children) { if (!newEnabled || !child->d_ptr->explicitlyDisabled) @@ -1930,9 +1945,8 @@ void QGraphicsItem::setSelected(bool selected) return; d_ptr->selected = newSelected; - d_ptr->updateHelper(); - if (d_ptr->scene) { + d_ptr->scene->d_func()->markDirty(this); QGraphicsScenePrivate *sceneD = d_ptr->scene->d_func(); if (selected) { sceneD->selectedItems << this; @@ -2051,7 +2065,12 @@ void QGraphicsItem::setOpacity(qreal opacity) itemChange(ItemOpacityHasChanged, newOpacity); // Update. - d_ptr->fullUpdateHelper(/*childrenOnly=*/false, /*maybeDirtyClipPath=*/false, /*ignoreOpacity=*/true); + if (d_ptr->scene) + d_ptr->scene->d_func()->markDirty(this, QRectF(), + /*invalidateChildren=*/true, + /*maybeDirtyClipPath=*/false, + /*force=*/false, + /*ignoreOpacity=*/true); } /*! @@ -2501,10 +2520,8 @@ void QGraphicsItemPrivate::setPosHelper(const QPointF &pos) // Update and repositition. inSetPosHelper = 1; updateCachedClipPathFromSetPosHelper(newPos); - if (scene) { - fullUpdateHelper(true); + if (scene) q->prepareGeometryChange(); - } this->pos = newPos; invalidateSceneTransformCache(); @@ -3305,7 +3322,6 @@ void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) return; // Update and set the new transformation. - d_ptr->fullUpdateHelper(true, true); prepareGeometryChange(); d_ptr->hasTransform = !newTransform.isIdentity(); d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform); @@ -3358,7 +3374,6 @@ void QGraphicsItem::setTransform(const QTransform &matrix, bool combine) return; // Update and set the new transformation. - d_ptr->fullUpdateHelper(true, true); prepareGeometryChange(); d_ptr->hasTransform = !newTransform.isIdentity(); d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform); @@ -3545,9 +3560,9 @@ void QGraphicsItem::setZValue(qreal z) if (newZ == d_ptr->z) return; d_ptr->z = newZ; - d_ptr->fullUpdateHelper(); if (d_ptr->scene) { + d_ptr->scene->d_func()->markDirty(this, QRectF(), /*invalidateChildren=*/true); // Invalidate any sort caching; arrival of a new item means we need to // resort. d_ptr->scene->d_func()->invalidateSortCache(); @@ -4208,84 +4223,13 @@ bool QGraphicsItemPrivate::discardUpdateRequest(bool ignoreClipping, bool ignore // No scene, or if the scene is updating everything, means we have nothing // to do. The only exception is if the scene tracks the growing scene rect. return (!visible && !ignoreVisibleBit) - || (dirty && !ignoreDirtyBit) + || (!ignoreDirtyBit && (dirty || hasDirtyAncestor())) || !scene || (scene->d_func()->updateAll && scene->d_func()->hasSceneRect) || (!ignoreClipping && (childrenClippedToShape() && isClippedAway())) || (!ignoreOpacity && childrenCombineOpacity() && isFullyTransparent()); } -/*! - \internal - - Asks the scene to mark this item's scene rect as dirty, requesting a - redraw. This does not invalidate any cache. - - The \a force argument is for the update call in setVisible(), which is the - only case where the item's background should be marked as dirty even when - the item isn't visible. -*/ -void QGraphicsItemPrivate::updateHelper(const QRectF &rect, bool force, bool maybeDirtyClipPath) -{ - // No scene, or if the scene is updating everything, means we have nothing - // to do. The only exception is if the scene tracks the growing scene rect. - if (discardUpdateRequest(/*ignoreClipping=*/maybeDirtyClipPath, /*ignoreVisibleBit=*/force)) - return; - - if (rect.isNull()) - dirty = 1; - scene->itemUpdated(q_ptr, rect); -} - -/*! - \internal - - Propagates updates to \a item and all its children. -*/ -void QGraphicsItemPrivate::fullUpdateHelper(bool childrenOnly, bool maybeDirtyClipPath, bool ignoreOpacity) -{ - if (discardUpdateRequest(/*ignoreClipping=*/maybeDirtyClipPath, /*ignoreVisibleBit=*/false, - /*ignoreDirtyBit=*/true, ignoreOpacity)) { - return; - } - - if (!childrenOnly && !dirty) { - // Effectively the same as updateHelper(QRectF(), false, maybeDirtyClipPath). - dirty = 1; - scene->itemUpdated(q_ptr, QRectF()); - } - - if (dirtyChildren || childrenClippedToShape()) { - // Unnecessary to update children as well. - return; - } - - if (ancestorFlags & AncestorClipsChildren) { - Q_Q(QGraphicsItem); - // Check if we can avoid updating all children. - QGraphicsItem *p = parent; - QRectF br = q->boundingRect(); - while (p) { - if (p->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) { - bool ok; - QTransform x = q->itemTransform(p, &ok); - if (!ok) - break; - if (x.mapRect(br).contains(p->boundingRect())) - return; - } - p = p->d_ptr->parent; - if (!p || !(p->d_ptr->ancestorFlags & AncestorClipsChildren)) - break; - // ### check one level only - break; - } - } - foreach (QGraphicsItem *child, children) - child->d_ptr->fullUpdateHelper(false, maybeDirtyClipPath); - dirtyChildren = 1; -} - static inline bool allChildrenCombineOpacityHelper(QGraphicsItem *parent) { Q_ASSERT(parent); @@ -4590,14 +4534,10 @@ void QGraphicsItem::update(const QRectF &rect) // Only invalidate cache; item is already dirty. if (d_ptr->dirty) return; - } else if (d_ptr->discardUpdateRequest()) { - return; } - // Effectively the same as updateHelper(rect); - if (rect.isNull()) - d_ptr->dirty = 1; - d_ptr->scene->itemUpdated(this, rect); + if (d_ptr->scene) + d_ptr->scene->d_func()->markDirty(this); } /*! @@ -4695,7 +4635,7 @@ void QGraphicsItem::scroll(qreal dx, qreal dy, const QRectF &rect) exposed -= r.translated(dx, dy); foreach (QRect rect, exposed.rects()) update(rect); - d_ptr->updateHelper(); + d->scene->d_func()->markDirty(this); } else { update(rect); } @@ -5911,7 +5851,8 @@ void QGraphicsItem::focusOutEvent(QFocusEvent *event) void QGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); - d_ptr->updateHelper(); + if (d_ptr->scene) + d_ptr->scene->d_func()->markDirty(this); } /*! @@ -5939,7 +5880,8 @@ void QGraphicsItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) void QGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); - d_ptr->updateHelper(); + if (d_ptr->scene) + d_ptr->scene->d_func()->markDirty(this); } /*! @@ -6367,9 +6309,10 @@ void QGraphicsItem::addToIndex() // ### add to child index only if applicable return; } - if (d_ptr->scene) + if (d_ptr->scene) { d_ptr->scene->d_func()->addToIndex(this); - d_ptr->updateHelper(); + d_ptr->scene->d_func()->markDirty(this); + } } /*! @@ -6385,9 +6328,10 @@ void QGraphicsItem::removeFromIndex() // ### remove from child index only if applicable return; } - d_ptr->updateHelper(); - if (d_ptr->scene) + if (d_ptr->scene) { + d_ptr->scene->d_func()->markDirty(this); d_ptr->scene->d_func()->removeFromIndex(this); + } } /*! @@ -6406,7 +6350,10 @@ void QGraphicsItem::removeFromIndex() void QGraphicsItem::prepareGeometryChange() { if (d_ptr->scene) { - d_ptr->updateHelper(QRectF(), false, /*maybeDirtyClipPath=*/!d_ptr->inSetPosHelper); + d_ptr->paintedViewBoundingRectsNeedRepaint = 1; + d_ptr->scene->d_func()->markDirty(this, QRectF(), + /*invalidateChildren=*/true, + /*maybeDirtyClipPath=*/!d_ptr->inSetPosHelper); QGraphicsScenePrivate *scenePrivate = d_ptr->scene->d_func(); scenePrivate->removeFromIndex(this); } @@ -8075,7 +8022,6 @@ void QGraphicsPixmapItem::setTransformationMode(Qt::TransformationMode mode) { Q_D(QGraphicsPixmapItem); if (mode != d->transformationMode) { - d_ptr->updateHelper(); d->transformationMode = mode; update(); } diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 200d177..1d4b37a 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -161,6 +161,8 @@ public: dirtyTransform(0), dirtyTransformComponents(0), dirtyChildrenBoundingRect(1), + inDirtyList(0), + paintedViewBoundingRectsNeedRepaint(0), globalStackingOrder(-1), sceneTransformIndex(-1), q_ptr(0) @@ -186,8 +188,6 @@ public: void setEnabledHelper(bool newEnabled, bool explicitly, bool update = true); bool discardUpdateRequest(bool ignoreClipping = false, bool ignoreVisibleBit = false, bool ignoreDirtyBit = false, bool ignoreOpacity = false) const; - void updateHelper(const QRectF &rect = QRectF(), bool force = false, bool maybeDirtyClipPath = false); - void fullUpdateHelper(bool childrenOnly = false, bool maybeDirtyClipPath = false, bool ignoreOpacity = false); void updateEffectiveOpacity(); void resolveEffectiveOpacity(qreal effectiveParentOpacity); void resolveDepth(int parentDepth); @@ -308,8 +308,22 @@ public: || (childrenCombineOpacity() && isFullyTransparent()); } + inline bool hasDirtyAncestor() const + { + QGraphicsItem *p = parent; + while (p) { + if (p->d_ptr->dirtyChildren || (p->d_ptr->dirty && p->d_ptr->childrenClippedToShape())) + return true; + p = p->d_ptr->parent; + } + return false; + } + + QPainterPath cachedClipPath; QRectF childrenBoundingRect; + QRectF needsRepaint; + QMap<QWidget *, QRect> paintedViewBoundingRects; QPointF pos; qreal z; QGraphicsScene *scene; @@ -353,7 +367,9 @@ public: quint32 dirtyTransform : 1; quint32 dirtyTransformComponents : 1; quint32 dirtyChildrenBoundingRect : 1; - quint32 padding : 17; // feel free to use + quint32 inDirtyList : 1; + quint32 paintedViewBoundingRectsNeedRepaint : 1; + quint32 padding : 15; // feel free to use // Optional stacking order int globalStackingOrder; diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 7eb49e9..d004ed2 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -334,7 +334,6 @@ QGraphicsScenePrivate::QGraphicsScenePrivate() updateAll(false), calledEmitUpdated(false), selectionChanging(0), - dirtyItemResetPending(false), regenerateIndex(true), purgePending(false), indexTimerId(0), @@ -678,31 +677,81 @@ void QGraphicsScenePrivate::_q_polishItems() unpolishedItems.clear(); } -/*! - \internal -*/ -void QGraphicsScenePrivate::_q_resetDirtyItems() +void QGraphicsScenePrivate::_q_processDirtyItems() { + Q_Q(QGraphicsScene); + if (dirtyItems.isEmpty()) + return; + + if (updateAll) { + for (int i = 0; i < dirtyItems.size(); ++i) + resetDirtyItem(dirtyItems.at(i)); + dirtyItems.clear(); + return; + } + + const bool useCompatUpdate = views.isEmpty() || (connectedSignals & changedSignalMask); for (int i = 0; i < dirtyItems.size(); ++i) { QGraphicsItem *item = dirtyItems.at(i); - item->d_ptr->dirty = 0; - item->d_ptr->dirtyChildren = 0; + if (useCompatUpdate && !item->d_ptr->itemIsUntransformable() + && qFuzzyIsNull(item->boundingRegionGranularity())) { + // 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->sceneBoundingRect()); + resetDirtyItem(item); + continue; + } + + QRectF dirtyRect; + bool uninitializedDirtyRect = true; + + for (int j = 0; j < views.size(); ++j) { + QGraphicsView *view = views.at(j); + QGraphicsViewPrivate *viewPrivate = view->d_func(); + if (viewPrivate->fullUpdatePending) + continue; + switch (viewPrivate->viewportUpdateMode) { + case QGraphicsView::NoViewportUpdate: + continue; + case QGraphicsView::FullViewportUpdate: + view->viewport()->update(); + viewPrivate->fullUpdatePending = 1; + continue; + default: + break; + } + + if (uninitializedDirtyRect) { + dirtyRect = adjustedItemBoundingRect(item); + if (!item->d_ptr->dirty) { + _q_adjustRect(&item->d_ptr->needsRepaint); + dirtyRect &= item->d_ptr->needsRepaint; + } + + if (item->d_ptr->dirtyChildren && !item->d_ptr->children.isEmpty() + && !item->d_ptr->childrenClippedToShape()) { + QRectF childrenBounds = item->childrenBoundingRect(); + _q_adjustRect(&childrenBounds); + dirtyRect |= childrenBounds; + } + uninitializedDirtyRect = false; + } + + if (item->d_ptr->paintedViewBoundingRectsNeedRepaint) + viewPrivate->updateRect(item->d_ptr->paintedViewBoundingRects.value(viewPrivate->viewport)); + + if (!item->d_ptr->hasBoundingRegionGranularity) + viewPrivate->updateRect(viewPrivate->mapToViewRect(item, dirtyRect)); + else + viewPrivate->updateRegion(viewPrivate->mapToViewRegion(item, dirtyRect)); + } + resetDirtyItem(item); } - dirtyItems.clear(); - dirtyItemResetPending = false; -} -/*! - \internal -*/ -void QGraphicsScenePrivate::resetDirtyItemsLater() -{ - Q_Q(QGraphicsScene); - if (dirtyItemResetPending) - return; - // dirtyItems.reserve(indexedItems.size() + unindexedItems.size()); - dirtyItemResetPending = true; - QMetaObject::invokeMethod(q, "_q_resetDirtyItems", Qt::QueuedConnection); + dirtyItems.clear(); + for (int i = 0; i < views.size(); ++i) + views.at(i)->d_func()->processPendingUpdates(); } /*! @@ -761,10 +810,9 @@ void QGraphicsScenePrivate::_q_removeItemLater(QGraphicsItem *item) // Update selected & hovered item bookkeeping selectedItems.remove(item); hoverItems.removeAll(item); - pendingUpdateItems.removeAll(item); cachedItemsUnderMouse.removeAll(item); unpolishedItems.removeAll(item); - dirtyItems.removeAll(item); + removeFromDirtyItems(item); //We remove all references of item from the sceneEventFilter arrays QMultiMap<QGraphicsItem*, QGraphicsItem*>::iterator iterator = sceneEventFilters.begin(); @@ -3319,7 +3367,7 @@ void QGraphicsScene::removeItem(QGraphicsItem *item) d->pendingUpdateItems.removeAll(item); d->cachedItemsUnderMouse.removeAll(item); d->unpolishedItems.removeAll(item); - d->dirtyItems.removeAll(item); + d->removeFromDirtyItems(item); //We remove all references of item from the sceneEventFilter arrays QMultiMap<QGraphicsItem*, QGraphicsItem*>::iterator iterator = d->sceneEventFilters.begin(); @@ -3330,11 +3378,6 @@ void QGraphicsScene::removeItem(QGraphicsItem *item) ++iterator; } - - //Ensure dirty flag have the correct default value so the next time it will be added it will receive updates - item->d_func()->dirty = 0; - item->d_func()->dirtyChildren = 0; - // Remove all children recursively foreach (QGraphicsItem *child, item->children()) removeItem(child); @@ -5050,7 +5093,9 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * } QRectF brect = item->boundingRect(); _q_adjustRect(&brect); - viewBoundingRect = transform.mapRect(brect).toRect().adjusted(-1, -1, 1, 1) & exposedRegion.boundingRect(); + const QRect paintedViewBoundingRect = transform.mapRect(brect).toRect().adjusted(-1, -1, 1, 1); + item->d_ptr->paintedViewBoundingRects.insert(widget, paintedViewBoundingRect); + viewBoundingRect = paintedViewBoundingRect & exposedRegion.boundingRect(); } else { transform = parentTransform; } @@ -5117,6 +5162,44 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * painter->restore(); } +void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, bool invalidateChildren, + bool maybeDirtyClipPath, bool force, bool ignoreOpacity) +{ + Q_ASSERT(item); + if (updateAll) + return; + + if (item->d_ptr->discardUpdateRequest(/*ignoreClipping=*/maybeDirtyClipPath, + /*ignoreVisibleBit=*/force, + /*ignoreDirtyBit=*/false, ignoreOpacity)) { + return; + } + + const bool fullItemUpdate = rect.isNull(); + if (!fullItemUpdate && rect.isEmpty()) + return; + + if (dirtyItems.isEmpty()) + QMetaObject::invokeMethod(q_ptr, "_q_processDirtyItems", Qt::QueuedConnection); + + if (item->d_ptr->inDirtyList) { + if (fullItemUpdate) + item->d_ptr->dirty = 1; + else if (!item->d_ptr->dirty) + item->d_ptr->needsRepaint |= rect; + } else { + dirtyItems.append(item); + item->d_ptr->inDirtyList = 1; + if (fullItemUpdate) + item->d_ptr->dirty = 1; + else if (!item->d_ptr->dirty) + item->d_ptr->needsRepaint = rect; + } + + if (invalidateChildren) + item->d_ptr->dirtyChildren = 1; +} + /*! Paints the given \a items using the provided \a painter, after the background has been drawn, and before the foreground has been @@ -5238,6 +5321,8 @@ void QGraphicsScene::drawItems(QPainter *painter, // Draw the item d->drawItemHelper(item, painter, &options[i], widget, d->painterStateProtection); + const QRect paintedViewBoundingRect = painter->worldTransform().mapRect(options[i].rect).adjusted(-1, -1, 1, 1); + item->d_ptr->paintedViewBoundingRects.insert(widget, paintedViewBoundingRect); if (saveState) painter->restore(); @@ -5364,73 +5449,6 @@ bool QGraphicsScene::focusNextPrevChild(bool next) */ /*! - \internal - - This private function is called by QGraphicsItem, which is a friend of - QGraphicsScene. It is used by QGraphicsScene to record the rectangles that - need updating. It also launches a single-shot timer to ensure that - updated() will be emitted later. - - The \a item parameter is the item that changed, and \a rect is the - area of the item that changed given in item coordinates. -*/ -void QGraphicsScene::itemUpdated(QGraphicsItem *item, const QRectF &rect) -{ - Q_D(QGraphicsScene); - // Deliver the actual update. - if (!d->updateAll) { - if (d->views.isEmpty() || ((d->connectedSignals & d->changedSignalMask) && !item->d_ptr->itemIsUntransformable() - && qFuzzyIsNull(item->boundingRegionGranularity()))) { - // 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. - update(item->sceneBoundingRect()); - } else { - // ### Remove _q_adjustedRects(). - QRectF boundingRect(adjustedItemBoundingRect(item)); - if (!rect.isNull()) { - QRectF adjustedRect(rect); - _q_adjustRect(&adjustedRect); - boundingRect &= adjustedRect; - } - - // Update each view directly. - for (int i = 0; i < d->views.size(); ++i) - d->views.at(i)->d_func()->itemUpdated(item, boundingRect); - } - } - if (item->d_ptr->dirty) { - d->dirtyItems << item; - d->resetDirtyItemsLater(); - } - - if (!item->isVisible()) - return; // Hiding an item won't effect the largestUntransformableItem/sceneRect. - - // Update d->largestUntransformableItem by mapping this item's bounding - // rect back to the topmost untransformable item's untransformed - // coordinate system (which sort of equals the 1:1 coordinate system of an - // untransformed view). - if (item->d_ptr->itemIsUntransformable()) { - QGraphicsItem *parent = item; - while (parent && (parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorIgnoresTransformations)) - parent = parent->parentItem(); - d->largestUntransformableItem |= item->mapToItem(parent, item->boundingRect()).boundingRect(); - } - - // Only track the automatically growing scene rect if the scene has no - // defined scene rect. - if (!d->hasSceneRect) { - QRectF oldGrowingItemsBoundingRect = d->growingItemsBoundingRect; - QRectF adjustedItemSceneBoundingRect(item->sceneBoundingRect()); - _q_adjustRect(&adjustedItemSceneBoundingRect); - d->growingItemsBoundingRect |= adjustedItemSceneBoundingRect; - if (d->growingItemsBoundingRect != oldGrowingItemsBoundingRect) - emit sceneRectChanged(d->growingItemsBoundingRect); - } -} - -/*! \since 4.4 Returns the scene's style, or the same as QApplication::style() if the diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h index 9802f87..4c0f2ec 100644 --- a/src/gui/graphicsview/qgraphicsscene.h +++ b/src/gui/graphicsview/qgraphicsscene.h @@ -271,8 +271,6 @@ Q_SIGNALS: void selectionChanged(); private: - void itemUpdated(QGraphicsItem *item, const QRectF &rect); - Q_DECLARE_PRIVATE(QGraphicsScene) Q_DISABLE_COPY(QGraphicsScene) Q_PRIVATE_SLOT(d_func(), void _q_updateIndex()) @@ -281,7 +279,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_updateLater()) Q_PRIVATE_SLOT(d_func(), void _q_polishItems()) Q_PRIVATE_SLOT(d_func(), void _q_updateSortCache()) - Q_PRIVATE_SLOT(d_func(), void _q_resetDirtyItems()) + Q_PRIVATE_SLOT(d_func(), void _q_processDirtyItems()) friend class QGraphicsItem; friend class QGraphicsItemPrivate; friend class QGraphicsView; diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index bb99908..f2226bf 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -111,7 +111,7 @@ public: QSet<QGraphicsItem *> selectedItems; QList<QGraphicsItem *> unindexedItems; QList<QGraphicsItem *> indexedItems; - QList<QGraphicsItem *> dirtyItems; + QVector<QGraphicsItem *> dirtyItems; QList<QGraphicsItem *> pendingUpdateItems; QList<QGraphicsItem *> unpolishedItems; QList<QGraphicsItem *> topLevelItems; @@ -121,9 +121,7 @@ public: void _q_updateLater(); void _q_polishItems(); - void _q_resetDirtyItems(); - void resetDirtyItemsLater(); - bool dirtyItemResetPending; + void _q_processDirtyItems(); QList<int> freeItemIndexes; bool regenerateIndex; @@ -257,6 +255,30 @@ public: void drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter, const QTransform &parentTransform, const QTransform &viewTransform, const QRegion &exposedRegion, QWidget *widget, QGraphicsView::OptimizationFlags optimizationFlags); + void markDirty(QGraphicsItem *item, const QRectF &rect = QRectF(), bool invalidateChildren = false, + bool maybeDirtyClipPath = false, bool force = false, bool ignoreOpacity = false); + + inline void resetDirtyItem(QGraphicsItem *item) + { + Q_ASSERT(item); + item->d_ptr->dirty = 0; + item->d_ptr->paintedViewBoundingRectsNeedRepaint = 0; + item->d_ptr->dirtyChildren = 0; + item->d_ptr->inDirtyList = 0; + item->d_ptr->needsRepaint = QRectF(); + } + + inline void removeFromDirtyItems(QGraphicsItem *item) + { + int i = 0; + while (i < dirtyItems.size()) { + if (dirtyItems.at(i) == item) + dirtyItems.remove(i); + else + ++i; + } + resetDirtyItem(item); + } QStyle *style; QFont font; diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index 1994675..acf26e1 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -329,8 +329,6 @@ QGraphicsViewPrivate::QGraphicsViewPrivate() #endif lastDragDropEvent(0), fullUpdatePending(true), - dirtyRectCount(0), - updatingLater(false), updateSceneSlotReimplementedChecked(false) { styleOptions.reserve(QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS); @@ -804,125 +802,28 @@ static inline QRectF adjustedItemBoundingRect(const QGraphicsItem *item) return boundingRect; } -/*! - \internal -*/ -void QGraphicsViewPrivate::itemUpdated(QGraphicsItem *item, const QRectF &rect) +void QGraphicsViewPrivate::processPendingUpdates() { - if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate) - return; - if (item->d_ptr->dirty) - updateLater(); - - QRectF updateRect = rect; - if ((item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) || item->d_ptr->children.isEmpty()) { - updateRect &= adjustedItemBoundingRect(item); - if (updateRect.isEmpty()) - return; - } - - QGraphicsItem *clipItem = item; - if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) { - // Minimize unnecessary redraw. - QGraphicsItem *parent = item; - while ((parent = parent->d_ptr->parent)) { - if (parent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) { - // Map update rect to the current parent and itersect with its bounding rect. - updateRect = clipItem->itemTransform(parent).mapRect(updateRect) - & adjustedItemBoundingRect(parent); - if (updateRect.isEmpty()) - return; - clipItem = parent; - } - - if (!(parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) - break; - } - } - - // Map update rect from clipItem coordinates to view coordinates. - Q_ASSERT(clipItem); - if (!item->d_ptr->hasBoundingRegionGranularity) - this->updateRect(mapToViewRect(clipItem, updateRect) & viewport->rect()); - else - updateRegion(mapToViewRegion(clipItem, updateRect) & viewport->rect()); -} - -void QGraphicsViewPrivate::updateLater() -{ - Q_Q(QGraphicsView); - if (updatingLater) - return; - updatingLater = true; - QMetaObject::invokeMethod(q, "_q_updateLaterSlot", Qt::QueuedConnection); -} - -void QGraphicsViewPrivate::_q_updateLaterSlot() -{ - Q_Q(QGraphicsView); if (!scene) return; - QRect vr = viewport->rect(); - QTransform viewTransform = q->viewportTransform(); - const QList<QGraphicsItem *> &dirtyItems = scene->d_func()->dirtyItems; - for (int i = 0; i < dirtyItems.size(); ++i) { - const QGraphicsItem *item = dirtyItems.at(i); - if (item->d_ptr->discardUpdateRequest(/*ignoreClipping=*/false, - /*ignoreVisibleBit=*/false, - /*ignoreDirtyBit=*/true)) { - continue; - } - QTransform x = item->sceneTransform() * viewTransform; - updateRect(x.mapRect(item->boundingRect()).toAlignedRect() & vr); + if (fullUpdatePending) { // We have already called viewport->update() + dirtyBoundingRect = QRect(); + dirtyRegion = QRegion(); + return; } - dirtyRectCount += dirtyRects.size(); - - bool noUpdate = !fullUpdatePending && viewportUpdateMode == QGraphicsView::FullViewportUpdate; - if ((dirtyRectCount > 0 || !dirtyBoundingRect.isEmpty()) && !fullUpdatePending && !noUpdate) { - if (viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate - || (viewportUpdateMode == QGraphicsView::SmartViewportUpdate - && dirtyRectCount >= QGRAPHICSVIEW_REGION_RECT_THRESHOLD)) { - if (!(optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)) { - viewport->update(dirtyBoundingRect.adjusted(-2, -2, 2, 2)); - } else { - viewport->update(dirtyBoundingRect); - } - } else { - // ### Improve this block, which is very slow for complex regions. We - // need to strike the balance between having an accurate update - // region, and running fast. The below approach is the simplest way to - // create a region from a bunch of rects, but we might want to use - // other approaches; e.g., a grid of a fixed size representing - // quadrants of the viewport, which we mark as dirty depending on the - // rectangles in the list. Perhaps this should go into a - // QRegion::fromRects(rects, how) function. - QRegion region; - if (!(optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)) { - for (int i = 0; i < dirtyRegions.size(); ++i) { - QVector<QRect> rects = dirtyRegions.at(i).rects(); - for (int j = 0; j < rects.size(); ++j) - region += rects.at(j).adjusted(-2, -2, 2, 2); - } - for (int i = 0; i < dirtyRects.size(); ++i) - region += dirtyRects.at(i).adjusted(-2, -2, 2, 2); - } else { - for (int i = 0; i < dirtyRegions.size(); ++i) - region += dirtyRegions.at(i); - for (int i = 0; i < dirtyRects.size(); ++i) - region += dirtyRects.at(i); - } - - viewport->update(region); - } + if (viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate) { + if (optimizationFlags & QGraphicsView::DontAdjustForAntialiasing) + viewport->update(dirtyBoundingRect); + else + viewport->update(dirtyBoundingRect.adjusted(-2, -2, 2, 2)); + } else { + viewport->update(dirtyRegion); // Already adjusted in updateRect/Region. } - dirtyRegions.clear(); - dirtyRects.clear(); - dirtyRectCount = 0; dirtyBoundingRect = QRect(); - updatingLater = false; + dirtyRegion = QRegion(); } void QGraphicsViewPrivate::updateAll() @@ -930,14 +831,13 @@ void QGraphicsViewPrivate::updateAll() Q_Q(QGraphicsView); q->viewport()->update(); fullUpdatePending = true; - dirtyRectCount = 0; dirtyBoundingRect = QRect(); - updatingLater = false; + dirtyRegion = QRegion(); } void QGraphicsViewPrivate::updateRegion(const QRegion &r) { - if (r.isEmpty()) + if (r.isEmpty() || fullUpdatePending) return; Q_Q(QGraphicsView); @@ -950,45 +850,30 @@ void QGraphicsViewPrivate::updateRegion(const QRegion &r) break; case QGraphicsView::BoundingRectViewportUpdate: dirtyBoundingRect |= r.boundingRect(); - if (dirtyBoundingRect == q->viewport()->rect()) { + if (dirtyBoundingRect.contains(q->viewport()->rect())) { fullUpdatePending = true; q->viewport()->update(); - } else { - updateLater(); } break; - case QGraphicsView::SmartViewportUpdate: - dirtyBoundingRect |= r.boundingRect(); - if ((dirtyRectCount + r.numRects()) < QGRAPHICSVIEW_REGION_RECT_THRESHOLD) - dirtyRegions << r; - dirtyRectCount += r.numRects(); - updateLater(); - break; + case QGraphicsView::SmartViewportUpdate: // ### DEPRECATE case QGraphicsView::MinimalViewportUpdate: - dirtyRegions << r; - dirtyRectCount += r.numRects(); - updateLater(); + if (optimizationFlags & QGraphicsView::DontAdjustForAntialiasing) { + dirtyRegion += r; + } else { + const QVector<QRect> &rects = r.rects(); + for (int i = 0; i < rects.size(); ++i) + dirtyRegion += rects.at(i).adjusted(-2, -2, 2, 2); + } break; case QGraphicsView::NoViewportUpdate: // Unreachable break; } - - // Compress the regions... - if (dirtyRectCount > QGRAPHICSVIEW_REGION_RECT_THRESHOLD && dirtyRegions.size() > 1) { - QRegion masterRegion; - for (int i=0; i<dirtyRegions.size(); ++i) { - masterRegion |= dirtyRegions.at(i); - } - dirtyRectCount = masterRegion.numRects(); - dirtyRegions.clear(); - dirtyRegions << masterRegion; - } } void QGraphicsViewPrivate::updateRect(const QRect &r) { - if (r.isEmpty()) + if (r.isEmpty() || fullUpdatePending) return; Q_Q(QGraphicsView); @@ -1001,22 +886,17 @@ void QGraphicsViewPrivate::updateRect(const QRect &r) break; case QGraphicsView::BoundingRectViewportUpdate: dirtyBoundingRect |= r; - if (dirtyBoundingRect == q->viewport()->rect()) { + if (dirtyBoundingRect.contains(q->viewport()->rect())) { fullUpdatePending = true; q->viewport()->update(); - } else { - updateLater(); } break; - case QGraphicsView::SmartViewportUpdate: - dirtyBoundingRect |= r; - if ((dirtyRectCount + dirtyRects.size()) < QGRAPHICSVIEW_REGION_RECT_THRESHOLD) - dirtyRects << r; - updateLater(); - break; + case QGraphicsView::SmartViewportUpdate: // ### DEPRECATE case QGraphicsView::MinimalViewportUpdate: - dirtyRects << r; - updateLater(); + if (optimizationFlags & QGraphicsView::DontAdjustForAntialiasing) + dirtyRegion += r; + else + dirtyRegion += r.adjusted(-2, -2, 2, 2); break; case QGraphicsView::NoViewportUpdate: // Unreachable @@ -2613,9 +2493,10 @@ void QGraphicsView::updateScene(const QList<QRectF> &rects) // Extract and reset dirty scene rect info. QVector<QRect> dirtyViewportRects; - for (int i = 0; i < d->dirtyRegions.size(); ++i) - dirtyViewportRects += d->dirtyRegions.at(i).rects(); - d->dirtyRegions.clear(); + const QVector<QRect> &dirtyRects = d->dirtyRegion.rects(); + for (int i = 0; i < dirtyRects.size(); ++i) + dirtyViewportRects += dirtyRects.at(i); + d->dirtyRegion = QRegion(); bool fullUpdate = !d->accelerateScrolling || d->viewportUpdateMode == QGraphicsView::FullViewportUpdate; bool boundingRectUpdate = (d->viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate) @@ -3544,10 +3425,7 @@ void QGraphicsView::scrollContentsBy(int dx, int dy) if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate) { if (d->viewportUpdateMode != QGraphicsView::FullViewportUpdate) { - for (int i = 0; i < d->dirtyRects.size(); ++i) - d->dirtyRects[i].translate(dx, dy); - for (int i = 0; i < d->dirtyRegions.size(); ++i) - d->dirtyRegions[i].translate(dx, dy); + d->dirtyRegion.translate(dx, dy); if (d->accelerateScrolling) { #ifndef QT_NO_RUBBERBAND // Update new and old rubberband regions diff --git a/src/gui/graphicsview/qgraphicsview.h b/src/gui/graphicsview/qgraphicsview.h index 7692e16..deed1d0 100644 --- a/src/gui/graphicsview/qgraphicsview.h +++ b/src/gui/graphicsview/qgraphicsview.h @@ -274,7 +274,6 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_setViewportCursor(const QCursor &)) Q_PRIVATE_SLOT(d_func(), void _q_unsetViewportCursor()) #endif - Q_PRIVATE_SLOT(d_func(), void _q_updateLaterSlot()) friend class QGraphicsSceneWidget; friend class QGraphicsScene; friend class QGraphicsScenePrivate; diff --git a/src/gui/graphicsview/qgraphicsview_p.h b/src/gui/graphicsview/qgraphicsview_p.h index c18f85d..637687b 100644 --- a/src/gui/graphicsview/qgraphicsview_p.h +++ b/src/gui/graphicsview/qgraphicsview_p.h @@ -159,15 +159,10 @@ public: QRect mapToViewRect(const QGraphicsItem *item, const QRectF &rect) const; QRegion mapToViewRegion(const QGraphicsItem *item, const QRectF &rect) const; - void itemUpdated(QGraphicsItem *item, const QRectF &rect); bool fullUpdatePending; - QList<QRect> dirtyRects; - QList<QRegion> dirtyRegions; - int dirtyRectCount; + QRegion dirtyRegion; QRect dirtyBoundingRect; - void updateLater(); - bool updatingLater; - void _q_updateLaterSlot(); + void processPendingUpdates(); void updateAll(); void updateRect(const QRect &rect); void updateRegion(const QRegion ®ion); diff --git a/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp b/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp index 0c5ebf6..23d7a94 100644 --- a/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp +++ b/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp @@ -1317,8 +1317,9 @@ void tst_QGraphicsScene::removeItem() scene.removeItem(hoverItem); hoverItem->setAcceptsHoverEvents(false); scene.addItem(hoverItem); - qApp->processEvents(); // update - qApp->processEvents(); // draw + qApp->processEvents(); // <- delayed update is called + qApp->processEvents(); // <- scene schedules pending update + qApp->processEvents(); // <- pending update is sent to view QVERIFY(!hoverItem->isHovered); } |