How to create and use an image provider, more exactly a QQuickAsyncImageProvider.

Full example can be found at https://github.com/g-fb/qml-custom-image-provider.

Create the provider class

customimageprovider.h

#ifndef CUSTOMIMAGEPROVIDER_H
#define CUSTOMIMAGEPROVIDER_H

#include <QQuickAsyncImageProvider>
#include <QRunnable>

class CustomImageProvider : public QQuickAsyncImageProvider
{
public:
    explicit CustomImageProvider();
    QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
};

class CustomImageResponse : public QQuickImageResponse
{
public:
    CustomImageResponse(const QString &id, const QSize &requestedSize);
    QQuickTextureFactory *textureFactory() const override;

private:
    QImage m_image;
};

class AsyncImageResponseRunnable : public QObject, public QRunnable
{
    Q_OBJECT

public:
    AsyncImageResponseRunnable(const QString &id, const QSize &requestedSize);
    void run() override;

Q_SIGNALS:
    void done(QImage image);

private:
    QString m_id;
    QSize m_requestedSize;
};

#endif // CUSTOMIMAGEPROVIDER_H

customimageprovider.cpp

#include "customimageprovider.h"

#include <QTransform>
#include <QThreadPool>

CustomImageProvider::CustomImageProvider()
    : QQuickAsyncImageProvider()
{
}

QQuickImageResponse *CustomImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize)
{
    auto response = new CustomImageResponse(id, requestedSize);
    return response;
}

CustomImageResponse::CustomImageResponse(const QString &id, const QSize &requestedSize)
{
    auto runnable = new AsyncImageResponseRunnable(id, requestedSize);
    connect(runnable, &AsyncImageResponseRunnable::done, this, [this](QImage image) {
        m_image = image;
        Q_EMIT finished();
    });

    QThreadPool::globalInstance()->start(runnable);
}

QQuickTextureFactory *CustomImageResponse::textureFactory() const
{
    return QQuickTextureFactory::textureFactoryForImage(m_image);
}

AsyncImageResponseRunnable::AsyncImageResponseRunnable(const QString &id, const QSize &requestedSize)
    : m_id{id}
    , m_requestedSize{requestedSize}
{
}

void AsyncImageResponseRunnable::run()
{
    QImage image{m_id};
    QImage scaled = image.scaled(m_requestedSize);
    QImage rotatedImage = scaled.transformed(QTransform().rotate(180.0));

    Q_EMIT done(rotatedImage);
}

When creating image providers we need to implement the requestImageResponse function. requestImageResponse takes to arguments an id and a requestedSize, these come from the QML side.

In the case of an async provider requestImageResponse creates and returns an QQuickImageResponse, in this example CustomImageResponse.

Inside the constructor of CustomImageResponse:

  • create an instance of AsyncImageResponseRunnable
  • connect AsyncImageResponseRunnable::done signal to a lambda; the lambda sets CustomImageResponse’s m_image to the image received through the signal and emits finished to let the provider know the response has finished
    • m_image is used in CustomImageResponse::textureFactory
  • start the runnable through a QThreadPool, which executes the AsyncImageResponseRunnable::run function where the image is created and manipulated using the id and requestedSize, when the final image is ready done is emited with the image as argument

Add the files to cmake

qt_add_qml_module(appexample_image_provider
    URI example_image_provider
    VERSION 1.0
    QML_FILES
        Main.qml
    SOURCES customimageprovider.h customimageprovider.cpp # <--
)

Add the provider to the QQmlApplicationEngine

main.cpp

#include "customimageprovider.h"
...
QQmlApplicationEngine engine;
engine.addImageProvider(QStringLiteral("provider_name"), new CustomImageProvider());
...

Use the provider

Main.qml


Window {
    ...
    Image {
        anchors.centerIn: parent
        source: "image://provider_name//path/to/image.jpg"
        width: 300
        height: 500
        sourceSize.width: 200
        sourceSize.height: 300
    }
}

The source is composed of multiple parts

  • image:// - the url scheme, tells the Image to use an image provider
  • provider_name - the name set when adding the provider to the QQmlApplicationEngine
  • / - separator between provider name and the id
  • /path/to/image.jpeg - the id passed to the image provider

The sourceSize is the size passed to the image provider (requestedSize).

In this example the id passed to the image provider is a local path, but it can be anything, for example it could be the id of a database record where images are stored as blobs.