1. Load texture
I used Android BitmapFactory to decode PNG texture. If you want one source multi flatform, Use libpng. This writing show example BitmapFactory decoding. First we have to load image. Theretofore we have to decide resource foloder.
A. res/raw
Many developer use assets foloder to manage resource, but I decide resource folder res/raw because res folder managed to R.java, there could be used on Android Java easily.
B. Load resource
First to manage loaded resource, I made Texture class
public class Texture { public String mName; public ByteBuffer mEncodeData; }
It have texture name and ByteBuffer that is used in decoding functions.
And save loaded resource to Texture object.
public static boolean loadImage(String strName) { Log.i("AndroidOpenGL", "loadImage start, name : "+ strName); if(MainActivity.context == null) { Log.w("AndroidOpenGL", "Failed get Bitmap from Asset. context is null"); return false; } if(strName == null) return false; try { Texture tex = new Texture(); Resources res = MainActivity.context.getResources(); int resId = res.getIdentifier("raw/"+strName, null, MainActivity.context.getPackageName()); if(resId == 0) { Log.w("AndroidOpenGL", "Can't find resource : "+ strName); return false; } BufferedInputStream is = new BufferedInputStream(res.openRawResource(resId)); ReadableByteChannel channel = Channels.newChannel(is); tex.mName = strName; int fileSize = (int) res.openRawResourceFd(res.getIdentifier("raw/"+strName, null, MainActivity.context.getPackageName())).getLength(); tex.mEncodeData = ByteBuffer.allocate(fileSize); int r = 0; while (tex.mEncodeData.remaining() > 0 && r != -1) r = channel.read(tex.mEncodeData); ResourceManager.instance.addTexture(tex); if(is != null) is.close(); return true; } catch (IOException e) { Log.w("AndroidOpenGL", e.toString()); e.printStackTrace(); } return false; }
I used ReadableByteChannel to get file size because I want to allocate memory bytes to Texture object.
2. Decode Texture
If you use libpng library, It could be used on other flatform like IOS. But libpng code little hard to use and tiresome. So I used Android API.
public static void decodeImage(String strName, boolean add_to_atlas) { Log.i("AndroidOpenGL", "decodeImage start, name : "+ strName); if(MainActivity.context == null) { Log.w("AndroidOpenGL", "Failed get Bitmap from Asset. context is null"); return ; } if(strName == null) return; Texture tex = ResourceManager.instance.getTexture(strName); if(tex == null) return; Bitmap bitmap = null; int width; int height; ByteBuffer bb = null; try { bitmap = BitmapFactory.decodeByteArray(tex.mEncodeData.array(), 0, tex.mEncodeData.capacity()); width = bitmap.getWidth(); height = bitmap.getHeight(); bb = ByteBuffer.allocate(width*height*4); bitmap.copyPixelsToBuffer(bb); bitmap.recycle(); }catch(Exception e) { Log.w("AndroidOpenGL", "OpenGL : Get Bitmap Failed"); Log.w("AndroidOpenGL", e.toString()); return ; } Log.i("AndroidOpenGL", "Allocated direct-buffer capacity:"+ bb.capacity()); Log.i("AndroidOpenGL", "decodeImage end, bb width:"+ width +" height:"+height); onDecodeImageComplete(bb.array(), width, height, strName, add_to_atlas); }
You can get byte array using BitmapFactory. [onDecodeImageComplete] is jni function that send decoded resource to c++.
Caution thing is your resource's width and height have to be 2^n(exponent) because OpenGL glTexImage2D function only get 2^n width and height to parameter.
So I used image that size is 32*32, 64*128 etc.
3. Jni
After decoding, we have to send data to c++. Below show [onDecodeImageComplete] code(Ignore add_to_atlas parameter, It don't need to example).
JNIEXPORT void JNICALL Java_com_snj_opengles_MainNDK_onDecodeImageComplete(JNIEnv * a, jclass b, jbyteArray pixels, jint width, jint height, jstring name, jboolean add_to_atlas) { Log::info_print("onDecodeImageComplete start"); //Use direct buffer // GLubyte* data =(GLubyte*)a->GetDirectBufferAddress(pixels); //Use non-direct buffer int len = width*height*4; GLbyte* data = new GLbyte[len]; a->GetByteArrayRegion(pixels,0, len, data); len = a->GetStringUTFLength(name); char* texName = new char[len+1]; a->GetStringUTFRegion(name,0,len,texName); shared_ptr<Texture> tex = make_shared<Texture>(); tex->m_name = string(texName); tex->m_data = (GLubyte*)data; tex->m_width = width; tex->m_height = height; tex->m_tex_map_width = width; tex->m_tex_map_height = height; tex->m_tex_map_ratio_width = tex->m_width/(float)tex->m_tex_map_width; tex->m_tex_map_ratio_height = tex->m_height/(float)tex->m_tex_map_height;
tex->m_request_add_to_atlas = add_to_atlas; ResourceManager::getInstance()->addToTextureCompletList(tex); delete[] (texName); Log::info("onDecodeImageComplete end"); }
Don't use Direct buffer. When I use direct buffer, I knew that allocating direct buffer is very slow. So I use non-direct buffer.
Use GetByteArrayRegion, It is optimized function to copy data.
When allocating char array, size is GetStringUTFLength +1. If you don't +1, it would be crashed when you delete array.
4. result
It is finish decode texture, If you use opengles 2.0 and want to know shader, reference http://shakddoo.tistory.com/9.
'게임을 만들자 > NDK & OpenGL ES' 카테고리의 다른 글
Android, NDK Log using __android_log (0) | 2014.09.25 |
---|---|
Android, Draw text using system font OpenGLES 2.0 (0) | 2014.09.24 |
OpenGLES2.0 GLSL, 2D Basic shader (1) | 2014.09.18 |
OpenGLES 2.0,GLSL lighting code (0) | 2014.09.15 |
GLSL, OpenGLES2.0 glFog (0) | 2014.09.12 |