QIcon/QIconLoader: misc cleanup

Cleanup QIcon:
 - factor out actualSize() calculation
 - factor out deleting invalid pixmap entry
 - don't overflow on int*int calculation
 - use quint8 for state and mode in cacheKey calculation
 - sync cacheKey calculation
 - make QIconPrivate::pixmapDevicePixelRatio() static

Change-Id: I7716b6f69687b6e5c910a0cb180b32b2de9a015d
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Christian Ehrlicher 2024-05-26 10:25:41 +02:00
parent 94919aea6a
commit 0efd2e084d
3 changed files with 65 additions and 68 deletions

View File

@ -32,6 +32,33 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
// Convenience class providing a bool read() function.
namespace {
class ImageReader
{
public:
ImageReader(const QString &fileName) : m_reader(fileName), m_atEnd(false) { }
QByteArray format() const { return m_reader.format(); }
bool read(QImage *image)
{
if (m_atEnd)
return false;
*image = m_reader.read();
if (!image->size().isValid()) {
m_atEnd = true;
return false;
}
m_atEnd = !m_reader.jumpToNextImage();
return true;
}
private:
QImageReader m_reader;
bool m_atEnd;
};
} // namespace
/*! /*!
\enum QIcon::Mode \enum QIcon::Mode
@ -150,7 +177,7 @@ void QPixmapIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode
painter->drawPixmap(rect, px); painter->drawPixmap(rect, px);
} }
static inline int area(const QSize &s) { return s.width() * s.height(); } static inline qint64 area(const QSize &s) { return qint64(s.width()) * s.height(); }
// Returns the smallest of the two that is still larger than or equal to size. // Returns the smallest of the two that is still larger than or equal to size.
// Pixmaps at the correct scale are preferred, pixmaps at lower scale are // Pixmaps at the correct scale are preferred, pixmaps at lower scale are
@ -178,18 +205,18 @@ static QPixmapIconEngineEntry *bestSizeScaleMatch(const QSize &size, qreal scale
return (qAbs(ascore) < qAbs(bscore)) ? pa : pb; return (qAbs(ascore) < qAbs(bscore)) ? pa : pb;
} }
int s = area(size); qint64 s = area(size);
if (pa->size == QSize() && pa->pixmap.isNull()) { if (pa->size == QSize() && pa->pixmap.isNull()) {
pa->pixmap = QPixmap(pa->fileName); pa->pixmap = QPixmap(pa->fileName);
pa->size = pa->pixmap.size(); pa->size = pa->pixmap.size();
} }
int a = area(pa->size); qint64 a = area(pa->size);
if (pb->size == QSize() && pb->pixmap.isNull()) { if (pb->size == QSize() && pb->pixmap.isNull()) {
pb->pixmap = QPixmap(pb->fileName); pb->pixmap = QPixmap(pb->fileName);
pb->size = pb->pixmap.size(); pb->size = pb->pixmap.size();
} }
int b = area(pb->size); qint64 b = area(pb->size);
int res = a; qint64 res = a;
if (qMin(a,b) >= s) if (qMin(a,b) >= s)
res = qMin(a,b); res = qMin(a,b);
else else
@ -273,41 +300,30 @@ 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 QPixmapIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
{ {
QPixmap pm; QPixmap pm;
QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state, false); QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state, false);
if (pe) if (pe)
pm = pe->pixmap; pm = pe->pixmap;
if (pm.isNull()) { if (pm.isNull()) {
auto idx = pixmaps.size(); removePixmapEntry(pe);
while (--idx >= 0) {
if (pe == &pixmaps.at(idx)) {
pixmaps.remove(idx);
break;
}
}
if (pixmaps.isEmpty()) if (pixmaps.isEmpty())
return pm; return pm;
else return scaledPixmap(size, mode, state, scale);
return pixmap(size, mode, state);
} }
QSize actualSize = pm.size(); const auto actualSize = adjustSize(size, pm.size());
if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
actualSize.scale(size, Qt::KeepAspectRatio);
QString key = "qt_"_L1 QString key = "qt_"_L1
% HexString<quint64>(pm.cacheKey()) % HexString<quint64>(pm.cacheKey())
% HexString<uint>(pe ? pe->mode : QIcon::Normal) % HexString<quint8>(pe ? pe->mode : QIcon::Normal)
% HexString<quint64>(QGuiApplication::palette().cacheKey()) % HexString<quint64>(QGuiApplication::palette().cacheKey())
% HexString<uint>(actualSize.width()) % HexString<uint>(actualSize.width())
% HexString<uint>(actualSize.height()); % HexString<uint>(actualSize.height());
if (mode == QIcon::Active) { if (mode == QIcon::Active) {
if (QPixmapCache::find(key % HexString<uint>(mode), &pm)) if (QPixmapCache::find(key % HexString<quint8>(mode), &pm))
return pm; // horray return pm; // horray
if (QPixmapCache::find(key % HexString<uint>(QIcon::Normal), &pm)) { if (QPixmapCache::find(key % HexString<quint8>(QIcon::Normal), &pm)) {
QPixmap active = pm; QPixmap active = pm;
if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp)) if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp))
active = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(guiApp))->applyQIconStyleHelper(QIcon::Active, pm); active = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(guiApp))->applyQIconStyleHelper(QIcon::Active, pm);
@ -316,7 +332,7 @@ QPixmap QPixmapIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIc
} }
} }
if (!QPixmapCache::find(key % HexString<uint>(mode), &pm)) { if (!QPixmapCache::find(key % HexString<quint8>(mode), &pm)) {
if (pm.size() != actualSize) if (pm.size() != actualSize)
pm = pm.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); pm = pm.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
if (pe->mode != mode && mode != QIcon::Normal) { if (pe->mode != mode && mode != QIcon::Normal) {
@ -326,7 +342,7 @@ QPixmap QPixmapIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIc
if (!generated.isNull()) if (!generated.isNull())
pm = generated; pm = generated;
} }
QPixmapCache::insert(key % HexString<uint>(mode), pm); QPixmapCache::insert(key % HexString<quint8>(mode), pm);
} }
return pm; return pm;
} }
@ -343,12 +359,7 @@ QSize QPixmapIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::
if (QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state, true)) if (QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state, true))
actualSize = pe->size; actualSize = pe->size;
if (actualSize.isNull()) return adjustSize(size, actualSize);
return actualSize;
if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
actualSize.scale(size, Qt::KeepAspectRatio);
return actualSize;
} }
QList<QSize> QPixmapIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state) QList<QSize> QPixmapIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state)
@ -396,34 +407,6 @@ static inline int findBySize(const QList<QImage> &images, const QSize &size)
return -1; return -1;
} }
// Convenience class providing a bool read() function.
namespace {
class ImageReader
{
public:
ImageReader(const QString &fileName) : m_reader(fileName), m_atEnd(false) {}
QByteArray format() const { return m_reader.format(); }
bool read(QImage *image)
{
if (m_atEnd)
return false;
*image = m_reader.read();
if (!image->size().isValid()) {
m_atEnd = true;
return false;
}
m_atEnd = !m_reader.jumpToNextImage();
return true;
}
private:
QImageReader m_reader;
bool m_atEnd;
};
} // namespace
void QPixmapIconEngine::addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state) void QPixmapIconEngine::addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state)
{ {
if (fileName.isEmpty()) if (fileName.isEmpty())

View File

@ -34,7 +34,7 @@ public:
delete engine; delete engine;
} }
qreal pixmapDevicePixelRatio(qreal displayDevicePixelRatio, const QSize &requestedSize, const QSize &actualSize); static qreal pixmapDevicePixelRatio(qreal displayDevicePixelRatio, const QSize &requestedSize, const QSize &actualSize);
QIconEngine *engine; QIconEngine *engine;
@ -89,7 +89,24 @@ public:
bool read(QDataStream &in) override; bool read(QDataStream &in) override;
bool write(QDataStream &out) const override; bool write(QDataStream &out) const override;
static inline QSize adjustSize(const QSize &expectedSize, QSize size)
{
if (!size.isNull() && (size.width() > expectedSize.width() || size.height() > expectedSize.height()))
size.scale(expectedSize, Qt::KeepAspectRatio);
return size;
}
private: private:
void removePixmapEntry(QPixmapIconEngineEntry *pe)
{
auto idx = pixmaps.size();
while (--idx >= 0) {
if (pe == &pixmaps.at(idx)) {
pixmaps.remove(idx);
return;
}
}
}
QPixmapIconEngineEntry *tryMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state); QPixmapIconEngineEntry *tryMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state);
QList<QPixmapIconEngineEntry> pixmaps; QList<QPixmapIconEngineEntry> pixmaps;

View File

@ -894,18 +894,15 @@ QPixmap PixmapEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State st
if (basePixmap.isNull()) if (basePixmap.isNull())
basePixmap.load(filename); basePixmap.load(filename);
QSize actualSize = basePixmap.size();
// If the size of the best match we have (basePixmap) is larger than the // If the size of the best match we have (basePixmap) is larger than the
// requested size, we downscale it to match. // requested size, we downscale it to match.
if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height())) const auto actualSize = QPixmapIconEngine::adjustSize(size, basePixmap.size());
actualSize.scale(size, Qt::KeepAspectRatio);
QString key = "$qt_theme_"_L1 QString key = "$qt_theme_"_L1
% HexString<qint64>(basePixmap.cacheKey()) % HexString<quint64>(basePixmap.cacheKey())
% HexString<int>(mode) % HexString<quint8>(mode)
% HexString<qint64>(QGuiApplication::palette().cacheKey()) % HexString<quint64>(QGuiApplication::palette().cacheKey())
% HexString<int>(actualSize.width()) % HexString<uint>(actualSize.width())
% HexString<int>(actualSize.height()); % HexString<uint>(actualSize.height());
QPixmap cachedPixmap; QPixmap cachedPixmap;
if (QPixmapCache::find(key, &cachedPixmap)) { if (QPixmapCache::find(key, &cachedPixmap)) {