Android:如何从图库/相机更改 OpenGLES 纹理数据/位图

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

我尝试重新使用 Android Media Effects samples 并希望能够使用来自相机或图库的新位图更改纹理数据。目前没有解决方案

这是示例中的初始加载纹理: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();
    }

这是我的麻烦函数:

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();
}

我尝试了几种方法:

注意:位图尺寸是固定的,为 1080 x 1080

如何change/modify/replace 使用新位图对数据进行纹理处理?

更新:

我认为这个问题与这个问题更相关:Setting texture on 3D object using image from photo gallery。从画廊意图中获取图像然后裁剪它(另一个意图)后,我调用 changeBackground() 。

现在,如何从图库意图中有效地更改带有位图的纹理数据?有人知道吗?

已解决

我通过将纹理加载过程推迟到 onSurfaceChange() 函数解决了这个问题。只需使用一个标志来指示要加载的文件,然后 onSurfaceChange 再次检查该标志。

private Uri mFileToLoad = null;

public void changeBackground(Uri bitmapUri) {
   Log.d(TAG, "changeBackground : " + bitmapUri.getPath());
   mFileToLoad = bitmapUri;
   theBackground.requestRender();
}

和我的 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);
        }
    }

到目前为止它有效。

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

GLSurfaceView 创建一个单独的渲染线程,当您的渲染方法被调用时,它有一个当前的 OpenGL 上下文。因此,它已为您的 OpenGL 调用做好准备。所有其他线程都不是这种情况。当前的 OpenGL 上下文是 per thread。所以除非你对此做些什么(这是可能的,但会增加显着的复杂性),否则你不能从其他线程进行 OpenGL 调用。

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