Reputation: 2122
Summary
Long part
I use next code to load PNG files with Java AssetManager to use them for Open GL ES 2.0 textures creation.
Java side PNG class
import java.nio.ByteBuffer;
import android.graphics.Bitmap;
public class PNG
{
private static final int BYTES_PER_PIXEL_PNG = 4;
private static final String LOG_TAG = "[PNG]";
public int width;
public int height;
public ByteBuffer pixels;
public PNG(Bitmap bitmap)
{
this.width = bitmap.getWidth();
this.height = bitmap.getHeight();
this.pixels = ByteBuffer.allocateDirect(this.width * this.height * BYTES_PER_PIXEL_PNG);
bitmap.copyPixelsToBuffer(this.pixels);
}
}
public static PNG loadPNG(String path)
{
InputStream is = null;
try
{
is = ASSETS_MANAGER.open(path);//get png file stream with AssetsManager instance
}
catch (IOException e)
{
Log.e(LOG_TAG, "Can't load png - " + path, e);
}
return new PNG(BitmapFactory.decodeStream(is));
}
C++ side PNG
typedef struct png
{
int width;
int height;
char* pixels;
} png;
png* load_png(const char* path)
{
png* res = (res*) malloc(sizeof(png);
...
jobject _png = env->CallStaticObjectMethod(get_java_lib_class(), get_method_id(JAVA_LIB_LOAD_PNG, JAVA_LIB_LOAD_PNG_SIGN), _path);//Calling loadPng() from Java, get PNG jobject
jobject _pixels = env->GetObjectField(_png, PNG_FIELDS->PNG_PIXELS_ID);//Getting pixels field from Java PNG jobject
res->pixels = (char*) env->GetDirectBufferAddress(_pixels);//Get direct pointer to our pixel data
//Cleanup
...
env->DeleteLocalRef(_png);
env->DeleteLocalRef(_pixels);
return res;
}
Then using png to create texture
void test_create_tex(const char* path)
{
...
png* source = load_png(path);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, source->width, source->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, source->pixels);
//We don't need source pixel data any more
//free(source->pixels);//INVALID HEAP ADDRESS (deadbaad)
free(source);
}
So how release byte buffer after using in C++ side with it's direct pointer? It is allocate directly (like malloc - on native side) and must be freed or i will get OutOfMemory error.
Upvotes: 4
Views: 3803
Reputation: 7278
You don't need to release the buffer. You have allocated it on Java side, which means it's JVM object and GC will take care about it. As contrary to allocating on C side, thus being a native object which GC doesn't know about. You even do not need to do DeleteLocalRef
as all local references will be deleted for you by the JNI machinery upon returning from the native method. You would need to explicitly delete only if there would be hundreds of JNI calls back to JVM in scope of one native call, so you would run out of handles even before returning back to JVM.
I must admit that i don't know exactly how GC knows that it should not touch your ByteBuffer, but i would guess that by calling GetObjectField
you're incrementing refcount on the ByteBuffer and decrementing with DeleteLocalRef
. So between those two JNI calls, ByteBuffer is safe to stay.
Upvotes: 4
Reputation: 26478
In my opinion, you don't need to worry about the releasing of the ByteBuffer pixels
, because it's managed by JVM. What you should really care about is keeping it from garbage collected when it's used by C++.
Upvotes: 2