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
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
@ -150,7 +177,7 @@ void QPixmapIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode
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.
// 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;
}
int s = area(size);
qint64 s = area(size);
if (pa->size == QSize() && pa->pixmap.isNull()) {
pa->pixmap = QPixmap(pa->fileName);
pa->size = pa->pixmap.size();
}
int a = area(pa->size);
qint64 a = area(pa->size);
if (pb->size == QSize() && pb->pixmap.isNull()) {
pb->pixmap = QPixmap(pb->fileName);
pb->size = pb->pixmap.size();
}
int b = area(pb->size);
int res = a;
qint64 b = area(pb->size);
qint64 res = a;
if (qMin(a,b) >= s)
res = qMin(a,b);
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 pm;
QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state, false);
if (pe)
pm = pe->pixmap;
if (pm.isNull()) {
auto idx = pixmaps.size();
while (--idx >= 0) {
if (pe == &pixmaps.at(idx)) {
pixmaps.remove(idx);
break;
}
}
removePixmapEntry(pe);
if (pixmaps.isEmpty())
return pm;
else
return pixmap(size, mode, state);
return scaledPixmap(size, mode, state, scale);
}
QSize actualSize = pm.size();
if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
actualSize.scale(size, Qt::KeepAspectRatio);
const auto actualSize = adjustSize(size, pm.size());
QString key = "qt_"_L1
% HexString<quint64>(pm.cacheKey())
% HexString<uint>(pe ? pe->mode : QIcon::Normal)
% HexString<quint8>(pe ? pe->mode : QIcon::Normal)
% HexString<quint64>(QGuiApplication::palette().cacheKey())
% HexString<uint>(actualSize.width())
% HexString<uint>(actualSize.height());
if (mode == QIcon::Active) {
if (QPixmapCache::find(key % HexString<uint>(mode), &pm))
if (QPixmapCache::find(key % HexString<quint8>(mode), &pm))
return pm; // horray
if (QPixmapCache::find(key % HexString<uint>(QIcon::Normal), &pm)) {
if (QPixmapCache::find(key % HexString<quint8>(QIcon::Normal), &pm)) {
QPixmap active = pm;
if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp))
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)
pm = pm.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
if (pe->mode != mode && mode != QIcon::Normal) {
@ -326,7 +342,7 @@ QPixmap QPixmapIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIc
if (!generated.isNull())
pm = generated;
}
QPixmapCache::insert(key % HexString<uint>(mode), pm);
QPixmapCache::insert(key % HexString<quint8>(mode), 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))
actualSize = pe->size;
if (actualSize.isNull())
return actualSize;
if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
actualSize.scale(size, Qt::KeepAspectRatio);
return actualSize;
return adjustSize(size, actualSize);
}
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;
}
// 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)
{
if (fileName.isEmpty())

View File

@ -34,7 +34,7 @@ public:
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;
@ -89,7 +89,24 @@ public:
bool read(QDataStream &in) 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:
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);
QList<QPixmapIconEngineEntry> pixmaps;

View File

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