diff options
author | Aaron McCarthy <aaron.mccarthy@nokia.com> | 2010-05-24 00:49:54 (GMT) |
---|---|---|
committer | Aaron McCarthy <aaron.mccarthy@nokia.com> | 2010-05-24 00:49:54 (GMT) |
commit | 7104b7598f6c8d61d008edab227cfce3ec4442fc (patch) | |
tree | e390a009bbc0826af59b105f7d5501a3f59028a3 /src/gui/graphicsview | |
parent | b827967fef469f66a5a2f975bfafc3cdb82c4ffb (diff) | |
parent | cb23151d3abfdbdf291abe8a1510592db2011927 (diff) | |
download | Qt-7104b7598f6c8d61d008edab227cfce3ec4442fc.zip Qt-7104b7598f6c8d61d008edab227cfce3ec4442fc.tar.gz Qt-7104b7598f6c8d61d008edab227cfce3ec4442fc.tar.bz2 |
Merge remote branch 'origin/4.7' into oslo-staging-1
Conflicts:
src/gui/styles/qmacstyle_mac.mm
Diffstat (limited to 'src/gui/graphicsview')
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.cpp | 208 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene.cpp | 20 |
2 files changed, 78 insertions, 150 deletions
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index dfd58b3..db6c4c5 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -5643,6 +5643,14 @@ void QGraphicsItem::update(const QRectF &rect) viewport, which does not benefit from scroll optimizations), this function is equivalent to calling update(\a rect). + \bold{Note:} Scrolling is only supported when QGraphicsItem::ItemCoordinateCache + is enabled; in all other cases calling this function is equivalent to calling + update(\a rect). If you for sure know that the item is opaque and not overlapped + by other items, you can map the \a rect to viewport coordinates and scroll the + viewport. + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 19 + \sa boundingRect() */ void QGraphicsItem::scroll(qreal dx, qreal dy, const QRectF &rect) @@ -5652,153 +5660,73 @@ void QGraphicsItem::scroll(qreal dx, qreal dy, const QRectF &rect) return; if (!d->scene) return; - if (d->cacheMode != NoCache) { - QGraphicsItemCache *c; - bool scrollCache = qFuzzyIsNull(dx - int(dx)) && qFuzzyIsNull(dy - int(dy)) - && (c = (QGraphicsItemCache *)qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraCacheData))) - && (d->cacheMode == ItemCoordinateCache && !c->fixedSize.isValid()); - if (scrollCache) { - QPixmap pix; - if (QPixmapCache::find(c->key, &pix)) { - // Adjust with 2 pixel margin. Notice the loss of precision - // when converting to QRect. - int adjust = 2; - QRectF scrollRect = !rect.isNull() ? rect : boundingRect(); - QRectF br = boundingRect().adjusted(-adjust, -adjust, adjust, adjust); - QRect irect = scrollRect.toRect().translated(-br.x(), -br.y()); - - pix.scroll(dx, dy, irect); - - QPixmapCache::replace(c->key, pix); - - // Translate the existing expose. - foreach (QRectF exposedRect, c->exposed) - c->exposed += exposedRect.translated(dx, dy) & scrollRect; - - // Calculate exposure. - QRegion exposed; - QRect r = scrollRect.toRect(); - exposed += r; - exposed -= r.translated(dx, dy); - foreach (QRect rect, exposed.rects()) - update(rect); - d->scene->d_func()->markDirty(this); - } else { - update(rect); - } - } else { - // ### This is very slow, and can be done much better. If the cache is - // local and matches the below criteria for rotation and scaling, we - // can easily scroll. And if the cache is in device coordinates, we - // can scroll both the viewport and the cache. - update(rect); - } + + // Accelerated scrolling means moving pixels from one location to another + // and only redraw the newly exposed area. The following requirements must + // be fulfilled in order to do that: + // + // 1) Item is opaque. + // 2) Item is not overlapped by other items. + // + // There's (yet) no way to detect whether an item is opaque or not, which means + // we cannot do accelerated scrolling unless the cache is enabled. In case of using + // DeviceCoordinate cache we also have to take the device transform into account in + // order to determine whether we can do accelerated scrolling or not. That's left out + // for simplicity here, but it is definitely something we can consider in the future + // as a performance improvement. + if (d->cacheMode != QGraphicsItem::ItemCoordinateCache + || !qFuzzyIsNull(dx - int(dx)) || !qFuzzyIsNull(dy - int(dy))) { + update(rect); return; } - QRectF scrollRect = !rect.isNull() ? rect : boundingRect(); - int couldntScroll = d->scene->views().size(); - foreach (QGraphicsView *view, d->scene->views()) { - if (view->viewport()->inherits("QGLWidget")) { - // ### Please replace with a widget attribute; any widget that - // doesn't support partial updates / doesn't support scrolling - // should be skipped in this code. Qt::WA_NoPartialUpdates or so. - continue; - } + QGraphicsItemCache *cache = d->extraItemCache(); + if (cache->allExposed || cache->fixedSize.isValid()) { + // Cache is either invalidated or item is scaled (see QGraphicsItem::setCacheMode). + update(rect); + return; + } - static const QLineF up(0, 0, 0, -1); - static const QLineF down(0, 0, 0, 1); - static const QLineF left(0, 0, -1, 0); - static const QLineF right(0, 0, 1, 0); - - QTransform deviceTr = deviceTransform(view->viewportTransform()); - QRect deviceScrollRect = deviceTr.mapRect(scrollRect).toRect(); - QLineF v1 = deviceTr.map(right); - QLineF v2 = deviceTr.map(down); - QLineF u1 = v1.unitVector(); u1.translate(-v1.p1()); - QLineF u2 = v2.unitVector(); u2.translate(-v2.p1()); - bool noScroll = false; - - // Check if the delta resolves to ints in device space. - QPointF deviceDelta = deviceTr.map(QPointF(dx, dy)); - if ((deviceDelta.x() - int(deviceDelta.x())) - || (deviceDelta.y() - int(deviceDelta.y()))) { - noScroll = true; - } else { - // Check if the unit vectors have no fraction in device space. - qreal v1l = v1.length(); - if (v1l - int(v1l)) { - noScroll = true; - } else { - dx *= v1.length(); - } - qreal v2l = v2.length(); - if (v2l - int(v2l)) { - noScroll = true; - } else { - dy *= v2.length(); - } - } + QPixmap cachedPixmap; + if (!QPixmapCache::find(cache->key, &cachedPixmap)) { + update(rect); + return; + } - if (!noScroll) { - if (u1 == right) { - if (u2 == up) { - // flipped - dy = -dy; - } else if (u2 == down) { - // normal - } else { - noScroll = true; - } - } else if (u1 == left) { - if (u2 == up) { - // mirrored & flipped / rotated 180 degrees - dx = -dx; - dy = -dy; - } else if (u2 == down) { - // mirrored - dx = -dx; - } else { - noScroll = true; - } - } else if (u1 == up) { - if (u2 == left) { - // rotated -90 & mirrored - qreal tmp = dy; - dy = -dx; - dx = -tmp; - } else if (u2 == right) { - // rotated -90 - qreal tmp = dy; - dy = -dx; - dx = tmp; - } else { - noScroll = true; - } - } else if (u1 == down) { - if (u2 == left) { - // rotated 90 - qreal tmp = dy; - dy = dx; - dx = -tmp; - } else if (u2 == right) { - // rotated 90 & mirrored - qreal tmp = dy; - dy = dx; - dx = tmp; - } else { - noScroll = true; - } - } - } + QRegion exposed; + const bool scrollEntirePixmap = rect.isNull(); + if (scrollEntirePixmap) { + // Scroll entire pixmap. + cachedPixmap.scroll(dx, dy, cachedPixmap.rect(), &exposed); + } else { + if (!rect.intersects(cache->boundingRect)) + return; // Nothing to scroll. + // Scroll sub-rect of pixmap. The rect is in item coordinates + // so we have to translate it to pixmap coordinates. + QRect scrollRect = rect.toAlignedRect(); + cachedPixmap.scroll(dx, dy, scrollRect.translated(-cache->boundingRect.topLeft()), &exposed); + } - if (!noScroll) { - view->viewport()->scroll(int(dx), int(dy), deviceScrollRect); - --couldntScroll; - } + QPixmapCache::replace(cache->key, cachedPixmap); + + // Translate the existing expose. + for (int i = 0; i < cache->exposed.size(); ++i) { + QRectF &e = cache->exposed[i]; + if (!scrollEntirePixmap && !e.intersects(rect)) + continue; + e.translate(dx, dy); } - if (couldntScroll) - update(rect); + + // Append newly exposed areas. Note that the exposed region is currently + // in pixmap coordinates, so we have to translate it to item coordinates. + exposed.translate(cache->boundingRect.topLeft()); + const QVector<QRect> exposedRects = exposed.rects(); + for (int i = 0; i < exposedRects.size(); ++i) + cache->exposed += exposedRects.at(i); + + // Trigger update. This will redraw the newly exposed area and make sure + // the pixmap is re-blitted in case there are overlapping items. + d->scene->d_func()->markDirty(this, rect); } /*! diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 4b09a7e..344df30 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -4372,11 +4372,6 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte bool pixmapFound; QGraphicsItemCache *itemCache = itemd->extraItemCache(); if (cacheMode == QGraphicsItem::ItemCoordinateCache) { - if (itemCache->boundingRect != brect.toRect()) { - itemCache->boundingRect = brect.toRect(); - itemCache->allExposed = true; - itemCache->exposed.clear(); - } pixmapKey = itemCache->key; } else { pixmapKey = itemCache->deviceData.value(widget).key; @@ -4389,19 +4384,24 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte if (cacheMode == QGraphicsItem::ItemCoordinateCache) { QSize pixmapSize; bool fixedCacheSize = false; - QRectF brectAligned = brect.toAlignedRect(); + QRect br = brect.toAlignedRect(); if ((fixedCacheSize = itemCache->fixedSize.isValid())) { pixmapSize = itemCache->fixedSize; } else { - pixmapSize = brectAligned.size().toSize(); + pixmapSize = br.size(); } // Create or recreate the pixmap. int adjust = itemCache->fixedSize.isValid() ? 0 : 2; QSize adjustSize(adjust*2, adjust*2); - QRectF br = brectAligned.adjusted(-adjust, -adjust, adjust, adjust); + br.adjust(-adjust, -adjust, adjust, adjust); if (pix.isNull() || (!fixedCacheSize && (pixmapSize + adjustSize) != pix.size())) { pix = QPixmap(pixmapSize + adjustSize); + itemCache->boundingRect = br; + itemCache->exposed.clear(); + itemCache->allExposed = true; + } else if (itemCache->boundingRect != br) { + itemCache->boundingRect = br; itemCache->exposed.clear(); itemCache->allExposed = true; } @@ -4455,10 +4455,10 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte // qpixmap-image-transform-pixmap roundtrip. if (newPainterOpacity != oldPainterOpacity) { painter->setOpacity(newPainterOpacity); - painter->drawPixmap(br, pix, QRectF(QPointF(), pix.size())); + painter->drawPixmap(br.topLeft(), pix); painter->setOpacity(oldPainterOpacity); } else { - painter->drawPixmap(br, pix, QRectF(QPointF(), pix.size())); + painter->drawPixmap(br.topLeft(), pix); } return; } |