From 94a3a492f52398eb1c08ea910fbd007b31228525 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Sun, 26 May 2024 11:18:26 +0200 Subject: [PATCH] QIcon::addFile() delay-load icons when a proper size is given Delay-load the icon within QPixmapEngine::addFile() when a proper size is given - this speeds up QIcon creation and also allows to let QIcon::actualSize() return the correct size of an QIcon instead the given one. This is especially useful e.g. when a 32x32 icon has a smaller width or height and it should be centered during painting. Also add an optimization for multi-image formats to check for the correct size without decoding the image. Pick-to: 6.8 Task-number: QTBUG-59621 Change-Id: I7ed930ae3f65a8b3a272f70bcc2958980f9f02f2 Reviewed-by: Volker Hilsheimer --- src/gui/image/qicon.cpp | 48 +++++++++++++++++++++++++++++++---------- src/gui/image/qicon_p.h | 2 +- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp index 43a0c8a93d0..65c016c27f1 100644 --- a/src/gui/image/qicon.cpp +++ b/src/gui/image/qicon.cpp @@ -43,6 +43,7 @@ public: bool supportsReadSize() const { return m_reader.supportsOption(QImageIOHandler::Size); } QSize size() const { return m_reader.size(); } bool jumpToNextImage() { return m_reader.jumpToNextImage(); } + void jumpToImage(int index) { m_reader.jumpToImage(index); } bool read(QImage *image) { @@ -244,7 +245,7 @@ QPixmapIconEngineEntry *QPixmapIconEngine::tryMatch(const QSize &size, qreal sca } -QPixmapIconEngineEntry *QPixmapIconEngine::bestMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state, bool sizeOnly) +QPixmapIconEngineEntry *QPixmapIconEngine::bestMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state) { QPixmapIconEngineEntry *pe = tryMatch(size, scale, mode, state); while (!pe){ @@ -287,10 +288,38 @@ QPixmapIconEngineEntry *QPixmapIconEngine::bestMatch(const QSize &size, qreal sc return pe; } - if (sizeOnly ? (pe->size.isNull() || !pe->size.isValid()) : pe->pixmap.isNull()) { - pe->pixmap = QPixmap(pe->fileName); - if (!pe->pixmap.isNull()) - pe->size = pe->pixmap.size(); + if (pe->pixmap.isNull()) { + // delay-load the image + ImageReader imageReader(pe->fileName); + QImage image, prevImage; + const QSize realSize = size * scale; + bool fittingImageFound = false; + if (imageReader.supportsReadSize()) { + // find the image with the best size without loading the entire image + do { + fittingImageFound = imageReader.size() == realSize; + } while (!fittingImageFound && imageReader.jumpToNextImage()); + } + if (!fittingImageFound) { + imageReader.jumpToImage(0); + while (imageReader.read(&image) && image.size() != realSize) + prevImage = image; + if (image.isNull()) + image = prevImage; + } else { + imageReader.read(&image); + } + if (!image.isNull()) { + pe->pixmap.convertFromImage(image); + if (!pe->pixmap.isNull()) { + pe->size = pe->pixmap.size(); + pe->pixmap.setDevicePixelRatio(scale); + } + } + if (!pe->size.isValid()) { + removePixmapEntry(pe); + pe = nullptr; + } } return pe; @@ -304,7 +333,7 @@ QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::St QPixmap QPixmapIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) { QPixmap pm; - QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state, false); + QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state); if (pe) pm = pe->pixmap; else @@ -364,7 +393,7 @@ QSize QPixmapIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon:: // does not proviode extra actual sizes not also provided by the 1x versions. qreal scale = 1; - if (QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state, true)) + if (QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state)) actualSize = pe->size; return adjustSize(size, actualSize); @@ -438,10 +467,7 @@ void QPixmapIconEngine::addFile(const QString &fileName, const QSize &size, QIco pixmaps += QPixmapIconEngineEntry(abs, image, mode, state); } } else { - // Try to match size. If that fails, add a placeholder with the filename and empty pixmap for the size. - while (imageReader.read(&image) && image.size() != size) {} - pixmaps += image.size() == size ? - QPixmapIconEngineEntry(abs, image, mode, state) : QPixmapIconEngineEntry(abs, size, mode, state); + pixmaps += QPixmapIconEngineEntry(abs, size, mode, state); } return; } diff --git a/src/gui/image/qicon_p.h b/src/gui/image/qicon_p.h index 8050d764351..60985d538dc 100644 --- a/src/gui/image/qicon_p.h +++ b/src/gui/image/qicon_p.h @@ -77,7 +77,7 @@ public: void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override; QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override; QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) override; - QPixmapIconEngineEntry *bestMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state, bool sizeOnly); + QPixmapIconEngineEntry *bestMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state); QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override; QList availableSizes(QIcon::Mode mode, QIcon::State state) override; void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state) override;