Managing assets at Android NDK

Assets provide a way for Android apps to include various types of files, including text, image, audio, video, and so on. This recipe discusses how to load asset files from Android NDK.

Getting ready

We will modify the example we developed in the Mapping texture in OpenGL ES 1.x recipe in Chapter 4, Android NDK OpenGL ES API. Readers are suggested to read through the recipe or take a look at the code first.

How to do it…

The following steps describe how the sample application is developed:

  1. Create an Android application named NativeAssets. Set the package name as cookbook.chapter5.nativeassets. Please refer to the Loading native libraries and registering native methods recipe of Chapter 2, Java Native Interface, if you want more detailed instructions.
  2. Right-click on the NativeAssets project, select Android Tools | Add Native Support.
  3. Add three Java files, namely MyActivity.java, MySurfaceView.java, and MyRenderer.java under the cookbook.chapter5.nativeassets package. The first two files are identical to the corresponding files in the Mapping texture in OpenGL ES 1.x recipe in Chapter 4, Android NDK OpenGL ES API. The last file is slightly changed, where the naLoadTexture native method signature is updated as follows:
    private static native void naLoadTexture(AssetManager pAssetManager);

    In the onSurfaceCreated method, we called the native method by passing a Java AssetManager instance:

    naLoadTexture(mContext.getAssets());
  4. Create two folders under the jni folder, namely dice and libpng-1.5.12. In the libpng-1.5.12 folder, we place the source files of libpng, which can be downloaded from http://sourceforge.net/projects/libpng/files/.

    In the dice folder, we add the Cube.cpp, Cube.h, mylog.h, and DiceG1.cpp files. The first three files are the same as the example in the Mapping texture in OpenGL ES 1.x recipe in Chapter 4, Android NDK OpenGL ES API. The DiceG1.cpp file is updated by adding procedures to read .png assets files from the assets folder. Let's show a part of the updated code:

    • readPng: It is the callback function used at png_set_read_fn. It reads the data from the asset file:
      void readPng(png_structp pPngPtr, png_bytep pBuf, png_size_t pCount) {
        AAsset* assetF = (AAsset*)png_get_io_ptr(pPngPtr);
        AAsset_read(assetF, pBuf, pCount);
      }
    • naLoadTexture: It reads all the .png files under the assets top-level directory and loads the data to OpenGL for texture mapping:
      void naLoadTexture(JNIEnv* env, jclass clazz, jobject pAssetManager) {
        AAssetManager* assetManager = AAssetManager_fromJava(env, pAssetManager);
        AAssetDir* texDir = AAssetManager_openDir(assetManager, "");
        const char* texFn;
        int pId = 0;
        while (NULL != (texFn = AAssetDir_getNextFileName(texDir))) {
          AAsset* assetF = AAssetManager_open(assetManager, texFn, AASSET_MODE_UNKNOWN);
          //read the png header
          png_byte header[8];
         png_byte *imageData;
          …...
          if (8 != AAsset_read(assetF, header, 8)) {
            goto FEND;
          }
          …...
          //init png reading by setting a read callback
          png_set_read_fn(pngPtr, assetF, readPng);
          …...
          // Loads image data into OpenGL.
          glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, imageData);
      FEND:
          AAsset_close(assetF);
          pId++;
        }
        AAssetDir_close(texDir);
      }
  5. Add an Android.mk file under jni, jni/dice, and jni/libpng-1.5.12 respectively. The Android.mk file under the jni top-level folder is as follows. This simply instructs the Android build system to include the Android.mk files under each sub-directory under the jni folder:
    LOCAL_PATH := $(call my-dir)
    include $(call all-subdir-makefiles)

    The Android.mk file under the jni/libpng-1.5.12 folder is as follows. This compiles libpng as a local static library:

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_CFLAGS := 
    LOCAL_MODULE    := libpng
    LOCAL_SRC_FILES :=
      png.c 
      pngerror.c 
      pngget.c 
      pngmem.c 
      pngpread.c 
      pngread.c 
      pngrio.c 
      pngrtran.c 
      pngrutil.c 
      pngset.c 
      pngtrans.c 
      pngwio.c 
      pngwrite.c 
      pngwtran.c 
      pngwutil.c 
    LOCAL_LDLIBS := -lz
    include $(BUILD_STATIC_LIBRARY)

    The Android.mk file under the jni/dice folder is as follows:

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE    := DiceG1NativeAssets
    LOCAL_C_INCLUDES := $(LOCAL_PATH)/../libpng-1.5.12/
    LOCAL_STATIC_LIBRARIES := libpng
    LOCAL_SRC_FILES := Cube.cpp DiceG1.cpp
    LOCAL_LDLIBS := -lGLESv1_CM -llog -landroid -lz
    include $(BUILD_SHARED_LIBRARY)
  6. Build the Android NDK application and run it on an Android device. The app will display a cube textured as a dice; this is the same as what we have seen in Chapter 4, Android NDK OpenGL ES API.
    How to do it…

How it works…

In the example, we load the .png files from the assets folder and used them as OpenGL textures. You can use the following steps to read assets:

  1. Get a native AAssetManager object from the Java AssetManager object: This is done by the AAssetManager_fromJava function, which is defined in asset_manager_jni.h.
  2. Open an asset directory: This is done by AAssetManager_openDir.
    AAssetDir* AAssetManager_openDir(AAssetManager* mgr, const char* dirName);

    To open the top-level directory "assets", we set dirName to "". For the subdirectories, we will need to supply the directory name.

  3. Get an asset file name:
    const char* AAssetDir_getNextFileName(AAssetDir* assetDir);

    Iterate over the files under the asset directory referred by the input argument assetDir. If all files have been returned or there are no files, NULL is returned.

  4. Open an asset file: This is done by using AAssetManager_open:
    AAsset* AAssetManager_open(AAssetManager* mgr, const char* filename, int mode);

    The filename should be set to the asset file name, where mode can be one of the following:

    • AASSET_MODE_UNKNOWN: Not known how the data is to be accessed
    • AASSET_MODE_RANDOM: Read chunks, and seek forward and backward
    • AASSET_MODE_STREAMING: Read sequentially, with an occasional forward seek
    • AASSET_MODE_BUFFER: Attempt to load contents into memory, for fast small reads
  5. Read the asset file: This is done by using AAsset_read.
    int AAsset_read(AAsset* asset, void* buf, size_t count);

    The input argument buf refers to the location where the data is placed after reading, and count indicates the number of bytes we want to read. The actual number of bytes read is returned and may differ from count.

  6. Close the asset file: This is done by using the AAsset_close function.
  7. Close the asset directory: This is done by using the AAssetDir_close function.

There's more…

In this example, we built libpng as a local static library. This is necessary to read the .png files, because Android NDK does not provide APIs to access .png files. We will discuss how to develop Android NDK applications with existing libraries in Chapter 8, Porting and Using the Existing.

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

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