summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/widgets/graphicsview/qgraphicsscene.cpp82
-rw-r--r--src/widgets/graphicsview/qgraphicsscene.h4
-rw-r--r--src/widgets/graphicsview/qgraphicsscene_p.h2
-rw-r--r--src/widgets/styles/qstyleoption.cpp2
-rw-r--r--tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp73
5 files changed, 154 insertions, 9 deletions
diff --git a/src/widgets/graphicsview/qgraphicsscene.cpp b/src/widgets/graphicsview/qgraphicsscene.cpp
index bccdb1fbaa..a7584ef198 100644
--- a/src/widgets/graphicsview/qgraphicsscene.cpp
+++ b/src/widgets/graphicsview/qgraphicsscene.cpp
@@ -299,6 +299,7 @@ QGraphicsScenePrivate::QGraphicsScenePrivate()
painterStateProtection(true),
sortCacheEnabled(false),
allItemsIgnoreTouchEvents(true),
+ minimumRenderSize(0.0),
selectionChanging(0),
rectAdjust(2),
focusItem(0),
@@ -4718,17 +4719,37 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *
const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape);
bool drawItem = itemHasContents && !itemIsFullyTransparent;
- if (drawItem) {
+ if (drawItem || minimumRenderSize > 0.0) {
const QRectF brect = adjustedItemEffectiveBoundingRect(item);
ENSURE_TRANSFORM_PTR
- QRect viewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()).toAlignedRect()
- : transformPtr->mapRect(brect).toAlignedRect();
- viewBoundingRect.adjust(-int(rectAdjust), -int(rectAdjust), rectAdjust, rectAdjust);
- if (widget)
- item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect);
- drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect)
- : !viewBoundingRect.normalized().isEmpty();
- if (!drawItem) {
+ QRectF preciseViewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy())
+ : transformPtr->mapRect(brect);
+
+ bool itemIsTooSmallToRender = false;
+ if (minimumRenderSize > 0.0
+ && (preciseViewBoundingRect.width() < minimumRenderSize
+ || preciseViewBoundingRect.height() < minimumRenderSize)) {
+ itemIsTooSmallToRender = true;
+ drawItem = false;
+ }
+
+ bool itemIsOutsideVisibleRect = false;
+ if (drawItem) {
+ QRect viewBoundingRect = preciseViewBoundingRect.toAlignedRect();
+ viewBoundingRect.adjust(-int(rectAdjust), -int(rectAdjust), rectAdjust, rectAdjust);
+ if (widget)
+ item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect);
+ drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect)
+ : !viewBoundingRect.normalized().isEmpty();
+ itemIsOutsideVisibleRect = !drawItem;
+ }
+
+ if (itemIsTooSmallToRender || itemIsOutsideVisibleRect) {
+ // We cannot simply use !drawItem here. If we did it is possible
+ // to enter the outter if statement with drawItem == false and minimumRenderSize > 0
+ // and finally end up inside this inner if, even though none of the above two
+ // conditions are met. In that case we should not return from this function
+ // but call draw() instead.
if (!itemHasChildren)
return;
if (itemClipsChildrenToShape) {
@@ -5729,6 +5750,49 @@ bool QGraphicsScene::sendEvent(QGraphicsItem *item, QEvent *event)
return d->sendEvent(item, event);
}
+/*!
+ \property QGraphicsScene::minimumRenderSize
+ \since 5.4
+ \brief the minimal view-transformed size an item must have to be drawn
+
+ When the scene is rendered, any item whose width or height, transformed
+ to the target view, is smaller that minimumRenderSize(), will not be
+ rendered. If an item is not rendered and it clips its children items
+ they will also not be rendered. Set this value to speed up rendering
+ of scenes with many objects rendered on a zoomed out view.
+
+ The default value is 0. If unset, or if set to 0 or a negative value,
+ all items will always be rendered.
+
+ For example, setting this property can be especially useful if a scene
+ is rendered by multiple views, one of which serves as an overview which
+ always displays all items. In scenes with many items, such a view will
+ use a high scaling factor so that all items can be shown. Due to the
+ scaling, smaller items will only make an insignificant contribution to
+ the final rendered scene. To avoid drawing these items and reduce the
+ time necessary to render the scene, you can call setMinimumRenderSize()
+ with a non-negative value.
+
+ \note Items that are not drawn as a result of being too small, are still
+ returned by methods such as items() and itemAt(), and participate in
+ collision detection and interactions. It is recommended that you set
+ minimumRenderSize() to a value less than or equal to 1 in order to
+ avoid large unrendered items that are interactive.
+
+ \sa QStyleOptionGraphicsItem::levelOfDetailFromTransform()
+*/
+qreal QGraphicsScene::minimumRenderSize() const
+{
+ Q_D(const QGraphicsScene);
+ return d->minimumRenderSize;
+}
+void QGraphicsScene::setMinimumRenderSize(qreal minSize)
+{
+ Q_D(QGraphicsScene);
+ d->minimumRenderSize = minSize;
+ update();
+}
+
void QGraphicsScenePrivate::addView(QGraphicsView *view)
{
views << view;
diff --git a/src/widgets/graphicsview/qgraphicsscene.h b/src/widgets/graphicsview/qgraphicsscene.h
index cde0eda125..ba47d45d63 100644
--- a/src/widgets/graphicsview/qgraphicsscene.h
+++ b/src/widgets/graphicsview/qgraphicsscene.h
@@ -107,6 +107,7 @@ class Q_WIDGETS_EXPORT QGraphicsScene : public QObject
Q_PROPERTY(QFont font READ font WRITE setFont)
Q_PROPERTY(bool sortCacheEnabled READ isSortCacheEnabled WRITE setSortCacheEnabled)
Q_PROPERTY(bool stickyFocus READ stickyFocus WRITE setStickyFocus)
+ Q_PROPERTY(qreal minimumRenderSize READ minimumRenderSize WRITE setMinimumRenderSize)
public:
enum ItemIndexMethod {
@@ -249,6 +250,9 @@ public:
bool sendEvent(QGraphicsItem *item, QEvent *event);
+ qreal minimumRenderSize() const;
+ void setMinimumRenderSize(qreal minSize);
+
public Q_SLOTS:
void update(const QRectF &rect = QRectF());
void invalidate(const QRectF &rect = QRectF(), SceneLayers layers = AllLayers);
diff --git a/src/widgets/graphicsview/qgraphicsscene_p.h b/src/widgets/graphicsview/qgraphicsscene_p.h
index 9e5bcec488..9f50dcfea7 100644
--- a/src/widgets/graphicsview/qgraphicsscene_p.h
+++ b/src/widgets/graphicsview/qgraphicsscene_p.h
@@ -117,6 +117,8 @@ public:
quint32 allItemsIgnoreTouchEvents : 1;
quint32 padding : 15;
+ qreal minimumRenderSize;
+
QRectF growingItemsBoundingRect;
void _q_emitUpdated();
diff --git a/src/widgets/styles/qstyleoption.cpp b/src/widgets/styles/qstyleoption.cpp
index 5913b2f261..f642a05d13 100644
--- a/src/widgets/styles/qstyleoption.cpp
+++ b/src/widgets/styles/qstyleoption.cpp
@@ -3702,6 +3702,8 @@ QStyleOptionGraphicsItem::QStyleOptionGraphicsItem(int version)
of the painter used to draw the item. By default, if no
transformations are applied, its value is 1. If zoomed out 1:2, the level
of detail will be 0.5, and if zoomed in 2:1, its value is 2.
+
+ \sa QGraphicsScene::minimumRenderSize()
*/
qreal QStyleOptionGraphicsItem::levelOfDetailFromTransform(const QTransform &worldTransform)
{
diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp
index dfc8465210..a4752126bc 100644
--- a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp
+++ b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp
@@ -267,6 +267,7 @@ private slots:
void removeFullyTransparentItem();
void zeroScale();
void focusItemChangedSignal();
+ void minimumRenderSize();
// task specific tests below me
void task139710_bspTreeCrash();
@@ -4678,6 +4679,78 @@ void tst_QGraphicsScene::focusItemChangedSignal()
}
+class ItemCountsPaintCalls : public QGraphicsRectItem
+{
+public:
+ ItemCountsPaintCalls(const QRectF & rect, QGraphicsItem *parent = 0)
+ : QGraphicsRectItem(rect, parent), repaints(0) {}
+ void paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 )
+ {
+ QGraphicsRectItem::paint(painter, option, widget);
+ ++repaints;
+ }
+ int repaints;
+};
+
+void tst_QGraphicsScene::minimumRenderSize()
+{
+ Q_CHECK_PAINTEVENTS
+
+ ItemCountsPaintCalls *bigParent = new ItemCountsPaintCalls(QRectF(0,0,100,100));
+ ItemCountsPaintCalls *smallChild = new ItemCountsPaintCalls(QRectF(0,0,10,10), bigParent);
+ ItemCountsPaintCalls *smallerGrandChild = new ItemCountsPaintCalls(QRectF(0,0,1,1), smallChild);
+ QGraphicsScene scene;
+ scene.addItem(bigParent);
+
+ CustomView view;
+ view.setScene(&scene);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+ qApp->processEvents();
+
+ // Initially, everything should be repainted the same number of times
+ int viewRepaints = 0;
+ QTRY_VERIFY(view.repaints > viewRepaints);
+ viewRepaints = view.repaints;
+
+ QVERIFY(viewRepaints == bigParent->repaints);
+ QVERIFY(viewRepaints == smallChild->repaints);
+ QVERIFY(viewRepaints == smallerGrandChild->repaints);
+
+ // Setting a minimum render size should cause a repaint
+ scene.setMinimumRenderSize(0.5);
+ qApp->processEvents();
+
+ QTRY_VERIFY(view.repaints > viewRepaints);
+ viewRepaints = view.repaints;
+
+ QVERIFY(viewRepaints == bigParent->repaints);
+ QVERIFY(viewRepaints == smallChild->repaints);
+ QVERIFY(viewRepaints == smallerGrandChild->repaints);
+
+ // Scaling should cause a repaint of big items only.
+ view.scale(0.1, 0.1);
+ qApp->processEvents();
+
+ QTRY_VERIFY(view.repaints > viewRepaints);
+ viewRepaints = view.repaints;
+
+ QVERIFY(viewRepaints == bigParent->repaints);
+ QVERIFY(viewRepaints == smallChild->repaints);
+ QVERIFY(smallChild->repaints > smallerGrandChild->repaints);
+
+ // Scaling further should cause even fewer items to be repainted
+ view.scale(0.1, 0.1); // Stacks with previous scale
+ qApp->processEvents();
+
+ QTRY_VERIFY(view.repaints > viewRepaints);
+ viewRepaints = view.repaints;
+
+ QVERIFY(viewRepaints == bigParent->repaints);
+ QVERIFY(bigParent->repaints > smallChild->repaints);
+ QVERIFY(smallChild->repaints > smallerGrandChild->repaints);
+}
+
void tst_QGraphicsScene::taskQTBUG_15977_renderWithDeviceCoordinateCache()
{
QGraphicsScene scene;