summaryrefslogtreecommitdiffstats
path: root/src/gui/image
diff options
context:
space:
mode:
authorMorten Johan Sørvig <morten.sorvig@qt.io>2020-08-12 15:19:54 +0200
committerMorten Johan Sørvig <morten.sorvig@qt.io>2020-11-08 19:49:53 +0100
commit46a552583f99ed04945ccf949afbdff58dcdfa1f (patch)
treef194720cc63b5b282edd8db85523e884dbbc5e28 /src/gui/image
parente6cba05b6623d96278ac042b50eaba1c0cd77ddb (diff)
Teach QPixmapIconEngine how to handle @Nx pixmaps
Bring QPixmapIconEngine on par with QIconLoaderEngine when it comes to @Nx pixmap handling: Make the scale factor a test parameter during icon lookup. This allows storing e.g 16x16@1, 16x16@2, 16x16@3 versions of a pixmap in the icon, and then having QIcon select the correct one based on the target devicePixelRatio. Extend the qiconhighdpi test to also cover QPixmapIconEngine, via the addPixmap() API. The corner cases of pixmap lookup can be much complicated. QIconLoaderEngine and QPixmapIconEngine should ideally have identical behavior in order to avoid surprises. Change-Id: I17552cc61755bff9553c4a462e3983ac6759c13b Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Diffstat (limited to 'src/gui/image')
-rw-r--r--src/gui/image/qicon.cpp81
-rw-r--r--src/gui/image/qicon_p.h18
2 files changed, 65 insertions, 34 deletions
diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp
index 73b25fd584..0bbc4005a0 100644
--- a/src/gui/image/qicon.cpp
+++ b/src/gui/image/qicon.cpp
@@ -180,9 +180,28 @@ void QPixmapIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode
static inline int area(const QSize &s) { return s.width() * s.height(); }
-// returns the smallest of the two that is still larger than or equal to size.
-static QPixmapIconEngineEntry *bestSizeMatch( const QSize &size, QPixmapIconEngineEntry *pa, QPixmapIconEngineEntry *pb)
-{
+// 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
+// used as fallbacks. We assume that the pixmap set is complete, in the sense
+// that no 2x pixmap is going to be a better match than a 3x pixmap for the the
+// target scale of 3 (It's OK if 3x pixmaps are missing - we'll fall back to
+// the 2x pixmaps then.)
+static QPixmapIconEngineEntry *bestSizeScaleMatch(const QSize &size, qreal scale, QPixmapIconEngineEntry *pa, QPixmapIconEngineEntry *pb)
+{
+
+ // scale: we can only differentiate on scale if the scale differs
+ if (pa->scale != pb->scale) {
+
+ // Score the pixmaps: 0 is an exact scale match, postive
+ // scores have more detail than requested, negative scores
+ // have less detail than rquested.
+ qreal ascore = pa->scale - scale;
+ qreal bscore = pb->scale - scale;
+
+ // Take the one closest to 0
+ return (qAbs(ascore) < qAbs(bscore)) ? pa : pb;
+ }
+
int s = area(size);
if (pa->size == QSize() && pa->pixmap.isNull()) {
pa->pixmap = QPixmap(pa->fileName);
@@ -204,13 +223,13 @@ static QPixmapIconEngineEntry *bestSizeMatch( const QSize &size, QPixmapIconEngi
return pb;
}
-QPixmapIconEngineEntry *QPixmapIconEngine::tryMatch(const QSize &size, QIcon::Mode mode, QIcon::State state)
+QPixmapIconEngineEntry *QPixmapIconEngine::tryMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state)
{
QPixmapIconEngineEntry *pe = nullptr;
for (int i = 0; i < pixmaps.count(); ++i)
if (pixmaps.at(i).mode == mode && pixmaps.at(i).state == state) {
if (pe)
- pe = bestSizeMatch(size, &pixmaps[i], pe);
+ pe = bestSizeScaleMatch(size, scale, &pixmaps[i], pe);
else
pe = &pixmaps[i];
}
@@ -218,42 +237,42 @@ QPixmapIconEngineEntry *QPixmapIconEngine::tryMatch(const QSize &size, QIcon::Mo
}
-QPixmapIconEngineEntry *QPixmapIconEngine::bestMatch(const QSize &size, QIcon::Mode mode, QIcon::State state, bool sizeOnly)
+QPixmapIconEngineEntry *QPixmapIconEngine::bestMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state, bool sizeOnly)
{
- QPixmapIconEngineEntry *pe = tryMatch(size, mode, state);
+ QPixmapIconEngineEntry *pe = tryMatch(size, scale, mode, state);
while (!pe){
QIcon::State oppositeState = (state == QIcon::On) ? QIcon::Off : QIcon::On;
if (mode == QIcon::Disabled || mode == QIcon::Selected) {
QIcon::Mode oppositeMode = (mode == QIcon::Disabled) ? QIcon::Selected : QIcon::Disabled;
- if ((pe = tryMatch(size, QIcon::Normal, state)))
+ if ((pe = tryMatch(size, scale, QIcon::Normal, state)))
break;
- if ((pe = tryMatch(size, QIcon::Active, state)))
+ if ((pe = tryMatch(size, scale, QIcon::Active, state)))
break;
- if ((pe = tryMatch(size, mode, oppositeState)))
+ if ((pe = tryMatch(size, scale, mode, oppositeState)))
break;
- if ((pe = tryMatch(size, QIcon::Normal, oppositeState)))
+ if ((pe = tryMatch(size, scale, QIcon::Normal, oppositeState)))
break;
- if ((pe = tryMatch(size, QIcon::Active, oppositeState)))
+ if ((pe = tryMatch(size, scale, QIcon::Active, oppositeState)))
break;
- if ((pe = tryMatch(size, oppositeMode, state)))
+ if ((pe = tryMatch(size, scale, oppositeMode, state)))
break;
- if ((pe = tryMatch(size, oppositeMode, oppositeState)))
+ if ((pe = tryMatch(size, scale, oppositeMode, oppositeState)))
break;
} else {
QIcon::Mode oppositeMode = (mode == QIcon::Normal) ? QIcon::Active : QIcon::Normal;
- if ((pe = tryMatch(size, oppositeMode, state)))
+ if ((pe = tryMatch(size, scale, oppositeMode, state)))
break;
- if ((pe = tryMatch(size, mode, oppositeState)))
+ if ((pe = tryMatch(size, scale, mode, oppositeState)))
break;
- if ((pe = tryMatch(size, oppositeMode, oppositeState)))
+ if ((pe = tryMatch(size, scale, oppositeMode, oppositeState)))
break;
- if ((pe = tryMatch(size, QIcon::Disabled, state)))
+ if ((pe = tryMatch(size, scale, QIcon::Disabled, state)))
break;
- if ((pe = tryMatch(size, QIcon::Selected, state)))
+ if ((pe = tryMatch(size, scale, QIcon::Selected, state)))
break;
- if ((pe = tryMatch(size, QIcon::Disabled, oppositeState)))
+ if ((pe = tryMatch(size, scale, QIcon::Disabled, oppositeState)))
break;
- if ((pe = tryMatch(size, QIcon::Selected, oppositeState)))
+ if ((pe = tryMatch(size, scale, QIcon::Selected, oppositeState)))
break;
}
@@ -272,8 +291,14 @@ QPixmapIconEngineEntry *QPixmapIconEngine::bestMatch(const QSize &size, QIcon::M
QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
{
+ return scaledPixmap(size, mode, state, 1.0);
+}
+
+QPixmap QPixmapIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
+{
+
QPixmap pm;
- QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, false);
+ QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state, false);
if (pe)
pm = pe->pixmap;
@@ -332,7 +357,13 @@ QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::St
QSize QPixmapIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
{
QSize actualSize;
- if (QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, true))
+
+ // The returned actiual size is the size in device independent pixels,
+ // so we limit the search to scale 1 and assume that e.g. @2x versions
+ // 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))
actualSize = pe->size;
if (actualSize.isNull())
@@ -361,8 +392,8 @@ QList<QSize> QPixmapIconEngine::availableSizes(QIcon::Mode mode, QIcon::State st
void QPixmapIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state)
{
if (!pixmap.isNull()) {
- QPixmapIconEngineEntry *pe = tryMatch(pixmap.size(), mode, state);
- if(pe && pe->size == pixmap.size()) {
+ QPixmapIconEngineEntry *pe = tryMatch(pixmap.size(), pixmap.devicePixelRatio(), mode, state);
+ if (pe && pe->size == pixmap.size() && pe->scale == pixmap.devicePixelRatio()) {
pe->pixmap = pixmap;
pe->fileName.clear();
} else {
diff --git a/src/gui/image/qicon_p.h b/src/gui/image/qicon_p.h
index 49b0ac0172..40cb0c7efa 100644
--- a/src/gui/image/qicon_p.h
+++ b/src/gui/image/qicon_p.h
@@ -83,15 +83,16 @@ public:
struct QPixmapIconEngineEntry
{
- QPixmapIconEngineEntry():mode(QIcon::Normal), state(QIcon::Off){}
+ QPixmapIconEngineEntry():scale(1), mode(QIcon::Normal), state(QIcon::Off){}
QPixmapIconEngineEntry(const QPixmap &pm, QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off)
- :pixmap(pm), size(pm.size()), mode(m), state(s){}
+ :pixmap(pm), size(pm.size()), scale(pm.devicePixelRatio()), mode(m), state(s){}
QPixmapIconEngineEntry(const QString &file, const QSize &sz = QSize(), QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off)
- :fileName(file), size(sz), mode(m), state(s){}
+ :fileName(file), size(sz), scale(1), mode(m), state(s){}
QPixmapIconEngineEntry(const QString &file, const QImage &image, QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off);
QPixmap pixmap;
QString fileName;
QSize size;
+ qreal scale;
QIcon::Mode mode;
QIcon::State state;
bool isNull() const {return (fileName.isEmpty() && pixmap.isNull()); }
@@ -99,12 +100,9 @@ struct QPixmapIconEngineEntry
Q_DECLARE_TYPEINFO(QPixmapIconEngineEntry, Q_MOVABLE_TYPE);
inline QPixmapIconEngineEntry::QPixmapIconEngineEntry(const QString &file, const QImage &image, QIcon::Mode m, QIcon::State s)
- : fileName(file), size(image.size()), mode(m), state(s)
+ : fileName(file), size(image.size()), scale(image.devicePixelRatio()), mode(m), state(s)
{
pixmap.convertFromImage(image);
- // Reset the devicePixelRatio. The pixmap may be loaded from a @2x file,
- // but be used as a 1x pixmap by QIcon.
- pixmap.setDevicePixelRatio(1.0);
}
class Q_GUI_EXPORT QPixmapIconEngine : public QIconEngine {
@@ -114,19 +112,21 @@ public:
~QPixmapIconEngine();
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;
- QPixmapIconEngineEntry *bestMatch(const QSize &size, QIcon::Mode mode, QIcon::State state, bool sizeOnly);
+ 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);
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;
void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state) override;
+
QString key() const override;
QIconEngine *clone() const override;
bool read(QDataStream &in) override;
bool write(QDataStream &out) const override;
private:
- QPixmapIconEngineEntry *tryMatch(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ QPixmapIconEngineEntry *tryMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state);
QList<QPixmapIconEngineEntry> pixmaps;
friend Q_GUI_EXPORT QDataStream &operator<<(QDataStream &s, const QIcon &icon);