summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/doc/qtgui.qdocconf3
-rw-r--r--src/gui/doc/src/external-resources.qdoc12
-rw-r--r--src/gui/doc/src/includes/qiconengine-virtualhookhelper.qdocinc3
-rw-r--r--src/gui/image/qicon.cpp52
-rw-r--r--src/gui/image/qiconengine.cpp102
-rw-r--r--src/gui/image/qiconengine.h12
-rw-r--r--src/gui/image/qiconloader.cpp48
-rw-r--r--src/gui/image/qiconloader_p.h5
-rw-r--r--tests/auto/gui/image/image.pro1
-rw-r--r--tests/auto/gui/image/qiconhighdpi/icons/testtheme/16x16/actions/appointment-new.pngbin0 -> 897 bytes
-rw-r--r--tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22/actions/appointment-new.pngbin0 -> 1411 bytes
-rw-r--r--tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22@2/actions/appointment-new.pngbin0 -> 1740 bytes
-rw-r--r--tests/auto/gui/image/qiconhighdpi/icons/testtheme/index.theme21
-rw-r--r--tests/auto/gui/image/qiconhighdpi/qiconhighdpi.pro8
-rw-r--r--tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.cpp195
-rw-r--r--tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.qrc8
16 files changed, 443 insertions, 27 deletions
diff --git a/src/gui/doc/qtgui.qdocconf b/src/gui/doc/qtgui.qdocconf
index b07d39fa37..94574a314c 100644
--- a/src/gui/doc/qtgui.qdocconf
+++ b/src/gui/doc/qtgui.qdocconf
@@ -44,7 +44,8 @@ depends += \
headerdirs += ..
sourcedirs += .. \
- ../../../examples/gui/doc/src
+ ../../../examples/gui/doc/src \
+ src/includes
exampledirs += ../../../examples/gui \
snippets
diff --git a/src/gui/doc/src/external-resources.qdoc b/src/gui/doc/src/external-resources.qdoc
index 6a423323ea..480a4057be 100644
--- a/src/gui/doc/src/external-resources.qdoc
+++ b/src/gui/doc/src/external-resources.qdoc
@@ -49,4 +49,14 @@
/*!
\externalpage http://www.opengl.org/wiki/Tessellation_Shader
\title OpenGL Tessellation Shaders
-*/ \ No newline at end of file
+*/
+
+/*!
+ \externalpage https://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
+ \title Icon Theme Specification
+*/
+
+/*!
+ \externalpage https://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#directory_layout
+ \title Icon Theme Specification - Directory Layout
+*/
diff --git a/src/gui/doc/src/includes/qiconengine-virtualhookhelper.qdocinc b/src/gui/doc/src/includes/qiconengine-virtualhookhelper.qdocinc
new file mode 100644
index 0000000000..b17d2bd66f
--- /dev/null
+++ b/src/gui/doc/src/includes/qiconengine-virtualhookhelper.qdocinc
@@ -0,0 +1,3 @@
+\note This is a helper method and the actual work is done by the
+virtual_hook() method, hence this method depends on icon engine support
+and may not work with all icon engines.
diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp
index e8f2c878c8..17734f05f3 100644
--- a/src/gui/image/qicon.cpp
+++ b/src/gui/image/qicon.cpp
@@ -605,6 +605,51 @@ QFactoryLoader *qt_iconEngineFactoryLoader()
\note QIcon needs a QGuiApplication instance before the icon is created.
+ \section1 High DPI Icons
+
+ There are two ways that QIcon supports \l {High DPI Displays}{high DPI}
+ icons: via \l addFile() and \l fromTheme().
+
+ \l addFile() is useful if you have your own custom directory structure and do
+ not need to use the \l {Icon Theme Specification}{freedesktop.org Icon Theme
+ Specification}. Icons created via this approach use Qt's \l {High Resolution
+ Versions of Images}{"@nx" high DPI syntax}.
+
+ Using \l fromTheme() is necessary if you plan on following the Icon Theme
+ Specification. To make QIcon use the high DPI version of an image, add an
+ additional entry to the appropriate \c index.theme file:
+
+ \badcode
+ [Icon Theme]
+ Name=Test
+ Comment=Test Theme
+
+ Directories=32x32/actions,32x32@2/actions
+
+ [32x32/actions]
+ Size=32
+ Context=Actions
+ Type=Fixed
+
+ # High DPI version of the entry above.
+ [32x32@2/actions]
+ Size=32
+ Scale=2
+ Type=Fixed
+ \endcode
+
+ Your icon theme directory would then look something like this:
+
+ \badcode
+ ├── 32x32
+ │ └── actions
+ │ └── appointment-new.png
+ ├── 32x32@2
+ │ └── actions
+ │ └── appointment-new.png
+ └── index.theme
+ \endcode
+
\sa {fowler}{GUI Design Handbook: Iconic Label}, {Icons Example}
*/
@@ -847,9 +892,10 @@ QPixmap QIcon::pixmap(QWindow *window, const QSize &size, Mode mode, State state
}
// Try get a pixmap that is big enough to be displayed at device pixel resolution.
- QPixmap pixmap = d->engine->pixmap(size * devicePixelRatio, mode, state);
- pixmap.setDevicePixelRatio(d->pixmapDevicePixelRatio(devicePixelRatio, size, pixmap.size()));
- return pixmap;
+ QIconEngine::ScaledPixmapArgument scalePixmapArg = { size * devicePixelRatio, mode, state, devicePixelRatio, QPixmap() };
+ d->engine->virtual_hook(QIconEngine::ScaledPixmapHook, reinterpret_cast<void*>(&scalePixmapArg));
+ scalePixmapArg.pixmap.setDevicePixelRatio(d->pixmapDevicePixelRatio(devicePixelRatio, size, scalePixmapArg.pixmap.size()));
+ return scalePixmapArg.pixmap;
}
/*!
diff --git a/src/gui/image/qiconengine.cpp b/src/gui/image/qiconengine.cpp
index 0ba9844f7a..1f8e5f321a 100644
--- a/src/gui/image/qiconengine.cpp
+++ b/src/gui/image/qiconengine.cpp
@@ -169,6 +169,12 @@ void QIconEngine::addFile(const QString &/*fileName*/, const QSize &/*size*/, QI
bool that can be set to true if the icon is null. This enum value
was added in Qt 5.7.
+ \value ScaledPixmapHook Provides a way to get a pixmap that is scaled
+ according to the given scale (typically equal to the \l {Glossary Of High
+ DPI Terms}{device pixel ratio}). The \a data argument of the virtual_hook()
+ function is a \l ScaledPixmapArgument pointer that contains both the input and
+ output arguments. This enum value was added in Qt 5.9.
+
\sa virtual_hook()
*/
@@ -207,6 +213,60 @@ void QIconEngine::addFile(const QString &/*fileName*/, const QSize &/*size*/, QI
vectorial format normally return an empty list.
*/
+/*!
+ \class QIconEngine::ScaledPixmapArgument
+ \since 5.9
+
+ \inmodule QtGui
+
+ This struct represents arguments to the virtual_hook() function when
+ the \a id parameter is QIconEngine::ScaledPixmapHook.
+
+ The struct provides a way for icons created via \l QIcon::fromTheme()
+ to return pixmaps that are designed for the current \l {Glossary Of High
+ DPI Terms}{device pixel ratio}. The scale for such an icon is specified
+ using the \l {Icon Theme Specification - Directory Layout}{Scale directory key}
+ in the appropriate \c index.theme file.
+
+ Icons created via other approaches will return the same result as a call to
+ \l pixmap() would, and continue to benefit from Qt's \l {High Resolution
+ Versions of Images}{"@nx" high DPI syntax}.
+
+ \sa virtual_hook(), QIconEngine::IconEngineHook, {High DPI Icons}
+ */
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::size
+ \brief The requested size of the pixmap.
+*/
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::mode
+ \brief The requested mode of the pixmap.
+
+ \sa QIcon::Mode
+*/
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::state
+ \brief The requested state of the pixmap.
+
+ \sa QIcon::State
+*/
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::scale
+ \brief The requested scale of the pixmap.
+*/
+
+/*!
+ \variable QIconEngine::ScaledPixmapArgument::pixmap
+
+ \brief The pixmap that is the best match for the given \l size, \l mode, \l
+ \state, and \l scale. This is an output parameter that is set after calling
+ \l virtual_hook().
+*/
+
/*!
Returns a key that identifies this icon engine.
@@ -262,6 +322,13 @@ void QIconEngine::virtual_hook(int id, void *data)
arg.sizes.clear();
break;
}
+ case QIconEngine::ScaledPixmapHook: {
+ // We don't have any notion of scale besides "@nx", so just call pixmap() here.
+ QIconEngine::ScaledPixmapArgument &arg =
+ *reinterpret_cast<QIconEngine::ScaledPixmapArgument*>(data);
+ arg.pixmap = pixmap(arg.size, arg.mode, arg.state);
+ break;
+ }
default:
break;
}
@@ -273,9 +340,7 @@ void QIconEngine::virtual_hook(int id, void *data)
Returns sizes of all images that are contained in the engine for the
specific \a mode and \a state.
- \note This is a helper method and the actual work is done by
- virtual_hook() method, hence this method depends on icon engine support
- and may not work with all icon engines.
+ \include qiconengine-virtualhookhelper.qdocinc
*/
QList<QSize> QIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state) const
{
@@ -291,9 +356,7 @@ QList<QSize> QIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state) c
Returns the name used to create the engine, if available.
- \note This is a helper method and the actual work is done by
- virtual_hook() method, hence this method depends on icon engine support
- and may not work with all icon engines.
+ \include qiconengine-virtualhookhelper.qdocinc
*/
QString QIconEngine::iconName() const
{
@@ -306,6 +369,8 @@ QString QIconEngine::iconName() const
\since 5.7
Returns true if this icon engine represent a null QIcon.
+
+ \include qiconengine-virtualhookhelper.qdocinc
*/
bool QIconEngine::isNull() const
{
@@ -314,4 +379,29 @@ bool QIconEngine::isNull() const
return isNull;
}
+/*!
+ \since 5.9
+
+ Returns a pixmap for the given \a size, \a mode, \a state and \a scale.
+
+ The \a scale argument is typically equal to the \l {Glossary Of High DPI
+ Terms}{device pixel ratio} of the display.
+
+ \include qiconengine-virtualhookhelper.qdocinc
+
+ \note Some engines may cast \a scale to an integer.
+
+ \sa ScaledPixmapArgument
+*/
+QPixmap QIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
+{
+ ScaledPixmapArgument arg;
+ arg.size = size;
+ arg.mode = mode;
+ arg.state = state;
+ arg.scale = scale;
+ const_cast<QIconEngine *>(this)->virtual_hook(QIconEngine::ScaledPixmapHook, reinterpret_cast<void*>(&arg));
+ return arg.pixmap;
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/image/qiconengine.h b/src/gui/image/qiconengine.h
index 783770cd30..0c67ef2686 100644
--- a/src/gui/image/qiconengine.h
+++ b/src/gui/image/qiconengine.h
@@ -65,7 +65,7 @@ public:
virtual bool read(QDataStream &in);
virtual bool write(QDataStream &out) const;
- enum IconEngineHook { AvailableSizesHook = 1, IconNameHook, IsNullHook };
+ enum IconEngineHook { AvailableSizesHook = 1, IconNameHook, IsNullHook, ScaledPixmapHook };
struct AvailableSizesArgument
{
@@ -79,6 +79,16 @@ public:
virtual QString iconName() const;
bool isNull() const; // ### Qt6 make virtual
+ QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale); // ### Qt6 make virtual
+
+ struct ScaledPixmapArgument
+ {
+ QSize size;
+ QIcon::Mode mode;
+ QIcon::State state;
+ qreal scale;
+ QPixmap pixmap;
+ };
virtual void virtual_hook(int id, void *data);
diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp
index 324f13a17b..d72c05a3c5 100644
--- a/src/gui/image/qiconloader.cpp
+++ b/src/gui/image/qiconloader.cpp
@@ -47,6 +47,7 @@
#include <qpa/qplatformtheme.h>
#include <QtGui/QIconEngine>
#include <QtGui/QPalette>
+#include <QtCore/qmath.h>
#include <QtCore/QList>
#include <QtCore/QDir>
#include <QtCore/QSettings>
@@ -347,6 +348,10 @@ QIconTheme::QIconTheme(const QString &themeName)
dirInfo.maxSize = indexReader.value(directoryKey +
QLatin1String("/MaxSize"),
size).toInt();
+
+ dirInfo.scale = indexReader.value(directoryKey +
+ QLatin1String("/Scale"),
+ 1).toInt();
m_keyList.append(dirInfo);
}
}
@@ -553,8 +558,11 @@ void QIconLoaderEngine::paint(QPainter *painter, const QRect &rect,
* This algorithm is defined by the freedesktop spec:
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
*/
-static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize)
+static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize, int iconscale)
{
+ if (dir.scale != iconscale)
+ return false;
+
if (dir.type == QIconDirInfo::Fixed) {
return dir.size == iconsize;
@@ -575,24 +583,25 @@ static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize)
* This algorithm is defined by the freedesktop spec:
* http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
*/
-static int directorySizeDistance(const QIconDirInfo &dir, int iconsize)
+static int directorySizeDistance(const QIconDirInfo &dir, int iconsize, int iconscale)
{
+ const int scaledIconSize = iconsize * iconscale;
if (dir.type == QIconDirInfo::Fixed) {
- return qAbs(dir.size - iconsize);
+ return qAbs(dir.size * dir.scale - scaledIconSize);
} else if (dir.type == QIconDirInfo::Scalable) {
- if (iconsize < dir.minSize)
- return dir.minSize - iconsize;
- else if (iconsize > dir.maxSize)
- return iconsize - dir.maxSize;
+ if (scaledIconSize < dir.minSize * dir.scale)
+ return dir.minSize * dir.scale - scaledIconSize;
+ else if (scaledIconSize > dir.maxSize * dir.scale)
+ return scaledIconSize - dir.maxSize * dir.scale;
else
return 0;
} else if (dir.type == QIconDirInfo::Threshold) {
- if (iconsize < dir.size - dir.threshold)
- return dir.minSize - iconsize;
- else if (iconsize > dir.size + dir.threshold)
- return iconsize - dir.maxSize;
+ if (scaledIconSize < (dir.size - dir.threshold) * dir.scale)
+ return dir.minSize * dir.scale - scaledIconSize;
+ else if (scaledIconSize > (dir.size + dir.threshold) * dir.scale)
+ return scaledIconSize - dir.maxSize * dir.scale;
else return 0;
}
@@ -600,7 +609,7 @@ static int directorySizeDistance(const QIconDirInfo &dir, int iconsize)
return INT_MAX;
}
-QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size)
+QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size, int scale)
{
int iconsize = qMin(size.width(), size.height());
@@ -612,7 +621,7 @@ QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size)
// Search for exact matches first
for (int i = 0; i < numEntries; ++i) {
QIconLoaderEngineEntry *entry = m_info.entries.at(i);
- if (directoryMatchesSize(entry->dir, iconsize)) {
+ if (directoryMatchesSize(entry->dir, iconsize, scale)) {
return entry;
}
}
@@ -622,7 +631,7 @@ QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size)
QIconLoaderEngineEntry *closestMatch = 0;
for (int i = 0; i < numEntries; ++i) {
QIconLoaderEngineEntry *entry = m_info.entries.at(i);
- int distance = directorySizeDistance(entry->dir, iconsize);
+ int distance = directorySizeDistance(entry->dir, iconsize, scale);
if (distance < minimalSize) {
minimalSize = distance;
closestMatch = entry;
@@ -665,6 +674,8 @@ QPixmap PixmapEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State st
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);
@@ -748,6 +759,15 @@ void QIconLoaderEngine::virtual_hook(int id, void *data)
*reinterpret_cast<bool*>(data) = m_info.entries.isEmpty();
}
break;
+ case QIconEngine::ScaledPixmapHook:
+ {
+ QIconEngine::ScaledPixmapArgument &arg = *reinterpret_cast<QIconEngine::ScaledPixmapArgument*>(data);
+ // QIcon::pixmap() multiplies size by the device pixel ratio.
+ const int integerScale = qCeil(arg.scale);
+ QIconLoaderEngineEntry *entry = entryForSize(arg.size / integerScale, integerScale);
+ arg.pixmap = entry ? entry->pixmap(arg.size, arg.mode, arg.state) : QPixmap();
+ }
+ break;
default:
QIconEngine::virtual_hook(id, data);
}
diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h
index ed7b7ff7ae..5f3a3ef948 100644
--- a/src/gui/image/qiconloader_p.h
+++ b/src/gui/image/qiconloader_p.h
@@ -76,12 +76,14 @@ struct QIconDirInfo
maxSize(0),
minSize(0),
threshold(0),
+ scale(1),
type(Threshold) {}
QString path;
short size;
short maxSize;
short minSize;
short threshold;
+ short scale;
Type type;
};
Q_DECLARE_TYPEINFO(QIconDirInfo, Q_MOVABLE_TYPE);
@@ -135,7 +137,8 @@ private:
bool hasIcon() const;
void ensureLoaded();
void virtual_hook(int id, void *data) Q_DECL_OVERRIDE;
- QIconLoaderEngineEntry *entryForSize(const QSize &size);
+ QIconLoaderEngineEntry *entryForSize(const QSize &size, int scale = 1);
+
QIconLoaderEngine(const QIconLoaderEngine &other);
QThemeIconInfo m_info;
QString m_iconName;
diff --git a/tests/auto/gui/image/image.pro b/tests/auto/gui/image/image.pro
index 5a74df4c78..f4e2ab20ba 100644
--- a/tests/auto/gui/image/image.pro
+++ b/tests/auto/gui/image/image.pro
@@ -10,6 +10,7 @@ SUBDIRS=\
qmovie \
qpicture \
qicon \
+ qiconhighdpi
!qtHaveModule(network): SUBDIRS -= \
qimagereader
diff --git a/tests/auto/gui/image/qiconhighdpi/icons/testtheme/16x16/actions/appointment-new.png b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/16x16/actions/appointment-new.png
new file mode 100644
index 0000000000..18b7c6781e
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/16x16/actions/appointment-new.png
Binary files differ
diff --git a/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22/actions/appointment-new.png b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22/actions/appointment-new.png
new file mode 100644
index 0000000000..d676ffd463
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22/actions/appointment-new.png
Binary files differ
diff --git a/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22@2/actions/appointment-new.png b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22@2/actions/appointment-new.png
new file mode 100644
index 0000000000..6d094d7b54
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/22x22@2/actions/appointment-new.png
Binary files differ
diff --git a/tests/auto/gui/image/qiconhighdpi/icons/testtheme/index.theme b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/index.theme
new file mode 100644
index 0000000000..6ab6c15c42
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/icons/testtheme/index.theme
@@ -0,0 +1,21 @@
+[Icon Theme]
+Name=Test
+Comment=Test Theme
+
+Directories=16x16/actions,22x22/actions,22x22@2/actions
+
+[16x16/actions]
+Size=16
+Context=Actions
+Type=Fixed
+
+[22x22/actions]
+Size=22
+Context=Actions
+Type=Fixed
+
+[22x22@2/actions]
+Size=22
+Context=Actions
+Scale=2
+Type=Fixed
diff --git a/tests/auto/gui/image/qiconhighdpi/qiconhighdpi.pro b/tests/auto/gui/image/qiconhighdpi/qiconhighdpi.pro
new file mode 100644
index 0000000000..17553158bc
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/qiconhighdpi.pro
@@ -0,0 +1,8 @@
+CONFIG += testcase
+TARGET = tst_qicon
+
+QT += testlib
+SOURCES += tst_qiconhighdpi.cpp
+RESOURCES = tst_qiconhighdpi.qrc
+
+TESTDATA += icons/*
diff --git a/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.cpp b/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.cpp
new file mode 100644
index 0000000000..ce7f68a0a6
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <qicon.h>
+
+class tst_QIconHighDpi : public QObject
+{
+ Q_OBJECT
+public:
+ tst_QIconHighDpi();
+
+private slots:
+ void initTestCase();
+ void fromTheme_data();
+ void fromTheme();
+};
+
+tst_QIconHighDpi::tst_QIconHighDpi()
+{
+}
+
+void tst_QIconHighDpi::initTestCase()
+{
+}
+
+void tst_QIconHighDpi::fromTheme_data()
+{
+ QTest::addColumn<int>("requestedSize");
+ QTest::addColumn<int>("expectedSize");
+ QTest::addColumn<qreal>("expectedDpr");
+
+ // The pixmaps that we have available can be found in tst_qiconhighdpi.qrc.
+ // Currently we only have @1 and @2 icons available.
+ const int dpr = qCeil(qApp->devicePixelRatio());
+
+ // We have an @2 high DPI version of the 22x22 size of this icon.
+ switch (dpr) {
+ case 1: QTest::newRow("22x22,dpr=1") << 22 << 22 << 1.0; break;
+ case 2: QTest::newRow("22x22,dpr=2") << 22 << 44 << 2.0; break;
+ case 3: QTest::newRow("22x22,dpr=3") << 22 << 44 << 2.0; break;
+ }
+
+ // We don't have a high DPI version of the 16x16 size of this icon,
+ // so directoryMatchesSize() will return false for all directories.
+ // directorySizeDistance() is then called to find the best match.
+ // The table below illustrates the results for our available images at various DPRs:
+ // Available size | Available scale | Requested size | Requested scale | Distance
+ // 22 * 2 - 16 * 1 = 28
+ // 22 * 1 - 16 * 1 = 6
+ // 16 * 1 - 16 * 1 = 0 < (16x16)
+ // Available size | Available scale | Requested size | Requested scale | Distance
+ // 22 * 2 - 16 * 2 = 12
+ // 22 * 1 - 16 * 2 = 10 < (22x22)
+ // 16 * 1 - 16 * 2 = 16
+ // Available size | Available scale | Requested size | Requested scale | Distance
+ // 22 * 2 - 16 * 3 = 4 < (22x22@2)
+ // 22 * 1 - 16 * 3 = 26
+ // 16 * 1 - 16 * 3 = 32
+ // Both of these functions are implementations of the freedesktop icon theme spec,
+ // which dictates that if there is no matching scale, directorySizeDistance() determines
+ // the winner, regardless of whether or not the scale is too low for the requested scale.
+ switch (dpr) {
+ case 1: QTest::newRow("16x16,dpr=1") << 16 << 16 << 1.0; break;
+ // PixmapEntry::pixmap() will only downscale the pixmap if actualSize.width() > size.width().
+ // In this case, 22 > 32 is false, so a 22x22 pixmap is returned.
+ case 2: QTest::newRow("16x16,dpr=2") << 16 << 22 << 1.375; break;
+ case 3: QTest::newRow("16x16,dpr=3") << 16 << 44 << 2.75; break;
+ }
+
+ // We don't have an 8x8 size of this icon, so:
+ // Available size | Available scale | Requested size | Requested scale | Distance
+ // 22 * 2 - 8 * 1 = 36
+ // 22 * 1 - 8 * 1 = 14
+ // 16 * 1 - 8 * 1 = 8 < (16x16)
+ // Available size | Available scale | Requested size | Requested scale | Distance
+ // 22 * 2 - 8 * 2 = 28
+ // 22 * 1 - 8 * 2 = 6
+ // 16 * 1 - 8 * 2 = 0 < (16x16)
+ // Available size | Available scale | Requested size | Requested scale | Distance
+ // 22 * 2 - 8 * 3 = 20
+ // 22 * 1 - 8 * 3 = 2 < (22x22)
+ // 16 * 1 - 8 * 3 = 8
+ switch (dpr) {
+ case 1: QTest::newRow("8x8,dpr=1") << 8 << 8 << 1.0; break;
+ case 2: QTest::newRow("8x8,dpr=2") << 8 << 16 << 2.0; break;
+ case 3: QTest::newRow("8x8,dpr=3") << 8 << 22 << 2.75; break;
+ }
+
+ // We don't have a 44x44 size of this icon, so:
+ // Available size | Available scale | Requested size | Requested scale | Distance
+ // 22 * 2 - 44 * 1 = 0 < (22x22@2)
+ // 22 * 1 - 44 * 1 = 22
+ // 16 * 1 - 44 * 1 = 28
+ // Available size | Available scale | Requested size | Requested scale | Distance
+ // 22 * 2 - 44 * 2 = 44 < (22x22@2)
+ // 22 * 1 - 44 * 2 = 66
+ // 16 * 1 - 44 * 2 = 72
+ // Available size | Available scale | Requested size | Requested scale | Distance
+ // 22 * 2 - 44 * 3 = 88 < (22x22@2)
+ // 22 * 1 - 44 * 3 = 110
+ // 16 * 1 - 44 * 3 = 116
+ switch (dpr) {
+ case 1: QTest::newRow("44x44,dpr=1") << 44 << 44 << 1.0; break;
+ case 2: QTest::newRow("44x44,dpr=2") << 44 << 44 << 1.0; break;
+ case 3: QTest::newRow("44x44,dpr=3") << 44 << 44 << 1.0; break;
+ }
+
+ // We don't have a 20x20 size of this icon, so:
+ // Available size | Available scale | Requested size | Requested scale | Distance
+ // 22 * 2 - 20 * 1 = 24
+ // 22 * 1 - 20 * 1 = 2 < (22x22)
+ // 16 * 1 - 20 * 1 = 4
+ // Available size | Available scale | Requested size | Requested scale | Distance
+ // 22 * 2 - 20 * 2 = 4 < (22x22@2)
+ // 22 * 1 - 20 * 2 = 18
+ // 16 * 1 - 20 * 2 = 24
+ // Available size | Available scale | Requested size | Requested scale | Distance
+ // 22 * 2 - 20 * 3 = 16 < (22x22@2)
+ // 22 * 1 - 20 * 3 = 38
+ // 16 * 1 - 20 * 3 = 44
+ switch (dpr) {
+ case 1: QTest::newRow("20x20,dpr=1") << 20 << 20 << 1.0; break;
+ // PixmapEntry::pixmap() will only downscale the pixmap if actualSize.width() > size.width().
+ // In this case, 44 > 40 is true, so the 44x44 pixmap is downscaled to 40x40.
+ case 2: QTest::newRow("20x20,dpr=2") << 20 << 40 << 2.0; break;
+ case 3: QTest::newRow("20x20,dpr=3") << 20 << 44 << 2.2; break;
+ }
+}
+
+void tst_QIconHighDpi::fromTheme()
+{
+ QFETCH(int, requestedSize);
+ QFETCH(int, expectedSize);
+ QFETCH(qreal, expectedDpr);
+
+ QString searchPath = QLatin1String(":/icons");
+ QIcon::setThemeSearchPaths(QStringList() << searchPath);
+ QCOMPARE(QIcon::themeSearchPaths().size(), 1);
+ QCOMPARE(searchPath, QIcon::themeSearchPaths()[0]);
+
+ QString themeName("testtheme");
+ QIcon::setThemeName(themeName);
+ QCOMPARE(QIcon::themeName(), themeName);
+
+ QIcon appointmentIcon = QIcon::fromTheme("appointment-new");
+ QVERIFY(!appointmentIcon.isNull());
+ QVERIFY(!appointmentIcon.availableSizes(QIcon::Normal, QIcon::Off).isEmpty());
+ QVERIFY(appointmentIcon.availableSizes().contains(QSize(16, 16)));
+ QVERIFY(appointmentIcon.availableSizes().contains(QSize(22, 22)));
+
+ const QPixmap pixmap = appointmentIcon.pixmap(requestedSize);
+ QCOMPARE(pixmap.size(), QSize(expectedSize, expectedSize));
+ // We should get the high DPI version of an image if it exists in the correct directory.
+ // Note that we didn't pass the QWindow to QIcon::pixmap(),
+ // because QGuiApplication::devicePixelRatio() will be used if no window was given.
+ QCOMPARE(pixmap.devicePixelRatio(), expectedDpr);
+}
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
+ QGuiApplication app(argc, argv);
+ Q_UNUSED(app);
+ tst_QIconHighDpi test;
+ QTEST_SET_MAIN_SOURCE_PATH
+ return QTest::qExec(&test, argc, argv);
+}
+
+#include "tst_qiconhighdpi.moc"
diff --git a/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.qrc b/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.qrc
new file mode 100644
index 0000000000..80b5e38ee6
--- /dev/null
+++ b/tests/auto/gui/image/qiconhighdpi/tst_qiconhighdpi.qrc
@@ -0,0 +1,8 @@
+<RCC>
+ <qresource prefix="/">
+ <file>icons/testtheme/16x16/actions/appointment-new.png</file>
+ <file>icons/testtheme/22x22/actions/appointment-new.png</file>
+ <file>icons/testtheme/index.theme</file>
+ <file>icons/testtheme/22x22@2/actions/appointment-new.png</file>
+ </qresource>
+</RCC>