Friday, December 9, 2011

Using QDeclarativeImageProvider to load QPixmap to QML Image element

Recently for my mobile application, I required cache image from network and use that in QML application. Prior to caching, I was using WebView element to show image from network, but I needed to show that image multiple places, to avoid multiple network request I decided to prefetch network image and show that using Image element.

To make it work, I downloaded network image to QPixmap using QNetworkAccessManager and then used QDeclarativeImageProvider to provide cached QPixmap to Image element.

As per documentation "The QDeclarativeImageProvider class provides an interface for supporting pixmaps and threaded image requests in QML."  I also used it as proxy image provider. When actual Image data is not available my image provider class will provide some dummy image and when actual data is available it will provide actual image data.

Following is my code for my custom image provider derived from QDeclarativeImageProvider.

In following code, by providing QDeclarativeImageProvider::Pixmap as ImageType, I am saying I will povide QPixmap as Image type and in this case requestPixmap API will be called by declarative engine to fetch image. You can also use QDeclarativeImageProvider::Image, to provide QImage to Image and in that case requestImage API will be called.

#include <QDeclarativeImageProvider>

class ImageProvider: public QDeclarativeImageProvider
{
public:
    ImageProvider(ImageManager* manager)
        : QDeclarativeImageProvider(QDeclarativeImageProvider::Pixmap),
          mManager(manager)
    {}

    QPixmap requestPixmap(const QString &id, QSize *size, 
        const QSize &requestedSize)
    {
        int width = 320;
        int height = 53;

        QPixmap result;

        if( mManager->imageData() == 0 ) {
     QPixmap pixmap(requestedSize.width() > 0?requestedSize.width():width,
             requestedSize.height() > 0 ?requestedSize.height():height);

            pixmap.fill(QColor(id).rgba());

            if ( size ) *size = QSize(width, height);
            result = pixmap;
        } else {

            result = *mManager->imageData();
            if ( size ) *size =result.size();

            if (requestedSize.isValid()) {              
                result = result.scaled(requestedSize,Qt::IgnoreAspectRatio);
            } 
        }
        return result;
    }

private:
    ImageManager* mManager;
};
Now custom image provider is ready, but we need to register our image provide to declarative engine, so that it will now forward image loading request to our image provider. To do that, we need to use QDeclarativeEngine::addImageProvider API, as described in following code. Here in addImageProvider API, "MyImage" is provider name, that will be used in QML to indicate that our custom image provider should be used.
QScopedPointer view(new QDeclarativeView());
view->engine()->addImageProvider(QLatin1String("MyImage"), 
    new ImageProvider(ImageManager::instance()));
Now we are ready to use our custom image provider with Image element, like shown below. Here in Image.source we providing path as image://MyImage, which will tell system to use MyImage image provider and remaining part of url will be used as id in requestPixmap or requestImage parameter. Which can be used to decide what kind of image data to provide.
import QtQuick 1.0

Rectangle {
    id:container
    width: 320;height: 53
    anchors.horizontalCenter: parent.horizontalCenter    

    Connections{
        target: ImageManager
        onImageAvailable:{
            myImage.source = "image://MyImage/test"
        }
    }

    Image {
        id: myImage
        anchors.centerIn: parent
        source: "image://MyImage/"+container.color
    }
}
This is all that is needs to be done to implement custom image provide. Hope it will be usefull.

2 comments:

  1. Can you get the above to work in N9?

    ReplyDelete
  2. Yes, it works fine on n9. BUt as shown in blog i am creating my own QDeclarativeView, i am not using app viewer from template code.

    ReplyDelete