QGraphicsView: Improve high-DPI item caching

Scale cache size by target paint device devicePixelRatio.

Add manual test for the cache modes.

Change-Id: I9f3a2b4c4cf12571aefe54ebf534009a2448fb48
Done-with: MihailNaydenov <garfieldhq@yahoo.com>
Task-number: QTBUG-26795
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
This commit is contained in:
Morten Johan Sørvig 2016-11-25 13:57:48 +01:00
parent 19950b3267
commit 18d2619224
2 changed files with 47 additions and 12 deletions

View File

@ -4331,7 +4331,8 @@ static void _q_paintIntoCache(QPixmap *pix, QGraphicsItem *item, const QRegion &
pix->fill(Qt::transparent); pix->fill(Qt::transparent);
pixmapPainter.begin(pix); pixmapPainter.begin(pix);
} else { } else {
subPix = QPixmap(br.size()); subPix = QPixmap(br.size() * pix->devicePixelRatio());
subPix.setDevicePixelRatio(pix->devicePixelRatio());
subPix.fill(Qt::transparent); subPix.fill(Qt::transparent);
pixmapPainter.begin(&subPix); pixmapPainter.begin(&subPix);
pixmapPainter.translate(-br.topLeft()); pixmapPainter.translate(-br.topLeft());
@ -4409,6 +4410,7 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
return; return;
} }
const qreal devicePixelRatio = painter->device()->devicePixelRatio();
const qreal oldPainterOpacity = painter->opacity(); const qreal oldPainterOpacity = painter->opacity();
qreal newPainterOpacity = oldPainterOpacity; qreal newPainterOpacity = oldPainterOpacity;
QGraphicsProxyWidget *proxy = item->isWidget() ? qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(item)) : 0; QGraphicsProxyWidget *proxy = item->isWidget() ? qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(item)) : 0;
@ -4428,6 +4430,7 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
// Fetch the off-screen transparent buffer and exposed area info. // Fetch the off-screen transparent buffer and exposed area info.
QPixmapCache::Key pixmapKey; QPixmapCache::Key pixmapKey;
QPixmap pix; QPixmap pix;
bool pixmapFound; bool pixmapFound;
QGraphicsItemCache *itemCache = itemd->extraItemCache(); QGraphicsItemCache *itemCache = itemd->extraItemCache();
if (cacheMode == QGraphicsItem::ItemCoordinateCache) { if (cacheMode == QGraphicsItem::ItemCoordinateCache) {
@ -4442,18 +4445,20 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
// Render using item coordinate cache mode. // Render using item coordinate cache mode.
if (cacheMode == QGraphicsItem::ItemCoordinateCache) { if (cacheMode == QGraphicsItem::ItemCoordinateCache) {
QSize pixmapSize; QSize pixmapSize;
bool fixedCacheSize = false; bool fixedCacheSize = itemCache->fixedSize.isValid();
QRect br = brect.toAlignedRect(); QRect br = brect.toAlignedRect();
if ((fixedCacheSize = itemCache->fixedSize.isValid())) { if (fixedCacheSize) {
pixmapSize = itemCache->fixedSize; pixmapSize = itemCache->fixedSize;
} else { } else {
pixmapSize = br.size(); pixmapSize = br.size();
} }
pixmapSize *= devicePixelRatio;
// Create or recreate the pixmap. // Create or recreate the pixmap.
int adjust = itemCache->fixedSize.isValid() ? 0 : 2; int adjust = itemCache->fixedSize.isValid() ? 0 : 2;
QSize adjustSize(adjust*2, adjust*2); QSize adjustSize(adjust*2, adjust*2);
br.adjust(-adjust, -adjust, adjust, adjust); br.adjust(-adjust / devicePixelRatio, -adjust / devicePixelRatio, adjust / devicePixelRatio, adjust / devicePixelRatio);
if (pix.isNull() || (!fixedCacheSize && (pixmapSize + adjustSize) != pix.size())) { if (pix.isNull() || (!fixedCacheSize && (pixmapSize + adjustSize) != pix.size())) {
pix = QPixmap(pixmapSize + adjustSize); pix = QPixmap(pixmapSize + adjustSize);
itemCache->boundingRect = br; itemCache->boundingRect = br;
@ -4476,7 +4481,8 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
// Fit the item's bounding rect into the pixmap's coordinates. // Fit the item's bounding rect into the pixmap's coordinates.
QTransform itemToPixmap; QTransform itemToPixmap;
if (fixedCacheSize) { if (fixedCacheSize) {
const QPointF scale(pixmapSize.width() / brect.width(), pixmapSize.height() / brect.height()); const QPointF scale((pixmapSize.width() / devicePixelRatio) / brect.width(),
(pixmapSize.height() / devicePixelRatio) / brect.height());
itemToPixmap.scale(scale.x(), scale.y()); itemToPixmap.scale(scale.x(), scale.y());
} }
itemToPixmap.translate(-br.x(), -br.y()); itemToPixmap.translate(-br.x(), -br.y());
@ -4498,6 +4504,7 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
styleOptionTmp.exposedRect = exposedRect; styleOptionTmp.exposedRect = exposedRect;
// Render. // Render.
pix.setDevicePixelRatio(devicePixelRatio);
_q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(), _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(),
&styleOptionTmp, painterStateProtection); &styleOptionTmp, painterStateProtection);
@ -4595,21 +4602,22 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
// Copy / "scroll" the old pixmap onto the new ole and calculate // Copy / "scroll" the old pixmap onto the new ole and calculate
// scrolled exposure. // scrolled exposure.
if (newCacheIndent != deviceData->cacheIndent || deviceRect.size() != pix.size()) { if (newCacheIndent != deviceData->cacheIndent || deviceRect.size() != pix.size() / devicePixelRatio) {
QPoint diff = newCacheIndent - deviceData->cacheIndent; QPoint diff = newCacheIndent - deviceData->cacheIndent;
QPixmap newPix(deviceRect.size()); QPixmap newPix(deviceRect.size() * devicePixelRatio);
// ### Investigate removing this fill (test with Plasma and // ### Investigate removing this fill (test with Plasma and
// graphicssystem raster). // graphicssystem raster).
newPix.fill(Qt::transparent); newPix.fill(Qt::transparent);
if (!pix.isNull()) { if (!pix.isNull()) {
newPix.setDevicePixelRatio(devicePixelRatio);
QPainter newPixPainter(&newPix); QPainter newPixPainter(&newPix);
newPixPainter.drawPixmap(-diff, pix); newPixPainter.drawPixmap(-diff, pix);
newPixPainter.end(); newPixPainter.end();
} }
QRegion exposed; QRegion exposed;
exposed += newPix.rect(); exposed += QRect(QPoint(0,0), newPix.size() / devicePixelRatio);
if (!pix.isNull()) if (!pix.isNull())
exposed -= QRect(-diff, pix.size()); exposed -= QRect(-diff, pix.size() / devicePixelRatio);
scrollExposure = exposed; scrollExposure = exposed;
pix = newPix; pix = newPix;
@ -4621,9 +4629,9 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
deviceData->cacheIndent = QPoint(); deviceData->cacheIndent = QPoint();
// Auto-adjust the pixmap size. // Auto-adjust the pixmap size.
if (deviceRect.size() != pix.size()) { if (deviceRect.size() != pix.size() / devicePixelRatio) {
// exposed needs to cover the whole pixmap // exposed needs to cover the whole pixmap
pix = QPixmap(deviceRect.size()); pix = QPixmap(deviceRect.size() * devicePixelRatio);
pixModified = true; pixModified = true;
itemCache->allExposed = true; itemCache->allExposed = true;
itemCache->exposed.clear(); itemCache->exposed.clear();
@ -4667,6 +4675,7 @@ void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painte
styleOptionTmp.exposedRect = br.adjusted(-1, -1, 1, 1); styleOptionTmp.exposedRect = br.adjusted(-1, -1, 1, 1);
// Render the exposed areas. // Render the exposed areas.
pix.setDevicePixelRatio(devicePixelRatio);
_q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(), _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(),
&styleOptionTmp, painterStateProtection); &styleOptionTmp, painterStateProtection);

View File

@ -45,6 +45,8 @@
#include <QPainter> #include <QPainter>
#include <QWindow> #include <QWindow>
#include <QScreen> #include <QScreen>
#include <QGraphicsView>
#include <QGraphicsTextItem>
#include <QFile> #include <QFile>
#include <QMouseEvent> #include <QMouseEvent>
#include <QTemporaryDir> #include <QTemporaryDir>
@ -1151,6 +1153,30 @@ void PhysicalSizeTest::paintEvent(QPaintEvent *)
} }
class GraphicsViewCaching : public QGraphicsView
{
public:
GraphicsViewCaching() {
QGraphicsScene *scene = new QGraphicsScene(0, 0, 400, 400);
QGraphicsTextItem *item = 0;
item = scene->addText("NoCache");
item->setCacheMode(QGraphicsItem::NoCache);
item->setPos(10, 10);
item = scene->addText("ItemCoordinateCache");
item->setCacheMode(QGraphicsItem::ItemCoordinateCache);
item->setPos(10, 30);
item = scene->addText("DeviceCoordinateCache");
item->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
item->setPos(10, 50);
setScene(scene);
}
};
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
QApplication app(argc, argv); QApplication app(argc, argv);
@ -1186,7 +1212,7 @@ int main(int argc, char **argv)
demoList << new DemoContainer<CursorTester>("cursorpos", "Test cursor and window positioning"); demoList << new DemoContainer<CursorTester>("cursorpos", "Test cursor and window positioning");
demoList << new DemoContainer<ScreenDisplayer>("screens", "Test screen and window positioning"); demoList << new DemoContainer<ScreenDisplayer>("screens", "Test screen and window positioning");
demoList << new DemoContainer<PhysicalSizeTest>("physicalsize", "Test manual highdpi support using physicalDotsPerInch"); demoList << new DemoContainer<PhysicalSizeTest>("physicalsize", "Test manual highdpi support using physicalDotsPerInch");
demoList << new DemoContainer<GraphicsViewCaching>("graphicsview", "Test QGraphicsView caching");
foreach (DemoContainerBase *demo, demoList) foreach (DemoContainerBase *demo, demoList)
parser.addOption(demo->option()); parser.addOption(demo->option());