Programming with the jnigraphics library in Android NDK

The jnigraphics library provides a C-based interface for native code to access the pixel buffers of Java bitmap objects, which is available as a stable native API on Android 2.2 system images and higher. This recipe discusses how to use the jnigraphics library.

Getting ready…

Readers are expected to know how to create an Android NDK project. We can refer to the Writing a Hello NDK program recipe of Chapter 1, Hello NDK for detailed instructions.

How to do it...

The following steps describe how to create a simple Android application which demonstrates the usage of the jnigraphics library:

  1. Create an Android application named JNIGraphics. Set the package name as cookbook.chapter7.JNIGraphics. Refer to the Loading native libraries and registering native methods recipe of Chapter 2, Java Native Interface for more detailed instructions.
  2. Right-click on the project JNIGraphics, select Android Tools | Add Native Support.
  3. Add two Java files named MainActivity.java and RenderView.java in the cookbook.chapter7.JNIGraphics package. The RenderView.java loads the JNIGraphics native library, calls the native naDemoJniGraphics method to process a bitmap, and finally display the bitmap. The MainActivity.java files creates a bitmap, passes it to the RenderView class, and sets the RenderView class as its content view.
  4. Add mylog.h and JNIGraphics.cpp files under the jni folder. The mylog.h contains the Android native logcat utility functions, while the JNIGraphics.cpp file contains the native code to process the bitmap with the jnigraphics library functions. A part of the code in the JNIGraphics.cpp file is shown as follows:
    void naDemoJniGraphics(JNIEnv* pEnv, jclass clazz, jobject pBitmap) {
      int lRet, i, j;
      AndroidBitmapInfo lInfo;
      void* lBitmap;
      //1. retrieve information about the bitmap
      if ((lRet = AndroidBitmap_getInfo(pEnv, pBitmap, &lInfo)) < 0) {
        return;
      }
      if (lInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
        return;
      }
      //2. lock the pixel buffer and retrieve a pointer to it
      if ((lRet = AndroidBitmap_lockPixels(pEnv, pBitmap, &lBitmap)) < 0) {
        LOGE(1, "AndroidBitmap_lockPixels() failed! error = %d", lRet);
      }
      //3. manipulate the pixel buffer
      unsigned char *pixelBuf = (unsigned char*)lBitmap;
      for (i = 0; i < lInfo.height; ++i) {
        for (j = 0; j < lInfo.width; ++j) {
        unsigned char *pixelP = pixelBuf + i*lInfo.stride + j*4;
        *pixelP = (unsigned char)0x00;	//remove R component
    //    *(pixelP+1) = (unsigned char)0x00;	//remove G component
    //    *(pixelP+2) = (unsigned char)0x00;	//remove B component
    //    LOGI(1, "%d:%d:%d:%d", *pixelP, *(pixelP+1), *(pixelP+2), *(pixelP+3));}
      }
      //4. unlock the bitmap
      AndroidBitmap_unlockPixels(pEnv, pBitmap);
    }
  5. Add an Android.mk file in the jni folder with the following content:
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE    := JNIGraphics
    LOCAL_SRC_FILES := JNIGraphics.cpp
    LOCAL_LDLIBS := -llog -ljnigraphics
    include $(BUILD_SHARED_LIBRARY)
  6. Build and run the Android project. We can enable code to remove different components from the bitmap. The following screenshots show the original picture and the ones with red, green, and blue component removed respectively:
    How to do it...

How it works...

In our sample project, we modified the bitmap passed to the native naDemoJniGraphics function by setting one of its RGB components to zero.

Note

The jnigraphics library is only available for Android API level 8 (Android 2.2, Froyo) and higher.

The following steps should be followed to use the jnigraphics library:

  1. Include the <android/bitmap.h> header in the source code where we use the jnigraphics API.
  2. Link to the jnigraphics library by including the following line in the Android.mk file.
    LOCAL_LDLIBS += -ljnigraphics
  3. In the source code, call AndroidBitmap_getInfo to retrieve the information about a bitmap object. The AndroidBitmap_getInfo function has the following prototype:
    int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, AndroidBitmapInfo* info);

    The function accepts a pointer to the JNIEnv structure, a reference to the bitmap object, and a pointer to the AndroidBitmapInfo structure. If the call is successful, the data structure pointed by info will be filled.

    The AndroidBitmapInfo is defined as follows:

    typedef struct {
    uint32_t    width;
    	uint32_t    height;
    uint32_t    stride;
    int32_t     format;
    uint32_t    flags; 
    } AndroidBitmapInfo;

    width and height indicate the pixel width and height of the bitmap. stride refers to the number of bytes to skip between rows of the pixel buffer. The number must be no less than the width in bytes. In most cases, stride is the same as width. However, sometimes pixel buffer contains paddings so stride can be bigger than bitmap width.

    The format is the color format, which can be ANDROID_BITMAP_FORMAT_RGBA_8888, ANDROID_BITMAP_FORMAT_RGB_565, ANDROID_BITMAP_FORMAT_RGBA_4444, ANDROID_BITMAP_FORMAT_A_8, or ANDROID_BITMAP_FORMAT_NONE as defined in the bitmap.h header file.

    In our example, we used ANDROID_BITMAP_FORMAT_RGBA_8888 as the bitmap format. Therefore, every pixel takes 4 bytes.

  4. Lock the pixel address by calling the AndroidBitmap_lockPixels function:
    int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);

    If the call succeeds, the *addrPtr pointer will point to the pixels of the bitmap. Once the pixel address is locked, the memory for the pixels will not move until the pixel address is unlocked.

  5. Manipulate the pixel buffer in the native code.
  6. Unlock the pixel address by calling AndroidBitmap_unlockPixels:
    int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);

    Note that this function must be called if the AndroidBitmap_lockPixels function succeeds.

    Note

    The jnigraphics functions return ANDROID_BITMAP_RESUT_SUCCESS, which has a value of 0, upon success. They return a negative value upon failure.

There's more…

Recall that we used the jnigraphics library to load textures in the Mapping texture to 3D objects with OpenGL ES 1.x API recipe in Chapter 4, Android NDK OpenGL ES API. We can revisit the recipe for another example of how we use the jnigraphics library.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset