QIcon not loading QPixmap on demand
-
Hello,
I'm working on a project using a lot of custom widgets with images as a background (software a la VST plugin). However, these days I'm refactoring the code and paying more attention to HiDPI images. I've spent some time researching the qt materials on HiDPI/@2x pixmaps and as it seems QIcon has some nifty features on automatically loading and returning the correct QPixmap on different screens. However, I'm dealing with really A LOT of images (some of them strips of pre-rendered images, which are quite big) so it's crucial for me to have only the used QPixmaps loaded in memory. In QIcon documentation it's written:
void QIcon::addFile(const QString &fileName, const QSize &size = QSize(), Mode mode = Normal, State state = Off)
Adds an image from the file with the given fileName to the icon, as a specialization for size, mode and state. The file will be loaded on demand. Note: custom icon engines are free to ignore additionally added pixmaps.
Anyway, to be sure I've checked the source code of Qt and I have suspicions that this is not exactly right. However, I'm not a C++ dev, I'm using the python bindings for my app, so I might be wrong. That's why I'm asking in the forum, and not filing a bug report.
In file qicon.cpp, the only way to add image from a file is through calling QIcon::addFile(...), which calls (for png images) QPixmapIconEngine::addFile(...). It's definition is:
void QPixmapIconEngine::addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state) { if (fileName.isEmpty()) return; const QString abs = fileName.startsWith(QLatin1Char(':')) ? fileName : QFileInfo(fileName).absoluteFilePath(); const bool ignoreSize = !size.isValid(); ImageReader imageReader(abs); const QByteArray format = imageReader.format(); if (format.isEmpty()) // Device failed to open or unsupported format. return; QImage image; if (format != "ico") { if (ignoreSize) { // No size specified: Add all images. while (imageReader.read(&image)) pixmaps += QPixmapIconEngineEntry(abs, image, mode, state); } else { // Try to match size. If that fails, add a placeholder with the filename and empty pixmap for the size. while (imageReader.read(&image) && image.size() != size) {} pixmaps += image.size() == size ? QPixmapIconEngineEntry(abs, image, mode, state) : QPixmapIconEngineEntry(abs, size, mode, state); } return; } ... }
So this func reads the image from the file into a QImage and then this QImage is passed as an arg to QPixmapIConEngineEntry which is:
inline QPixmapIconEngineEntry::QPixmapIconEngineEntry(const QString &file, const QImage &image, QIcon::Mode m, QIcon::State s) : fileName(file), size(image.size()), 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); }
Then this entry is added to a QVector. So... as I see it: Every pixmap "@1" and "@2" is loaded into memory and nothing is loaded "on demand", except when there is no pixmap with the specified size in the file in which case we use:
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){}
Here is the documentation of QIconEngine::addFile:
void QIconEngine::addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state)
Called by QIcon::addFile(). Adds a specialized pixmap from the file with the given fileName, size, mode and state. The default pixmap-based engine stores any supplied file names, and it loads the pixmaps on demand instead of using scaled pixmaps if the size of a pixmap matches the size of icon requested. Custom icon engines that implement scalable vector formats are free to ignores any extra files.
I think there is no need to show what happnes on QIcon::pixmap(...) call if on addFile the pixmaps are already loaded. (Again, I might be wrong).
So... does QIcon load pixmaps on demand or everything at once on init?
-
Hi and welcome to devnet,
Based on your analysis, I'd say you're right.
I'd recommend bringing this to the interest mailing list. You'll find there Qt's developers/maintainers. This forum is more user oriented.