aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick
diff options
context:
space:
mode:
authorVladimir Belyavsky <belyavskyv@gmail.com>2024-04-19 22:55:07 +0300
committerVladimir Belyavsky <belyavskyv@gmail.com>2024-05-07 08:11:37 +0300
commitf8f42c929735dfb5a8f3202d4a4f3bf3f3d31d9a (patch)
treeacdaedaa71e5d2c08709b1d61c9a3a08e64128e1 /src/quick
parent5afde2703ff1e60c30379de72567a9facb008d7b (diff)
QQuickText: consider DPR when drawing scalable inline images
Take into account the device pixel ratio when drawing scalable inline images (such as SVG, SVGZ, PDF) in QML Text. Thus we can improve the image rendering quality, especially for cases when the image size is set explicitly. Consider the following case: Text { text: '<img src="foo.svg" width="16" height="16"/>' } In case the device pixel ratio is e.g. 1.5, we loaded the SVG file into 16x16 QImage and after that we upscaled it as a raster to 24x24 with quality loss. Now we consider DPR when loading the image. Note: this change improves only rendering for scalable images and do not provide the support for "@2x" raster resources. But this can be done in future changes. Fixes: QTBUG-113040 Pick-to: 6.7 Change-Id: I8baada728e9acec5528553d0616ac37994ab80b6 Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Diffstat (limited to 'src/quick')
-rw-r--r--src/quick/items/qquickimagebase.cpp15
-rw-r--r--src/quick/items/qquicktext.cpp82
-rw-r--r--src/quick/items/qquicktext_p_p.h3
-rw-r--r--src/quick/util/qquickpixmap_p.h1
-rw-r--r--src/quick/util/qquickpixmapcache.cpp13
5 files changed, 72 insertions, 42 deletions
diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp
index ab827379fb..bcc685d64c 100644
--- a/src/quick/items/qquickimagebase.cpp
+++ b/src/quick/items/qquickimagebase.cpp
@@ -15,16 +15,7 @@
QT_BEGIN_NAMESPACE
-bool isScalableImageFormat(const QUrl &url)
-{
- if (url.scheme() == QLatin1String("image"))
- return true;
-
- const QString stringUrl = url.path(QUrl::PrettyDecoded);
- return stringUrl.endsWith(QLatin1String("svg"))
- || stringUrl.endsWith(QLatin1String("svgz"))
- || stringUrl.endsWith(QLatin1String("pdf"));
-}
+using namespace Qt::Literals::StringLiterals;
// This function gives derived classes the chance set the devicePixelRatio
// if they're not happy with our implementation of it.
@@ -33,7 +24,7 @@ bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio
// QQuickImageProvider and SVG and PDF can generate a high resolution image when
// sourceSize is set. If sourceSize is not set then the provider default size will
// be used, as usual.
- const bool setDevicePixelRatio = isScalableImageFormat(url);
+ const bool setDevicePixelRatio = QQuickPixmap::isScalableImageFormat(url);
if (setDevicePixelRatio)
devicePixelRatio = targetDevicePixelRatio;
@@ -310,7 +301,7 @@ void QQuickImageBase::loadPixmap(const QUrl &url, LoadPixmapOptions loadOptions)
d->devicePixelRatio = 1.0;
bool updatedDevicePixelRatio = false;
if (d->sourcesize.isValid()
- || (isScalableImageFormat(d->url) && d->url.scheme() != QLatin1String("image"))) {
+ || (QQuickPixmap::isScalableImageFormat(d->url) && d->url.scheme() != "image"_L1)) {
updatedDevicePixelRatio = d->updateDevicePixelRatio(targetDevicePixelRatio);
}
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 3b37dd975b..5f85fa2080 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -82,7 +82,6 @@ QQuickTextPrivate::ExtraData::ExtraData()
, doc(nullptr)
, minimumPixelSize(12)
, minimumPointSize(12)
- , nbActiveDownloads(0)
, maximumLineCount(INT_MAX)
, renderTypeQuality(QQuickText::DefaultRenderTypeQuality)
, lineHeightValid(false)
@@ -357,29 +356,33 @@ void QQuickText::resourceRequestFinished()
void QQuickText::imageDownloadFinished()
{
Q_D(QQuickText);
+ if (!d->extra.isAllocated())
+ return;
- (d->extra->nbActiveDownloads)--;
+ if (std::any_of(d->extra->imgTags.cbegin(), d->extra->imgTags.cend(),
+ [] (auto *image) { return image->pix && image->pix->isLoading(); })) {
+ // return if we still have any active download
+ return;
+ }
// when all the remote images have been downloaded,
// if one of the sizes was not specified at parsing time
// we use the implicit size from pixmapcache and re-layout.
- if (d->extra.isAllocated() && d->extra->nbActiveDownloads == 0) {
- bool needToUpdateLayout = false;
- for (QQuickStyledTextImgTag *img : std::as_const(d->extra->visibleImgTags)) {
- if (!img->size.isValid()) {
- img->size = img->pix->implicitSize();
- needToUpdateLayout = true;
- }
+ bool needToUpdateLayout = false;
+ for (QQuickStyledTextImgTag *img : std::as_const(d->extra->visibleImgTags)) {
+ if (!img->size.isValid()) {
+ img->size = img->pix->implicitSize();
+ needToUpdateLayout = true;
}
+ }
- if (needToUpdateLayout) {
- d->textHasChanged = true;
- d->updateLayout();
- } else {
- d->updateType = QQuickTextPrivate::UpdatePaintNode;
- update();
- }
+ if (needToUpdateLayout) {
+ d->textHasChanged = true;
+ d->updateLayout();
+ } else {
+ d->updateType = QQuickTextPrivate::UpdatePaintNode;
+ update();
}
}
@@ -1289,12 +1292,10 @@ void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal
if (!image->pix) {
const QQmlContext *context = qmlContext(q);
const QUrl url = context->resolvedUrl(q->baseUrl()).resolved(image->url);
- image->pix = new QQuickPixmap(context->engine(), url, QRect(), image->size);
+ image->pix = new QQuickPixmap(context->engine(), url, QRect(), image->size * devicePixelRatio());
+
if (image->pix->isLoading()) {
image->pix->connectFinished(q, SLOT(imageDownloadFinished()));
- if (!extra.isAllocated() || !extra->nbActiveDownloads)
- extra.value().nbActiveDownloads = 0;
- extra->nbActiveDownloads++;
} else if (image->pix->isReady()) {
if (!image->size.isValid()) {
image->size = image->pix->implicitSize();
@@ -1379,6 +1380,11 @@ void QQuickTextPrivate::updateDocumentText()
rightToLeftText = extra->doc->toPlainText().isRightToLeft();
}
+qreal QQuickTextPrivate::devicePixelRatio() const
+{
+ return (window ? window->effectiveDevicePixelRatio() : qApp->devicePixelRatio());
+}
+
/*!
\qmltype Text
\instantiates QQuickText
@@ -1901,14 +1907,32 @@ void QQuickText::itemChange(ItemChange change, const ItemChangeData &value)
break;
case ItemDevicePixelRatioHasChanged:
- if (d->renderType == NativeRendering) {
- // Native rendering optimizes for a given pixel grid, so its results must not be scaled.
- // Text layout code respects the current device pixel ratio automatically, we only need
- // to rerun layout after the ratio changed.
- // Changes of implicit size should be minimal; they are hard to avoid.
- d->implicitWidthValid = false;
- d->implicitHeightValid = false;
- d->updateLayout();
+ {
+ bool needUpdateLayout = false;
+ if (d->renderType == NativeRendering) {
+ // Native rendering optimizes for a given pixel grid, so its results must not be scaled.
+ // Text layout code respects the current device pixel ratio automatically, we only need
+ // to rerun layout after the ratio changed.
+ // Changes of implicit size should be minimal; they are hard to avoid.
+ d->implicitWidthValid = false;
+ d->implicitHeightValid = false;
+ needUpdateLayout = true;
+ }
+
+ if (d->extra.isAllocated()) {
+ // check if we have scalable inline images with explicit size set, which should be reloaded
+ for (QQuickStyledTextImgTag *image : std::as_const(d->extra->visibleImgTags)) {
+ if (image->size.isValid() && QQuickPixmap::isScalableImageFormat(image->url)) {
+ delete image->pix;
+ image->pix = nullptr;
+
+ needUpdateLayout = true;
+ }
+ }
+ }
+
+ if (needUpdateLayout)
+ d->updateLayout();
}
break;
@@ -2755,7 +2779,7 @@ QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data
for (QQuickStyledTextImgTag *img : std::as_const(d->extra->visibleImgTags)) {
QQuickPixmap *pix = img->pix;
if (pix && pix->isReady())
- node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, pix->width(), pix->height()), pix->image());
+ node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, img->size.width(), img->size.height()), pix->image());
}
}
}
diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h
index 2e54ae53a1..a715313d2d 100644
--- a/src/quick/items/qquicktext_p_p.h
+++ b/src/quick/items/qquicktext_p_p.h
@@ -77,7 +77,6 @@ public:
QString hoveredLink;
int minimumPixelSize;
int minimumPointSize;
- int nbActiveDownloads;
int maximumLineCount;
int renderTypeQuality;
bool lineHeightValid : 1;
@@ -164,6 +163,8 @@ public:
void ensureDoc();
void updateDocumentText();
+ qreal devicePixelRatio() const;
+
QRectF setupTextLayout(qreal * const baseline);
void setupCustomLineGeometry(QTextLine &line, qreal &height, int fullLayoutTextLength, int lineOffset = 0);
bool isLinkActivatedConnected();
diff --git a/src/quick/util/qquickpixmap_p.h b/src/quick/util/qquickpixmap_p.h
index ad4fdb9111..f352da8d16 100644
--- a/src/quick/util/qquickpixmap_p.h
+++ b/src/quick/util/qquickpixmap_p.h
@@ -166,6 +166,7 @@ public:
static void purgeCache();
static bool isCached(const QUrl &url, const QRect &requestRegion, const QSize &requestSize,
const int frame, const QQuickImageProviderOptions &options);
+ static bool isScalableImageFormat(const QUrl &url);
static const QLatin1String itemGrabberScheme;
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index 8de79f2009..10cc407c21 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -56,6 +56,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::Literals::StringLiterals;
+
Q_DECLARE_LOGGING_CATEGORY(lcQsgLeak)
#if defined(QT_DEBUG) && QT_CONFIG(thread)
@@ -2008,6 +2010,17 @@ bool QQuickPixmap::isCached(const QUrl &url, const QRect &requestRegion, const Q
return store->m_cache.contains(key);
}
+bool QQuickPixmap::isScalableImageFormat(const QUrl &url)
+{
+ if (url.scheme() == "image"_L1)
+ return true;
+
+ const QString stringUrl = url.path(QUrl::PrettyDecoded);
+ return stringUrl.endsWith("svg"_L1)
+ || stringUrl.endsWith("svgz"_L1)
+ || stringUrl.endsWith("pdf"_L1);
+}
+
bool QQuickPixmap::connectFinished(QObject *object, const char *method)
{
if (!d || !d->reply) {