From 4df9c96e2c213c39924e22e02621b0c61e83f8fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Nilsen?= <bjorn.nilsen@nokia.com> Date: Tue, 20 Jul 2010 10:32:59 +0200 Subject: QGraphicsItem: Animation leaves drawing artifacts when clipping is used. This only happens when the ItemHasNoContents and ItemClipsChildrenToShape flags are set. Problem is that items with no content are threated as 'dummy' items, which means they are never drawn or 'processed' otherwise, so the cached bounding rect is not reliable/usable. This means that in case of changing the geometry of such items, its children always have to take care of invalidating the occupied areas and the update can not be clipped to the item's bounding rect. Regression after commit: c1c7dbf2 Auto test included. Task-number: QTBUG-11504 Reviewed-by: yoann --- src/gui/graphicsview/qgraphicsscene.cpp | 7 ++- tests/auto/qgraphicsview/tst_qgraphicsview.cpp | 73 ++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 4bc7f4c..48a0093 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -5178,7 +5178,12 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool // Process children. if (itemHasChildren && item->d_ptr->dirtyChildren) { const bool itemClipsChildrenToShape = item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape; - if (itemClipsChildrenToShape) { + // Items with no content are threated as 'dummy' items which means they are never drawn and + // 'processed', so the painted view bounding rect is never up-to-date. This means that whenever + // such an item changes geometry, its children have to take care of the update regardless + // of whether the item clips children to shape or not. + const bool bypassUpdateClip = !itemHasContents && wasDirtyParentViewBoundingRects; + if (itemClipsChildrenToShape && !bypassUpdateClip) { // Make sure child updates are clipped to the item's bounding rect. for (int i = 0; i < views.size(); ++i) views.at(i)->d_func()->setUpdateClip(item); diff --git a/tests/auto/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/qgraphicsview/tst_qgraphicsview.cpp index b8df7f6..1cce687 100644 --- a/tests/auto/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/qgraphicsview/tst_qgraphicsview.cpp @@ -219,6 +219,7 @@ private slots: void update2_data(); void update2(); void update_ancestorClipsChildrenToShape(); + void update_ancestorClipsChildrenToShape2(); void inputMethodSensitivity(); void inputContextReset(); void indirectPainting(); @@ -3815,6 +3816,78 @@ void tst_QGraphicsView::update_ancestorClipsChildrenToShape() #endif } +void tst_QGraphicsView::update_ancestorClipsChildrenToShape2() +{ + QGraphicsScene scene(-150, -150, 300, 300); + + /* + Add two rects: + + +------------------+ + | child | + | +--------------+ | + | | parent | | + | | | | + | | | | + | | | | + | +--------------+ | + +------------------+ + + ... where the parent has no contents and clips the child to shape. + */ + QApplication::processEvents(); // Get rid of pending update. + + QGraphicsRectItem *parent = static_cast<QGraphicsRectItem *>(scene.addRect(-50, -50, 100, 100)); + parent->setBrush(QColor(0, 0, 255, 125)); + parent->setFlag(QGraphicsItem::ItemClipsChildrenToShape); + parent->setFlag(QGraphicsItem::ItemHasNoContents); + + QGraphicsRectItem *child = static_cast<QGraphicsRectItem *>(scene.addRect(-100, -100, 200, 200)); + child->setBrush(QColor(255, 0, 0, 125)); + child->setParentItem(parent); + + CustomView view(&scene); + view.show(); + QTest::qWaitForWindowShown(&view); + QTRY_VERIFY(view.painted); + + view.lastUpdateRegions.clear(); + view.painted = false; + + // Call child->update() and make sure the updated area is within its parent's clip. + QRectF expected = child->deviceTransform(view.viewportTransform()).mapRect(child->boundingRect()); + expected &= parent->deviceTransform(view.viewportTransform()).mapRect(parent->boundingRect()); + + child->update(); + QTRY_VERIFY(view.painted); + +#ifndef QT_MAC_USE_COCOA //cocoa doesn't support drawing regions + QTRY_VERIFY(view.painted); + QCOMPARE(view.lastUpdateRegions.size(), 1); + QCOMPARE(view.lastUpdateRegions.at(0), QRegion(expected.toAlignedRect())); +#endif + + QTest::qWait(50); + + view.lastUpdateRegions.clear(); + view.painted = false; + + // Invalidate the parent's geometry and trigger an update. + // The update area should be clipped to the parent's bounding rect for 'normal' items, + // but in this case the item has no contents (ItemHasNoContents) and its geometry + // is invalidated, which means we cannot clip the child update. So, the expected + // area is exactly the same as the child's bounding rect (adjusted for antialiasing). + parent->setRect(parent->rect().adjusted(-10, -10, -10, -10)); + expected = child->deviceTransform(view.viewportTransform()).mapRect(child->boundingRect()); + expected.adjust(-2, -2, 2, 2); // Antialiasing + +#ifndef QT_MAC_USE_COCOA //cocoa doesn't support drawing regions + QTRY_VERIFY(view.painted); + QCOMPARE(view.lastUpdateRegions.size(), 1); + QCOMPARE(view.lastUpdateRegions.at(0), QRegion(expected.toAlignedRect())); +#endif +} + class FocusItem : public QGraphicsRectItem { public: -- cgit v0.12