Android: How to change OpenGLES texture data / bitmap from gallery / camera

时间:2015-06-20 09:35:00

标签: java android opengl-es effects

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.

1 个答案:

答案 0 :(得分:1)

看起来您的changeBackground()方法可能是从UI线程调用的,或者至少是渲染线程以外的其他线程。

GLSurfaceView创建一个单独的渲染线程,在调用渲染方法时,它具有当前的OpenGL上下文。因此,它可以用于您的OpenGL调用。所有其他线程都不是这种情况。当前的OpenGL上下文每个线程。因此,除非你对它做一些事情(这是可能的,但会增加显着的复杂性),你不能从其他线程进行OpenGL调用。

有几个选项可以解决这个问题。最简单的方法可能是使用queueEvent()上的GLSurfaceView方法,它允许您传入将在渲染线程中执行的Runnable

我不确定代码中的mTexRenderer.updateTextureSize()方法是做什么的。但通常情况下,如果纹理大小发生变化,则必须使用GLUtils.texImage2D()代替GLUtils.texSubImage2D()来更新纹理。

代码可能看起来像这样(未经测试,因此它可能与输入完全不同):

final Bitmap bitmap = BitmapFactory.decodeFile(bitmapUri.getPath());

theBackground.queueEvent(new Runnable() {
    @Override
    void run() {
        mImageWidth = bitmap.getWidth();
        mImageHeight = bitmap.getHeight();
        mTexRenderer.updateTextureSize(mImageWidth, mImageHeight);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]);
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

        bitmap.recycle();
    }
});

theBackground.requestRender();