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.

FacebookTwitterLinkedInEmail
Leave a Reply

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