Bitmap texture does not show up in framebuffer surface on Android

让人想犯罪 __ 提交于 2019-12-11 09:42:52

问题


I'm trying to use the new EffectFactory/Effect to add effects to images off screen (i.e. framebuffer). I've looked at the HelloEffects.java example provided in the SDK and I've tried it out and it works. Except it obviously uses a GLSurfaceView and that isn't what I want.

So I've taken tests/effect/src/android/effect/cts/GLEnv.java to setup the EGL stuff and I've also grabbed TextureRenderer.java and GLToolbox from the HelloEffects example. Mashed them all up and I've got the code below.

(On a side note, I have also tried tests/media/src/android/media/cts/OutputSurface.java to setup the EGL stuff and I got the exact same result.)

When I run it, the image I get back is just uniformly blue. This corresponds to the glClear I did with the colour blue. This proves at least to some degree that pixels are being rendered to the framebuffer, glReadPixels is seeing those pixels and the bitmap output is working.

But why is the texture not showing up? Neither the original nor the effect-applied texture shows up. No GL errors are detected either.

I've trimmed down the code to a single file working example that can be copied/pasted into Eclipse and will run. Obviously modify the input and output image paths per your needs.

Tested on a Nexus 10 / Android 4.3 as well as the Emulator. Same results.

import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;


import android.media.effect.Effect;
import android.media.effect.EffectContext;
import android.media.effect.EffectFactory;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import android.opengl.GLES20;
import android.opengl.GLUtils;



public class MainActivity extends Activity 
{
    private int[] mTextures = new int[2];
    private EffectContext mEffectContext;
    private Effect mEffect;
    private TextureRenderer mTexRenderer = new TextureRenderer();
    private int mImageWidth;
    private int mImageHeight;


    final static String imageFileOut = "/data/local/out.png";
    final static String imageFileIn  = "/data/local/lol.png";

    private GLEnv mEnv;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); 
        mEnv = new GLEnv();
        mEnv.makeCurrent();
        mEffectContext = EffectContext.createWithCurrentGlContext();
        mTexRenderer.init();
        loadTextures();
        initAndapplyEffect();
        renderResult();
        saveBitmap();
    }

    void saveBitmap() 
    {
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
        ByteBuffer pixelBuffer = ByteBuffer.allocateDirect(mImageWidth * mImageHeight * 4).order(ByteOrder.nativeOrder());
        GLES20.glReadPixels(0, 0, mImageWidth, mImageHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pixelBuffer);
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
        mEnv.checkForEGLErrors("store Pixels");

        Bitmap bitmap = Bitmap.createBitmap(mImageWidth, mImageHeight, Bitmap.Config.ARGB_8888);
        bitmap.copyPixelsFromBuffer(pixelBuffer);

        try 
        {
            FileOutputStream fos = new FileOutputStream(imageFileOut);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
            fos.close();
        } catch (Exception e) { e.printStackTrace();  }

    }

    private void initAndapplyEffect() 
    {
        EffectFactory effectFactory = mEffectContext.getFactory();
        if (mEffect != null) 
        {
            mEffect.release();
        }
        mEffect = effectFactory.createEffect(EffectFactory.EFFECT_BRIGHTNESS);
        mEffect.setParameter("brightness", 2.0f);
        mEffect.apply(mTextures[0], mImageWidth, mImageHeight, mTextures[1]);

     }

    private int loadTextures() 
    {
        // Generate textures
        GLES20.glGenTextures(2, mTextures, 0);

        // Load input bitmap
        Bitmap bitmap = BitmapFactory.decodeFile(imageFileIn);

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

        return mTextures[0];
    }

    private void renderResult() 
    { 
       mTexRenderer.renderTexture(mTextures[1]);
       //mTexRenderer.renderTexture(mTextures[0]);
    }


    public class GLEnv {

        private EGLContext mEGLContext;
        private EGLSurface mEGLSurface;
        private EGLDisplay mEGLDisplay;
        private EGLConfig  mEGLConfig;

        private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
        private static final int EGL_OPENGL_ES2_BIT = 0x0004;

        public GLEnv() {
            EGL10 egl = (EGL10)EGLContext.getEGL();

            mEGLDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
            checkForEGLErrors("eglGetDisplay");

            int[] version = new int[2];
            egl.eglInitialize(mEGLDisplay, version);
            int[] configSpec = {
                EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT,
                EGL10.EGL_RED_SIZE, 8,
                EGL10.EGL_GREEN_SIZE, 8,
                EGL10.EGL_BLUE_SIZE, 8,
                EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                EGL10.EGL_NONE
            };
            EGLConfig[] configs = new EGLConfig[1];
            int[] num_config = new int[1];
            egl.eglChooseConfig(mEGLDisplay, configSpec, configs, 1, num_config);
            checkForEGLErrors("eglChooseConfig");
            if (num_config[0] < 1) {
                throw new RuntimeException("Could not find a suitable config for EGL context!");
            }
            mEGLConfig = configs[0];

            int[] attribs = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
            mEGLContext = egl.eglCreateContext(mEGLDisplay, mEGLConfig, EGL10.EGL_NO_CONTEXT, attribs);
            checkForEGLErrors("eglCreateContext");

            int[] surfaceSize = { EGL10.EGL_WIDTH, 1920, EGL10.EGL_HEIGHT, 1080, EGL10.EGL_NONE };
            mEGLSurface = egl.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, surfaceSize);
            checkForEGLErrors("eglCreatePbufferSurface");
        }

        public void makeCurrent() {
            EGL10 egl = (EGL10)EGLContext.getEGL();
            egl.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
            checkForEGLErrors("eglMakeCurrent");
        }


        public void checkForEGLErrors(String operation) {
            EGL10 egl = (EGL10)EGLContext.getEGL();
            int error = egl.eglGetError();
            if (error != EGL10.EGL_SUCCESS) {
                throw new RuntimeException("Operation '" + operation + "' caused EGL error: " + error);
            }
        }

    }

    private static final float[] TEX_VERTICES = {
        0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f
    };

    private static final float[] POS_VERTICES = {
        -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f
    };

    public class TextureRenderer {

        private int mProgram;
        private int mTexSamplerHandle;
        private int mTexCoordHandle;
        private int mPosCoordHandle;

        private FloatBuffer mTexVertices;
        private FloatBuffer mPosVertices;

        private int mViewWidth;
        private int mViewHeight;

        private int mTexWidth;
        private int mTexHeight;

        private static final String VERTEX_SHADER =
            "attribute vec4 a_position;\n" +
            "attribute vec2 a_texcoord;\n" +
            "varying vec2 v_texcoord;\n" +
            "void main() {\n" +
            "  gl_Position = a_position;\n" +
            "  v_texcoord = a_texcoord;\n" +
            "}\n";

        private static final String FRAGMENT_SHADER =
            "precision mediump float;\n" +
            "uniform sampler2D tex_sampler;\n" +
            "varying vec2 v_texcoord;\n" +
            "void main() {\n" +
            "  gl_FragColor = texture2D(tex_sampler, v_texcoord);\n" +
            "}\n";



        private static final int FLOAT_SIZE_BYTES = 4;

        public void init() {
            // Create program
            mProgram = GLToolbox.createProgram(VERTEX_SHADER, FRAGMENT_SHADER);

            // Bind attributes and uniforms
            mTexSamplerHandle = GLES20.glGetUniformLocation(mProgram,
                    "tex_sampler");
            mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_texcoord");
            mPosCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_position");

            // Setup coordinate buffers
            mTexVertices = ByteBuffer.allocateDirect(
                    TEX_VERTICES.length * FLOAT_SIZE_BYTES)
                    .order(ByteOrder.nativeOrder()).asFloatBuffer();
            mTexVertices.put(TEX_VERTICES).position(0);
            mPosVertices = ByteBuffer.allocateDirect(
                    POS_VERTICES.length * FLOAT_SIZE_BYTES)
                    .order(ByteOrder.nativeOrder()).asFloatBuffer();
            mPosVertices.put(POS_VERTICES).position(0);
        }

        public void tearDown() {
            GLES20.glDeleteProgram(mProgram);
        }

        public void updateTextureSize(int texWidth, int texHeight) {
            mTexWidth = texWidth;
            mTexHeight = texHeight;
            computeOutputVertices();
        }

        public void updateViewSize(int viewWidth, int viewHeight) {
            mViewWidth = viewWidth;
            mViewHeight = viewHeight;
            computeOutputVertices();
        }

        public void renderTexture(int texId) {
            // Bind default FBO
            GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

            // Use our shader program
            GLES20.glUseProgram(mProgram);
            GLToolbox.checkGlError("glUseProgram");

            // Set viewport
            GLES20.glViewport(0, 0, mViewWidth, mViewHeight);
            GLToolbox.checkGlError("glViewport");

            // Disable blending
            GLES20.glDisable(GLES20.GL_BLEND);

            // Set the vertex attributes
            GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, false,
                    0, mTexVertices);
            GLES20.glEnableVertexAttribArray(mTexCoordHandle);
            GLES20.glVertexAttribPointer(mPosCoordHandle, 2, GLES20.GL_FLOAT, false,
                    0, mPosVertices);
            GLES20.glEnableVertexAttribArray(mPosCoordHandle);
            GLToolbox.checkGlError("vertex attribute setup");

            // Set the input texture
            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            GLToolbox.checkGlError("glActiveTexture");
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
            GLToolbox.checkGlError("glBindTexture");
            GLES20.glUniform1i(mTexSamplerHandle, 0);

            // Draw
            GLES20.glClearColor(0.0f, 0.0f, 0.5f, 1.0f);
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
            GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        }

        private void computeOutputVertices() {
            if (mPosVertices != null) {
                float imgAspectRatio = mTexWidth / (float)mTexHeight;
                float viewAspectRatio = mViewWidth / (float)mViewHeight;
                float relativeAspectRatio = viewAspectRatio / imgAspectRatio;
                float x0, y0, x1, y1;
                if (relativeAspectRatio > 1.0f) {
                    x0 = -1.0f / relativeAspectRatio;
                    y0 = -1.0f;
                    x1 = 1.0f / relativeAspectRatio;
                    y1 = 1.0f;
                } else {
                    x0 = -1.0f;
                    y0 = -relativeAspectRatio;
                    x1 = 1.0f;
                    y1 = relativeAspectRatio;
                }
                float[] coords = new float[] { x0, y0, x1, y0, x0, y1, x1, y1 };
                mPosVertices.put(coords).position(0);
            }
        }



    }

    public static class GLToolbox {

        public static int loadShader(int shaderType, String source) {
            int shader = GLES20.glCreateShader(shaderType);
            if (shader != 0) {
                GLES20.glShaderSource(shader, source);
                GLES20.glCompileShader(shader);
                int[] compiled = new int[1];
                GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
                if (compiled[0] == 0) {
                    String info = GLES20.glGetShaderInfoLog(shader);
                    GLES20.glDeleteShader(shader);
                    shader = 0;
                    throw new RuntimeException("Could not compile shader " +
                    shaderType + ":" + info);
                }
            }
            return shader;
        }

        public static int createProgram(String vertexSource,
                String fragmentSource) {
            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
            if (vertexShader == 0) {
                return 0;
            }
            int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
            if (pixelShader == 0) {
                return 0;
            }

            int program = GLES20.glCreateProgram();
            if (program != 0) {
                GLES20.glAttachShader(program, vertexShader);
                checkGlError("glAttachShader");
                GLES20.glAttachShader(program, pixelShader);
                checkGlError("glAttachShader");
                GLES20.glLinkProgram(program);
                int[] linkStatus = new int[1];
                GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus,
                        0);
                if (linkStatus[0] != GLES20.GL_TRUE) {
                    String info = GLES20.glGetProgramInfoLog(program);
                    GLES20.glDeleteProgram(program);
                    program = 0;
                    throw new RuntimeException("Could not link program: " + info);
                }
            }
            return program;
        }

        public static void checkGlError(String op) {
            int error;
            while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
                throw new RuntimeException(op + ": glError " + error);
            }
        }

        public static void initTexParams() {
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
                    GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
                    GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
                    GLES20.GL_CLAMP_TO_EDGE);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
                    GLES20.GL_CLAMP_TO_EDGE);
       }

    }
}

回答1:


So I finally figured it out myself after over clocking quite a few of the little grey cells.

The issue is with computeOutputVertices(); This might have worked with a GLSurfaceView, but for some reason, whatever its doing is not compatible with a PBuffer. Just comment that line out and it works beautifully.



来源:https://stackoverflow.com/questions/18141823/bitmap-texture-does-not-show-up-in-framebuffer-surface-on-android

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!