From 6ec9b34cbb5fa6b1214cc6551002f46f732ba72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Fri, 1 Mar 2013 12:15:34 +0100 Subject: Make QIcon generate high-dpi pixmaps. QIcon::pixmap() can now return pixmaps that are larger than the requested size, This is a behavior change that may require updating existing application code, and is enabled by setting Qt::AA_UseHighDPIIamges. Add new pixmap() and actualSize() overloads that take a window pointer. For 100% correctness you want to target pixmap generation to a specific window, in case the system has a mix of normal and high-dpi displays. (The non-window versions uses QGuiApplication:: devicePixelRatio(), which returns the ratio for the "best" display on the system.) actualSize now returns the size in device-independent pixels - not the pixmap pixel size. This matches its intended use case which is layout calculations. Change-Id: I2eefc4ed4aa48b036a7019446694e56213070efa Reviewed-by: Gabriel de Dietrich --- src/gui/image/qicon.cpp | 120 +++++++++++++++++++++++++++++++++++++++++++++--- src/gui/image/qicon.h | 2 + src/gui/image/qicon_p.h | 2 + 3 files changed, 117 insertions(+), 7 deletions(-) diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp index 1b36fa257a..683fe51d2b 100644 --- a/src/gui/image/qicon.cpp +++ b/src/gui/image/qicon.cpp @@ -119,6 +119,25 @@ static void qt_cleanup_icon_cache() qtIconCache()->clear(); } +/*! \internal + + Returns the effective device pixel ratio, using + the provided window pointer if possible. + + if Qt::AA_UseHighDpiPixmaps is not set this function + returns 1.0 to keep non-hihdpi aware code working. +*/ +static qreal qt_effective_device_pixel_ratio(QWindow *window = 0) +{ + if (!qApp->testAttribute(Qt::AA_UseHighDpiPixmaps)) + return qreal(1.0); + + if (window) + return window->devicePixelRatio(); + + return qApp->devicePixelRatio(); // Don't know which window to target. +} + QIconPrivate::QIconPrivate() : engine(0), ref(1), serialNum(serialNumCounter.fetchAndAddRelaxed(1)), @@ -126,6 +145,25 @@ QIconPrivate::QIconPrivate() { } +/*! \internal + Computes the displayDevicePixelRatio for a pixmap. + + If displayDevicePixelRatio is 1.0 the reurned value is 1.0, always. + + For a displayDevicePixelRatio of 2.0 the returned value will be between + 1.0 and 2.0, depending on requestedSize and actualsize: + * If actualsize < requestedSize : 1.0 (not enough pixels for a normal-dpi pixmap) + * If actualsize == requestedSize * 2.0 : 2.0 (enough pixels for a high-dpi pixmap) + * else : a scaled value between 1.0 and 2.0. (pixel count is between normal-dpi and high-dpi) +*/ +qreal QIconPrivate::pixmapDevicePixelRatio(qreal displayDevicePixelRatio, const QSize &requestedSize, const QSize &actualSize) +{ + QSize targetSize = requestedSize * displayDevicePixelRatio; + qreal scale = 0.5 * (qreal(actualSize.width()) / qreal(targetSize.width()) + + qreal(actualSize.height() / qreal(targetSize.height()))); + return qMax(qreal(1.0), displayDevicePixelRatio *scale); +} + QPixmapIconEngine::QPixmapIconEngine() { } @@ -141,10 +179,8 @@ QPixmapIconEngine::~QPixmapIconEngine() void QPixmapIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) { - QSize pixmapSize = rect.size(); -#if defined(Q_WS_MAC) - pixmapSize *= qt_mac_get_scalefactor(); -#endif + QSize pixmapSize = rect.size() * qt_effective_device_pixel_ratio(0); + QPixmap px = pixmap(pixmapSize, mode, state); painter->drawPixmap(rect, pixmap(pixmapSize, mode, state)); } @@ -345,6 +381,9 @@ void QPixmapIconEngine::addFile(const QString &fileName, const QSize &_size, QIc } if (pe->size == QSize() && pe->pixmap.isNull()) { pe->pixmap = QPixmap(pe->fileName); + // Reset the devicePixelRatio. The pixmap may be loaded from a @2x file, + // but be used as a 1x pixmap by QIcon. + pe->pixmap.setDevicePixelRatio(1.0); pe->size = pe->pixmap.size(); } if(pe->size == size) { @@ -658,13 +697,17 @@ qint64 QIcon::cacheKey() const state, generating one if necessary. The pixmap might be smaller than requested, but never larger. + Setting the Qt::AA_UseHighDpiPixmaps application attribute enables this + function to return pixmaps that are larger than the requested size. Such + images will have a devicePixelRatio larger than 1. + \sa actualSize(), paint() */ QPixmap QIcon::pixmap(const QSize &size, Mode mode, State state) const { if (!d) return QPixmap(); - return d->engine->pixmap(size, mode, state); + return pixmap(0, size, mode, state); } /*! @@ -674,6 +717,10 @@ QPixmap QIcon::pixmap(const QSize &size, Mode mode, State state) const Returns a pixmap of size QSize(\a w, \a h). The pixmap might be smaller than requested, but never larger. + + Setting the Qt::AA_UseHighDpiPixmaps application attribute enables this + function to return pixmaps that are larger than the requested size. Such + images will have a devicePixelRatio larger than 1. */ /*! @@ -683,11 +730,16 @@ QPixmap QIcon::pixmap(const QSize &size, Mode mode, State state) const Returns a pixmap of size QSize(\a extent, \a extent). The pixmap might be smaller than requested, but never larger. + + Setting the Qt::AA_UseHighDpiPixmaps application attribute enables this + function to return pixmaps that are larger than the requested size. Such + images will have a devicePixelRatio larger than 1. */ /*! Returns the actual size of the icon for the requested \a size, \a mode, and \a state. The result might be smaller than requested, but - never larger. + never larger. The returned size is in device-independent pixels (This + is relevant for high-dpi pixmaps.) \sa pixmap(), paint() */ @@ -695,9 +747,63 @@ QSize QIcon::actualSize(const QSize &size, Mode mode, State state) const { if (!d) return QSize(); - return d->engine->actualSize(size, mode, state); + return actualSize(0, size, mode, state); } +/*! + \since 5.1 + + Returns a pixmap with the requested \a window \a size, \a mode, and \a + state, generating one if necessary. + + The pixmap can be smaller than the requested size. If \a window is on + a high-dpi display the pixmap can be larger. In that case it will have + a devicePixelRatio larger than 1. + + \sa actualSize(), paint() +*/ +QPixmap QIcon::pixmap(QWindow *window, const QSize &size, Mode mode, State state) const +{ + if (!d) + return QPixmap(); + + qreal devicePixelRatio = qt_effective_device_pixel_ratio(window); + + // Handle the simple normal-dpi case: + if (!(devicePixelRatio > 1.0)) + return d->engine->pixmap(size, mode, 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; +} + +/*! + \since 5.1 + + Returns the actual size of the icon for the requested \a window \a size, \a + mode, and \a state. + + The pixmap can be smaller than the requested size. The returned size + is in device-independent pixels (This is relevant for high-dpi pixmaps.) + + \sa actualSize(), pixmap(), paint() +*/ +QSize QIcon::actualSize(QWindow *window, const QSize &size, Mode mode, State state) const +{ + if (!d) + return QSize(); + + qreal devicePixelRatio = qt_effective_device_pixel_ratio(window); + + // Handle the simple normal-dpi case: + if (!(devicePixelRatio > 1.0)) + return d->engine->actualSize(size, mode, state); + + QSize actualSize = d->engine->actualSize(size * devicePixelRatio, mode, state); + return actualSize / d->pixmapDevicePixelRatio(devicePixelRatio, size, actualSize); +} /*! Uses the \a painter to paint the icon with specified \a alignment, diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h index cda2360bed..e81bea69d6 100644 --- a/src/gui/image/qicon.h +++ b/src/gui/image/qicon.h @@ -79,8 +79,10 @@ public: { return pixmap(QSize(w, h), mode, state); } inline QPixmap pixmap(int extent, Mode mode = Normal, State state = Off) const { return pixmap(QSize(extent, extent), mode, state); } + QPixmap pixmap(QWindow *window, const QSize &size, Mode mode = Normal, State state = Off) const; QSize actualSize(const QSize &size, Mode mode = Normal, State state = Off) const; + QSize actualSize(QWindow *window, const QSize &size, Mode mode = Normal, State state = Off) const; QString name() const; diff --git a/src/gui/image/qicon_p.h b/src/gui/image/qicon_p.h index 2ddb5872e1..a46cc310ad 100644 --- a/src/gui/image/qicon_p.h +++ b/src/gui/image/qicon_p.h @@ -72,6 +72,8 @@ public: delete engine; } + qreal pixmapDevicePixelRatio(qreal displayDevicePixelRatio, const QSize &requestedSize, const QSize &actualSize); + QIconEngine *engine; QAtomicInt ref; -- cgit v1.2.3