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 <volker.hilsheimer@qt.io>
This commit is contained in:
Christian Ehrlicher 2024-05-26 11:18:26 +02:00
parent 6b23a3c5e4
commit 94a3a492f5
2 changed files with 38 additions and 12 deletions

View File

@ -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;
}

View File

@ -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<QSize> availableSizes(QIcon::Mode mode, QIcon::State state) override;
void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state) override;