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?


  • Lifetime Qt Champion

    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.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.