I try reuse Android Media Effects samples and want to be able to change texture data with new bitmap from camera or gallery pick. No solution so far
This is the initial load texture from the sample : https://github.com/googlesamples/android-MediaEffects/blob/master/Application/src/main/java/com/example/android/mediaeffects/MediaEffectsFragment.java
private void loadTextures() {
// Generate textures
GLES20.glGenTextures(2, mTextures, 0);
// Load input bitmap
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.puppy);
mImageWidth = bitmap.getWidth();
mImageHeight = bitmap.getHeight();
mTexRenderer.updateTextureSize(mImageWidth, mImageHeight);
// Upload to texture
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Set texture parameters
GLToolbox.initTexParams();
}
and this is my trouble function :
public void changeBackground(Uri bitmapUri) {
Log.d(TAG, "changeBackground : " + bitmapUri.getPath());
Bitmap bitmap = BitmapFactory.decodeFile(bitmapUri.getPath());
mImageWidth = bitmap.getWidth();
mImageHeight = bitmap.getHeight();
mTexRenderer.updateTextureSize(mImageWidth, mImageHeight);
GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, bitmap);
theBackground.requestRender(); //my GLSurfaceView
bitmap.recycle();
}
I tried several ways :
using texSubImage2D only -> texture not updated
delete and recreate textures with new bitmap -> black texture
note: the bitmap dimension is fixed, at 1080 x 1080
How to change/modify/replace texture data with new bitmap ?
UPDATE:
I think the problem more relate to this question : Setting texture on 3D object using image from photo gallery. i call changeBackground() after grabbing the image from gallery intent and then crop it (another intent).
Now, how to effectively change texture data with bitmap from gallery intent? anyone have a clue?
SOLVED
I solved the problem by defering texture load process to onSurfaceChange() function. Simply use a flag to indicate the file to load and onSurfaceChange check the flag again.
private Uri mFileToLoad = null;
public void changeBackground(Uri bitmapUri) {
Log.d(TAG, "changeBackground : " + bitmapUri.getPath());
mFileToLoad = bitmapUri;
theBackground.requestRender();
}
and my onSurfaceChange()
public void onSurfaceChanged(GL10 gl, int width, int height) {
Log.d(TAG, "onSurfaceChanged w:" + width + " h:" + height);
if (mFileToLoad!=null) {
GLES20.glDeleteTextures(mTextures.length, mTextures, 0);
GLES20.glGenTextures(mTextures.length, mTextures, 0);
Bitmap bitmap = BitmapFactory.decodeFile(mFileToLoad.getPath());
mImageWidth = bitmap.getWidth();
mImageHeight = bitmap.getHeight();
mTexRenderer.updateTextureSize(mImageWidth, mImageHeight);
// Upload to texture
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Set texture parameters
GLToolbox.initTexParams();
mFileToLoad = null;
bitmap.recycle();
}
if (mTexRenderer != null) {
mTexRenderer.updateViewSize(width, height);
}
}
So far it works.
It looks like your
changeBackground()
method is probably called from the UI thread, or at least a thread other than the rendering thread.GLSurfaceView
creates a separate rendering thread, which has a current OpenGL context when your rendering methods are called. It is therefore ready for your OpenGL calls. This is not the case for all other threads. The current OpenGL context is per thread. So unless you do something about it (which is possible, but adds significant complexity), you can't make OpenGL calls from other threads.There are a few options to resolve this. The easiest one is probably using the
queueEvent()
method on theGLSurfaceView
, which allows you to pass in aRunnable
that will be executed in the rendering thread.I'm not sure what the
mTexRenderer.updateTextureSize()
method in your code does. But generally, if the texture size changes, you'll have to useGLUtils.texImage2D()
instead ofGLUtils.texSubImage2D()
to update the texture.The code could look like this (untested, so it might not work exactly as typed):