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.
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.
The following steps describe how the sample application is developed:
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.NativeAssets
project, select Android Tools | Add Native Support.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());
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); }
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)
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
:
AAssetManager_fromJava
function, which is defined in asset_manager_jni.h
.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.
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.
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 accessedAASSET_MODE_RANDOM
: Read chunks, and seek forward and backwardAASSET_MODE_STREAMING
: Read sequentially, with an occasional forward seekAASSET_MODE_BUFFER
: Attempt to load contents into memory, for fast small readsAAsset_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
.
AAsset_close
function.AAssetDir_close
function.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.