Skip to content

Qt on Android: How to convert Qt images to Android Images and vice-versa

Sometimes we need to exchange images from Android world to Qt world and vice-versa, therefore in this article we’re going to see how to convert a QImage to an Android Bitmap and how to convert an Android bitmap and an Android Drawable into a QImage.

To get access to Android Bitmap data & info we’re going to use NDK’s support, so we need to make sure jnigraphics library is in libraries dependency in our .pro file:

LIBS += -ljnigraphics

Now let’s see what the convert functions looks like:

#include <QAndroidJniEnvironment>
#include <QAndroidJniObject>
#include <android/bitmap.h>

QImage toImage(const QAndroidJniObject &bitmap)
{
    QAndroidJniEnvironment env;
    AndroidBitmapInfo info;
    if (AndroidBitmap_getInfo(env, bitmap.object(), &info) != ANDROID_BITMAP_RESULT_SUCCESS)
        return QImage();

    QImage::Format format;
    switch (info.format) {
    case ANDROID_BITMAP_FORMAT_RGBA_8888:
        format = QImage::Format_RGBA8888;
        break;
    case ANDROID_BITMAP_FORMAT_RGB_565:
        format = QImage::Format_RGB16;
        break;
    case ANDROID_BITMAP_FORMAT_RGBA_4444:
        format = QImage::Format_ARGB4444_Premultiplied;
        break;
    case ANDROID_BITMAP_FORMAT_A_8:
        format = QImage::Format_Alpha8;
        break;
    default:
        return QImage();
    }

    void *pixels;
    if (AndroidBitmap_lockPixels(env, bitmap.object(), &pixels) != ANDROID_BITMAP_RESULT_SUCCESS)
        return QImage();

    QImage image(info.width, info.height, format);

    if (info.stride == uint32_t(image.bytesPerLine())) {
        memcpy((void*)image.constBits(), pixels, info.stride * info.height);
    } else {
        uchar *bmpPtr = static_cast<uchar *>(pixels);
        const unsigned width = std::min(info.width, (uint)image.width());
        const unsigned height = std::min(info.height, (uint)image.height());
        for (unsigned y = 0; y < height; y++, bmpPtr += info.stride)
            memcpy((void*)image.constScanLine(y), bmpPtr, width);
    }

    if (AndroidBitmap_unlockPixels(env, bitmap.object()) != ANDROID_BITMAP_RESULT_SUCCESS)
        return QImage();

    return image;
}

This function doesn’t have any JNI magic, and it’s quite simple. We try to find the best QImage::Format (To be honest I’m not very sure about ANDROID_BITMAP_FORMAT_RGBA_4444), then we copy the image data from Android bitmap to our QImage.

Being able to convert any Android Drawable to a QImage is quite useful, therefore, let’s check how to “convert” (actually draw) an Android drawable into a QImage.

QAndroidJniObject createBitmap(int width, int height)
{
    QAndroidJniObject config = QAndroidJniObject::getStaticObjectField("android/graphics/Bitmap$Config",
                                                                       "ARGB_8888",
                                                                       "Landroid/graphics/Bitmap$Config;");

    return QAndroidJniObject::callStaticObjectMethod("android/graphics/Bitmap",
                                                     "createBitmap",
                                                     "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;",
                                                     width, height, config.object());
}

QImage toImage(const QAndroidJniObject &drawable, const QRect &bounds)
{
    QAndroidJniObject bitmap = createBitmap(bounds.width(), bounds.height());
    QAndroidJniObject canvas("android/graphics/Canvas", "(Landroid/graphics/Bitmap;)V", bitmap.object());
    drawable.callMethod<void>("setBounds", "(IIII)V", bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
    drawable.callMethod<void>("draw", "(Landroid/graphics/Canvas;)V", canvas.object());
    return toImage(bitmap);
}

First function (createBitmap) is used to create an RGBA_32 bitmap, we’re going to use this function in the next snippet too. Second function (toImage) draws the drawable into a bitmap canvas, then it converts the bitmap using previous toImage function.

Finally, let’s see how to convert a QImage to an Android Bitmap

QAndroidJniObject toAndroidBitmap(const QImage &img)
{
    QImage image = img.format() == QImage::Format_RGBA8888 ? img : img.convertToFormat(QImage::Format_RGBA8888);
    QAndroidJniObject bitmap = createBitmap(img.width(), img.height());
    QAndroidJniEnvironment env;
    AndroidBitmapInfo info;
    if (AndroidBitmap_getInfo(env, bitmap.object(), &info) != ANDROID_BITMAP_RESULT_SUCCESS)
        return QAndroidJniObject();

    if (info.format!= ANDROID_BITMAP_FORMAT_RGBA_8888)
        return QAndroidJniObject();

    void *pixels;
    if (AndroidBitmap_lockPixels(env, bitmap.object(), &pixels) != ANDROID_BITMAP_RESULT_SUCCESS)
        return QAndroidJniObject();

    if (info.stride == uint32_t(image.bytesPerLine())) {
        memcpy(pixels, image.constBits(), info.stride * info.height);
    } else {
        uchar *bmpPtr = static_cast<uchar *>(pixels);
        const unsigned width = std::min(info.width, (uint)image.width());
        const unsigned height = std::min(info.height, (uint)image.height());
        for (unsigned y = 0; y < height; y++, bmpPtr += info.stride)
            memcpy(bmpPtr, image.constScanLine(y), width);
    }

    if (AndroidBitmap_unlockPixels(env, bitmap.object()) != ANDROID_BITMAP_RESULT_SUCCESS)
        return QAndroidJniObject();

    return bitmap;
}

This function is quite simple, we’re using createBitmap from a previous snippet to create an Android Bitmap object, then we copy the QImage content to Android Bitmap.

Leave a Reply

Your email address will not be published. Required fields are marked *

By continuing to use the site, you agree to the use of cookies. More information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close