aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMitch Curtis <mitch.curtis@qt.io>2026-09-14 14:06:57 +0800
committerMitch Curtis <mitch.curtis@qt.io>2026-09-14 14:27:26 +0000
commite8d5cd542f3ffcc7597c4a46804edb3b5f415180 (patch)
tree6781f6670b75317f0a956da7a046499f9e83354e
parentb464941cabc6e392214bc4076415f4b23b19eee3 (diff)
QQuickImage: fix devicePixelRatio being lost for grabToImage() results
QQuickItemGrabResult::render() correctly records the window's devicePixelRatio on the grabbed QImage. But when that image is exposed under an "itemgrabber:" URL via QQuickPixmap::QQuickPixmap(const QUrl &, const QImage &), the ratio was discarded: QQuickPixmapData's devicePixelRatio was left at its default of 1, and QQuickImageBase::loadPixmap() unconditionally resets its own devicePixelRatio to 1.0, only restoring a real value for scalable image formats (SVG/PDF) or "@2x" local files. An itemgrabber: URL, being a raster in-memory image, matches neither. As a result, an Image loaded from a grabToImage() result via "Image { source: result.url }" (with no explicit width/height/sourceSize) ended up devicePixelRatio times too big in device-independent pixels, even though the underlying pixel data and DPR metadata were both already correct. Fix this by propagating image.devicePixelRatio() into QQuickPixmapData when QQuickPixmap wraps a raw QImage, exposing it via a new QQuickPixmap::devicePixelRatio() accessor, and having QQuickImageBase::requestFinished() adopt that ratio for "itemgrabber:" URLs specifically, once the pixmap has actually loaded (the ratio isn't knowable ahead of load the way it is for "@2x"/scalable formats). Add tst_qquickimage::grabToImageDevicePixelRatio() to cover this. Fixes: QTBUG-150289 Change-Id: Iaf72a76c779ee208fac45e539543f9c6cb9738f7 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
-rw-r--r--src/quick/items/qquickimagebase.cpp5
-rw-r--r--src/quick/util/qquickpixmap_p.h1
-rw-r--r--src/quick/util/qquickpixmapcache.cpp8
-rw-r--r--tests/auto/quick/qquickimage/data/grabToImageDevicePixelRatio.qml12
-rw-r--r--tests/auto/quick/qquickimage/tst_qquickimage.cpp27
5 files changed, 52 insertions, 1 deletions
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp
index 592d9dec1a..17850a29fd 100644
--- a/src/quick/items/qquickimagebase.cpp
+++ b/src/quick/items/qquickimagebase.cpp
@@ -377,6 +377,11 @@ void QQuickImageBase::requestFinished()
d->status = Error;
d->setProgress(0);
} else {
+ // itemgrabber: images carry their own devicePixelRatio (set by whatever grabbed
+ // them), which isn't known ahead of load the way "@2x" and scalable-image-format
+ // ratios are, so pick it up here once the pixmap has actually loaded.
+ if (d->url.scheme() == QQuickPixmap::itemGrabberScheme)
+ d->devicePixelRatio = d->currentPix->devicePixelRatio();
d->status = Ready; // do not emit statusChanged until after setImplicitSize
d->setProgress(1);
}
diff --git a/src/quick/util/qquickpixmap_p.h b/src/quick/util/qquickpixmap_p.h
index 972235f172..ad66e014b0 100644
--- a/src/quick/util/qquickpixmap_p.h
+++ b/src/quick/util/qquickpixmap_p.h
@@ -144,6 +144,7 @@ public:
QRect rect() const;
int width() const;
int height() const;
+ qreal devicePixelRatio() const;
void load(QQmlEngine *, const QUrl &);
void load(QQmlEngine *, const QUrl &, QQuickPixmap::Options options);
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index 288d7afbce..7e693a6587 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -1709,7 +1709,8 @@ QQuickPixmap::QQuickPixmap(QQmlEngine *engine, const QUrl &url, const QRect &reg
QQuickPixmap::QQuickPixmap(const QUrl &url, const QImage &image)
{
d = new QQuickPixmapData(url, new QQuickDefaultTextureFactory(image), image.size(), QRect(), QSize(),
- QQuickImageProviderOptions(), QQuickImageProviderOptions::UsePluginDefaultTransform);
+ QQuickImageProviderOptions(), QQuickImageProviderOptions::UsePluginDefaultTransform,
+ 0, 1, image.devicePixelRatio());
d->addToCache();
}
@@ -1860,6 +1861,11 @@ int QQuickPixmap::height() const
return 0;
}
+qreal QQuickPixmap::devicePixelRatio() const
+{
+ return d ? d->devicePixelRatio : 1.0;
+}
+
QRect QQuickPixmap::rect() const
{
if (d && d->textureFactory)
diff --git a/tests/auto/quick/qquickimage/data/grabToImageDevicePixelRatio.qml b/tests/auto/quick/qquickimage/data/grabToImageDevicePixelRatio.qml
new file mode 100644
index 0000000000..4a7f9ad79c
--- /dev/null
+++ b/tests/auto/quick/qquickimage/data/grabToImageDevicePixelRatio.qml
@@ -0,0 +1,12 @@
+import QtQuick
+
+Image {
+ id: sourceImage
+ objectName: "sourceImage"
+ source: "rect.png"
+
+ Image {
+ id: grabbedImage
+ objectName: "grabbedImage"
+ }
+}
diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp
index cbd6282e2b..2bdad33777 100644
--- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp
+++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp
@@ -21,6 +21,7 @@
#include <QQuickView>
#include <QQuickImageProvider>
#include <QQmlAbstractUrlInterceptor>
+#include <QtQuick/QQuickItemGrabResult>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtQuickTestUtils/private/testhttpserver_p.h>
@@ -83,6 +84,7 @@ private slots:
void multiFrame();
void colorSpace();
void devicePixelRatio();
+ void grabToImageDevicePixelRatio();
private:
QQmlEngine engine;
@@ -1344,6 +1346,31 @@ void tst_qquickimage::devicePixelRatio()
QCOMPARE(obj2->sourceSize(), (devicePixelRatio * origSize).toSize());
}
+void tst_qquickimage::grabToImageDevicePixelRatio()
+{
+ QQuickView window;
+ QVERIFY(QQuickTest::showView(window, testFileUrl("grabToImageDevicePixelRatio.qml")));
+
+ if (window.effectiveDevicePixelRatio() <= 1)
+ QSKIP("This test only demonstrates the bug on a HiDPI (devicePixelRatio > 1) screen");
+
+ QQuickImage *sourceImage = qmlobject_cast<QQuickImage *>(window.rootObject());
+ QVERIFY(sourceImage);
+ QQuickImage *grabbedImage = sourceImage->findChild<QQuickImage *>("grabbedImage");
+ QVERIFY(grabbedImage);
+
+ auto grabResult = sourceImage->grabToImage();
+ QVERIFY(grabResult);
+ QSignalSpy readySpy(grabResult.data(), &QQuickItemGrabResult::ready);
+ QVERIFY(readySpy.wait());
+ grabbedImage->setSource(grabResult->url());
+ QTRY_COMPARE(grabbedImage->status(), QQuickImageBase::Ready);
+
+ // grabToImage() results loaded via Image.source ignore devicePixelRatio: on a HiDPI screen,
+ // grabbedImage ends up devicePixelRatio times too big instead of matching sourceImage.
+ QCOMPARE(grabbedImage->size(), sourceImage->size());
+}
+
QTEST_MAIN(tst_qquickimage)
#include "tst_qquickimage.moc"