Sign up for the KDAB Newsletter
Stay on top of the latest news, publications, events and more.
Go to Sign-up
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.
Wrapping tiny JS libraries in QML to do quick and simple things effortlessly and elegantly.
Factors to Consider When Choosing a Software Stack
A New Qt (5.x & 6.x) Library for Complete Shared Storage Access
Stay on top of the latest news, publications, events and more.
Go to Sign-up
Upgrade your applications from Qt 5 to Qt 6 with KDAB’s migration services. Get a free migration assessment and join a hands-on workshop to prepare your team for a successful transition!
Learn more
1 Comment
8 - Aug - 2020
Aleksey
How to convert Java
Drawable
to QImage or pixmap?