Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I am trying to draw a bitmap as an overlay on every frame of the video. I found an example on how to decode and encode a video and it is working. This example has a TextureRenderer class with a drawFrame function that I need to modify in order to add the bitmap. I am newbie to opengl but I learnt that I need to create a texture with the bitmap and bind it. I tried that in the following code but it is throwing an exception.

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. // from: https://android.googlesource.com/platform/cts/+/lollipop-release/tests/tests/media/src/android/media/cts/TextureRender.java // blob: 4125dcfcfed6ed7fddba5b71d657dec0d433da6a // modified: removed unused method bodies // modified: use GL_LINEAR for GL_TEXTURE_MIN_FILTER to improve quality. package com.example.name.videoeditortest; * Code for rendering a texture onto a surface using OpenGL ES 2.0. import android.graphics.Bitmap; import android.graphics.SurfaceTexture; import android.opengl.GLES11Ext; import android.opengl.GLES20; import android.opengl.GLUtils; import android.opengl.Matrix; import android.util.Log; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; * Code for rendering a texture onto a surface using OpenGL ES 2.0. class TextureRender { private Bitmap bitmap; private static final String TAG = "TextureRender"; private static final int FLOAT_SIZE_BYTES = 4; private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES; private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0; private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3; private final float[] mTriangleVerticesData = { // X, Y, Z, U, V -1.0f, -1.0f, 0, 0.f, 0.f, 1.0f, -1.0f, 0, 1.f, 0.f, -1.0f, 1.0f, 0, 0.f, 1.f, 1.0f, 1.0f, 0, 1.f, 1.f, private FloatBuffer mTriangleVertices; private static final String VERTEX_SHADER = "uniform mat4 uMVPMatrix;\n" + "uniform mat4 uSTMatrix;\n" + "attribute vec4 aPosition;\n" + "attribute vec4 aTextureCoord;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " gl_Position = uMVPMatrix * aPosition;\n" + " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" + "}\n"; private static final String FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + // highp here doesn't seem to matter "varying vec2 vTextureCoord;\n" + "uniform samplerExternalOES sTexture;\n" + "void main() {\n" + " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + "}\n"; private float[] mMVPMatrix = new float[16]; private float[] mSTMatrix = new float[16]; private int mProgram; private int mTextureID = -12345; private int mTextureBitmapID = -12345; private int muMVPMatrixHandle; private int muSTMatrixHandle; private int maPositionHandle; private int maTextureHandle; public TextureRender() { mTriangleVertices = ByteBuffer.allocateDirect( mTriangleVerticesData.length * FLOAT_SIZE_BYTES) .order(ByteOrder.nativeOrder()).asFloatBuffer(); mTriangleVertices.put(mTriangleVerticesData).position(0); Matrix.setIdentityM(mSTMatrix, 0); public int getTextureId() { return mTextureID; public void drawFrame(SurfaceTexture st) { checkGlError("onDrawFrame start"); st.getTransformMatrix(mSTMatrix); GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f); GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); GLES20.glUseProgram(mProgram); checkGlError("glUseProgram"); //Bing textrues GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID); GLES20.glActiveTexture(GLES20.GL_TEXTURE_2D); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureBitmapID); mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); checkGlError("glVertexAttribPointer maPosition"); GLES20.glEnableVertexAttribArray(maPositionHandle); checkGlError("glEnableVertexAttribArray maPositionHandle"); mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); checkGlError("glVertexAttribPointer maTextureHandle"); GLES20.glEnableVertexAttribArray(maTextureHandle); checkGlError("glEnableVertexAttribArray maTextureHandle"); Matrix.setIdentityM(mMVPMatrix, 0); GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0); GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); checkGlError("glDrawArrays"); GLES20.glFinish(); * Initializes GL state. Call this after the EGL surface has been created and made current. public void surfaceCreated() { mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER); if (mProgram == 0) { throw new RuntimeException("failed creating program"); maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); checkGlError("glGetAttribLocation aPosition"); if (maPositionHandle == -1) { throw new RuntimeException("Could not get attrib location for aPosition"); maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord"); checkGlError("glGetAttribLocation aTextureCoord"); if (maTextureHandle == -1) { throw new RuntimeException("Could not get attrib location for aTextureCoord"); muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); checkGlError("glGetUniformLocation uMVPMatrix"); if (muMVPMatrixHandle == -1) { throw new RuntimeException("Could not get attrib location for uMVPMatrix"); muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix"); checkGlError("glGetUniformLocation uSTMatrix"); if (muSTMatrixHandle == -1) { throw new RuntimeException("Could not get attrib location for uSTMatrix"); int[] textures = new int[1]; GLES20.glGenTextures(1, textures, 0); mTextureID = textures[0]; GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID); checkGlError("glBindTexture mTextureID"); GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); checkGlError("glTexParameter"); mTextureBitmapID = loadBitmapTexture(); private int loadBitmapTexture() final int[] textureHandle = new int[1]; GLES20.glGenTextures(1, textureHandle, 0); if (textureHandle[0] != 0) // Bind to the texture in OpenGL GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]); // Set filtering GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); // Load the bitmap into the bound texture. GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); if (textureHandle[0] == 0) throw new RuntimeException("Error loading texture."); return textureHandle[0]; * Replaces the fragment shader. public void changeFragmentShader(String fragmentShader) { GLES20.glDeleteProgram(mProgram); mProgram = createProgram(VERTEX_SHADER, fragmentShader); if (mProgram == 0) { throw new RuntimeException("failed creating program"); private int loadShader(int shaderType, String source) { int shader = GLES20.glCreateShader(shaderType); checkGlError("glCreateShader type=" + shaderType); 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) { Log.e(TAG, "Could not compile shader " + shaderType + ":"); Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); shader = 0; return shader; private 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(); checkGlError("glCreateProgram"); if (program == 0) { Log.e(TAG, "Could not create program"); 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) { Log.e(TAG, "Could not link program: "); Log.e(TAG, GLES20.glGetProgramInfoLog(program)); GLES20.glDeleteProgram(program); program = 0; return program; public void checkGlError(String op) { int error; while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { Log.e(TAG, op + ": glError " + error); throw new RuntimeException(op + ": glError " + error); public void setBitmap(Bitmap bitmap){ this.bitmap = bitmap; * Saves the current frame to disk as a PNG image. Frame starts from (0,0). * Useful for debugging. public static void saveFrame(String filename, int width, int height) { // glReadPixels gives us a ByteBuffer filled with what is essentially big-endian RGBA // data (i.e. a byte of red, followed by a byte of green...). We need an int[] filled // with native-order ARGB data to feed to Bitmap. // If we implement this as a series of buf.get() calls, we can spend 2.5 seconds just // copying data around for a 720p frame. It's better to do a bulk get() and then // rearrange the data in memory. (For comparison, the PNG compress takes about 500ms // for a trivial frame.) // So... we set the ByteBuffer to little-endian, which should turn the bulk IntBuffer // get() into a straight memcpy on most Android devices. Our ints will hold ABGR data. // Swapping B and R gives us ARGB. We need about 30ms for the bulk get(), and another // 270ms for the color swap. // Making this even more interesting is the upside-down nature of GL, which means we // may want to flip the image vertically here. ByteBuffer buf = ByteBuffer.allocateDirect(width * height * 4); buf.order(ByteOrder.LITTLE_ENDIAN); GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf); buf.rewind(); int pixelCount = width * height; int[] colors = new int[pixelCount]; buf.asIntBuffer().get(colors); for (int i = 0; i < pixelCount; i++) { int c = colors[i]; colors[i] = (c & 0xff00ff00) | ((c & 0x00ff0000) >> 16) | ((c & 0x000000ff) << 16); FileOutputStream fos = null; try { fos = new FileOutputStream(filename); Bitmap bmp = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888); bmp.compress(Bitmap.CompressFormat.PNG, 90, fos); bmp.recycle(); } catch (IOException ioe) { throw new RuntimeException("Failed to write file " + filename, ioe); } finally { try { if (fos != null) fos.close(); } catch (IOException ioe2) { throw new RuntimeException("Failed to close file " + filename, ioe2); Log.d(TAG, "Saved " + width + "x" + height + " frame as '" + filename + "'");

Exception thrown:

E/ExtractDecodeEditEncodeMuxTest: error while releasing muxer
                                   java.lang.IllegalStateException: Can't stop due to wrong state.
                                   at android.media.MediaMuxer.stop(MediaMuxer.java:231)
                                   at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest.extractDecodeEditEncodeMux(ExtractDecodeEditEncodeMuxTest.java 434)
                                   at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest.access$000(ExtractDecodeEditEncodeMuxTest.java:58)
                                   at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest$TestWrapper.run(ExtractDecodeEditEncodeMuxTest.java:171)
                                   at java.lang.Thread.run(Thread.java:841)

If I comment GLES20.glActiveTexture(GLES20.GL_TEXTURE_2D); in the drawFrame the video is rendered correctly but with not bitmap. If I comment GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureBitmapID); in the drawFrame I get the following exception:

java.lang.RuntimeException: glVertexAttribPointer maPosition: glError 1280
at com.example.name.videoeditortest.TextureRender.checkGlError(TextureRender.java:259)
at com.example.name.videoeditortest.TextureRender.drawFrame(TextureRender.java:111)
at com.example.name.videoeditortest.OutputSurface.drawImage(OutputSurface.java:252)
at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest.doExtractDecodeEditEncodeMux(ExtractDecodeEditEncodeMuxTest.java:793)
at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest.extractDecodeEditEncodeMux(ExtractDecodeEditEncodeMuxTest.java:341)
at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest.access$000(ExtractDecodeEditEncodeMuxTest.java:58)
at com.example.name.videoeditortest.ExtractDecodeEditEncodeMuxTest$TestWrapper.run(ExtractDecodeEditEncodeMuxTest.java:171)
at java.lang.Thread.run(Thread.java:841)

I see two things that seems wrong to me.

  • You are trying to bind everything at the same time, and hope that one call to GLES20.glDrawArrays() will draw everything.

  • You have only one shader, where you should have two : one for doing video texture rendering, and another for your bitmap layer rendering.

    What you must know is that a frame can be draw by multiple call to glDrawArrays, each call will just paint a little part over previously drawn stuff (basically).

    The first part of rendering a frame in your case should look to this :

    # init
    loadShaderForVideo()
    loadShaderForBitmapLayer()
    prepareYourArraysEtc()
    
    #loop
    GLClear()
    updateVideoTexture()
    drawFrame(){
       drawVideo()
       drawBitmap()
    drawVideo(){
        bindYourActiveTextureToVideo()
        setYourVertexAttribAndUniform()
        GLES20.glDrawArrays()
    drawBitmap() {
        bindYourActiveTextureToBitmap()
        setYourVertexAttribAndUniform() // This should be the same as above for video
        // Considering you want to draw above your video, consider activating the blending for transparency :
        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
        GLES20.glDrawArrays()
    

    Concerning the shader, just take a look at these :

    A common Vertex Shader for both :

    public static final String vertexDefaultShaderCode =
            "uniform mat4 uVPMatrix;" +
                    "uniform mat4 uModelMatrix;" + // uniform = input const
                    "attribute vec3 aPosition;" +  // attribute = input property different for each vertex
                    "attribute vec2 aTexCoordinate;" +
                    "varying vec2 vTexCoordinate;" +// varying = output property different for each pixel
                    "void main() {" +
                    "vTexCoordinate = aTexCoordinate;" +
                    "gl_Position = uVPMatrix * uModelMatrix * vec4(aPosition,1.0);" +
    

    Then a basic fragment shader ( for your bitmap 2D texture ) :

        public static final String fragmentDefaultShaderCode =
            "precision mediump float;" +
                    "uniform sampler2D uTexture;" +
                    "varying vec2 vTexCoordinate;" +
                    "void main() {" +
                    "  gl_FragColor = texture2D(uTexture, vTexCoordinate);" +
    

    Then a different version for video rendering :

        public static final String fragmentExternalShaderCode =
            "#extension GL_OES_EGL_image_external : require\n" +
                    "precision mediump float;" +
                    "uniform samplerExternalOES sTexture;" +
                    "varying vec2 vTexCoordinate;" +
                    "void main() {" +
                    "  gl_FragColor = texture2D(sTexture, vTexCoordinate);" +
    

    Thus you will need two Programs, one with the defaultVertexShader + defaultFragmentShader and another with defaultVertexShader + fragmentExternalShaderCode.

    Thank you for your answer, it's really helpful. As you can you see I stated that I expect from the bounty winner to provide a functioning code. Thanks again! – Ziad Halabi Sep 23, 2016 at 10:29 Yes I saw your conditions. I'll see that deeper if I have a too much free time, but can't do more right now. – J.Jacobs Sep 23, 2016 at 13:07

    Buggy but works. Per J.Jacobs-VP, you need to use two programs and two shaders, one for video frame and the other for the bitmap.

    * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. var gles = 2 * Code for rendering a texture onto a surface using OpenGL ES 2.0. internal class TextureRender { /** Video Frame **/ private val mTriangleVerticesData = floatArrayOf( // X, Y, Z, U, V -1.0f, -1.0f, 0f, 0f, 0f, 1.0f, -1.0f, 0f, 1f, 0f, -1.0f, 1.0f, 0f, 0f, 1f, 1.0f, 1.0f, 0f, 1f, 1f private val mTriangleVertices: FloatBuffer private val mMVPMatrix = FloatArray(16) private val mSTMatrix = FloatArray(16) private var mProgram = 0 var textureId = -12345 private set private var muMVPMatrixHandle = 0 private var muSTMatrixHandle = 0 private var maPositionHandle = 0 private var maTextureHandle = 0 /** Bitmap Overlay **/ private val mBitmapTriangleVerticesData = floatArrayOf( // X, Y, Z, U, V -1.0f, -1.0f, 0f, 0f, 0f, 1.0f, -1.0f, 0f, 1f, 0f, -1.0f, 1.0f, 0f, 0f, 1f, 1.0f, 1.0f, 0f, 1f, 1f private val mBitmapTriangleVertices: FloatBuffer private var mBitmapProgram = 1 private val mBitmapMVPMatrix = FloatArray(16) private val mBitmapSTMatrix = FloatArray(16) private var mBitmapuMVPMatrixHandle = 0 private var mBitmapuSTMatrixHandle = 0 private var mBitmapaPositionHandle = 0 private var mBitmapaTextureHandle = 0 var bitmapTextureId = -12345 private set fun drawFrame(st: SurfaceTexture, sourceWidth: Int, sourceHeight: Int, targetWidth: Int, targetHeight: Int, flipHorizontally: Boolean, flipVertically: Boolean) { if (gles == 2) { /** Draw Frame **/ println("drawFrame") checkGlError("onDrawFrame start") st.getTransformMatrix(mSTMatrix) //GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f) GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f) GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT or GLES20.GL_COLOR_BUFFER_BIT) GLES20.glUseProgram(mProgram) checkGlError("glUseProgram") // original GLES20.glActiveTexture(GLES20.GL_TEXTURE0) GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId) // vertices mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET) GLES20.glVertexAttribPointer( maPositionHandle, 3, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices checkGlError("glVertexAttribPointer maPosition") GLES20.glEnableVertexAttribArray(maPositionHandle) checkGlError("glEnableVertexAttribArray maPositionHandle") mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET) GLES20.glVertexAttribPointer( maTextureHandle, 2, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices checkGlError("glVertexAttribPointer maTextureHandle") GLES20.glEnableVertexAttribArray(maTextureHandle) checkGlError("glEnableVertexAttribArray maTextureHandle") Matrix.setIdentityM(mMVPMatrix, 0) GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0) GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0) GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4) checkGlError("glDrawArrays") /** Draw Bitmap **/ // bitmap // save to bitmap val bmp = savePixels(0, 0, sourceWidth, sourceHeight) // load texture from bitmap println("bmp: ${bmp?.width} ${bmp?.height}") if (bmp != null) { //val monochromeBmp = convertToBlackWhite(bmp)?.rotate(90f) var monochromeBmp = convertToBlackWhite(bmp) if (sourceHeight > sourceWidth) { if (monochromeBmp != null) { //monochromeBmp.rotate(90f) println("monochromeBmp: ${monochromeBmp?.width} ${monochromeBmp?.height}") println("drawFrame") checkGlError("onDrawFrame start") st.getTransformMatrix(mBitmapSTMatrix) GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f) GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT or GLES20.GL_COLOR_BUFFER_BIT) GLES20.glUseProgram(mBitmapProgram) checkGlError("glUseProgram") // original GLES20.glActiveTexture(GLES20.GL_TEXTURE1) GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, bitmapTextureId) // vertices mBitmapTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET) GLES20.glVertexAttribPointer( mBitmapaPositionHandle, 3, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mBitmapTriangleVertices checkGlError("glVertexAttribPointer mBitmapaPosition") GLES20.glEnableVertexAttribArray(mBitmapaPositionHandle) checkGlError("glEnableVertexAttribArray mBitmapaPositionHandle") mBitmapTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET) GLES20.glVertexAttribPointer( mBitmapaTextureHandle, 2, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mBitmapTriangleVertices checkGlError("glVertexAttribPointer mBitmapaTextureHandle") GLES20.glEnableVertexAttribArray(mBitmapaTextureHandle) checkGlError("glEnableVertexAttribArray maTextureHandle") Matrix.setIdentityM(mBitmapMVPMatrix, 0) GLES20.glUniformMatrix4fv(mBitmapuMVPMatrixHandle, 1, false, mBitmapMVPMatrix, 0) GLES20.glUniformMatrix4fv(mBitmapuSTMatrixHandle, 1, false, mBitmapSTMatrix, 0) if (monochromeBmp != null) { loadTexture(monochromeBmp) GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4) checkGlError("glDrawArrays") // finish GLES20.glFinish() * Save Bitmap as Grayscale private fun convertToBlackWhite(bmp: Bitmap): Bitmap? { val width = bmp.width val height = bmp.height val pixels = IntArray(width * height) bmp.getPixels(pixels, 0, width, 0, 0, width, height) val alpha = 0xFF shl 24 // ?bitmap?24? for (i in 0 until height) { for (j in 0 until width) { var grey = pixels[width * i + j] val red = grey and 0x00FF0000 shr 16 val green = grey and 0x0000FF00 shr 8 val blue = grey and 0x000000FF grey = (red * 0.3 + green * 0.59 + blue * 0.11).toInt() grey = alpha or (grey shl 16) or (grey shl 8) or grey pixels[width * i + j] = grey val newBmp = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565) newBmp.setPixels(pixels, 0, width, 0, 0, width, height) return newBmp Rotate Bitmap fun Bitmap.rotate(degrees: Float): Bitmap { val matrix = android.graphics.Matrix().apply { postRotate(degrees) } return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true) * Save Texture as Bitmap private fun savePixels(x: Int, y: Int, w: Int, h: Int): Bitmap? { val b = IntArray(w * (y + h)) val bt = IntArray(w * h) val ib: IntBuffer = IntBuffer.wrap(b) ib.position(0) GLES20.glReadPixels(0, 0, w, h, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, ib) var i = 0 var k = 0 while (i < h) { //remember, that OpenGL bitmap is incompatible with Android bitmap //and so, some correction need. for (j in 0 until w) { val pix = b[i * w + j] val pb = pix shr 16 and 0xff val pr = pix shl 16 and 0x00ff0000 val pix1 = pix and -0xff0100 or pr or pb bt[(h - k - 1) * w + j] = pix1 return Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888) fun saveImage(finalBitmap: Bitmap) { val generator = Random() var n = 10000 n = generator.nextInt(n) val fname = "Image-$n.jpg" val file = File(output(fname)!!.path!!) if (file.exists()) file.delete() try { val out = FileOutputStream(file) finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out) out.flush() out.close() } catch (e: Exception) { e.printStackTrace() private fun output(fileName: String) : Uri? { val root = mainctx!!.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS) val myDir = File("$root/Files/") if (!myDir.exists()) { Log.d("TAG", "$myDir doesn't exist") myDir.mkdirs() try { val f = File(myDir, fileName) if (f.exists()) { //println("File Exists! Deleting") f.delete() f.createNewFile() // file Uri //println("fileUri: $fileUri") return Uri.fromFile(f) // File Saved } catch (e: FileNotFoundException) { //println("FileNotFoundException") e.printStackTrace() return null } catch (e: IOException) { //println("IOException") e.printStackTrace() return null * Load Texture from Bitmap private fun loadTexture(bitmap: Bitmap) { println("loadTexture") // Bind to the texture in OpenGL GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, bitmapTextureId) // Set filtering GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST) GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST) // Load the bitmap into the bound texture. println("bitmap: ${bitmap.width} ${bitmap.height}") GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0) // check errors checkGlError("texImage2d"); // Recycle the bitmap, since its data has been loaded into OpenGL. bitmap.recycle() * Initializes GL state. Call this after the EGL surface has been created and made current. fun surfaceCreated() { println("surfaceCreated") if (gles == 2) { // video shader mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER) if (mProgram == 0) { throw RuntimeException("failed creating program") maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition") checkGlError("glGetAttribLocation aPosition") if (maPositionHandle == -1) { throw RuntimeException("Could not get attrib location for aPosition") maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord") checkGlError("glGetAttribLocation aTextureCoord") if (maTextureHandle == -1) { throw RuntimeException("Could not get attrib location for aTextureCoord") muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix") checkGlError("glGetUniformLocation uMVPMatrix") if (muMVPMatrixHandle == -1) { throw RuntimeException("Could not get attrib location for uMVPMatrix") muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix") checkGlError("glGetUniformLocation uSTMatrix") if (muSTMatrixHandle == -1) { throw RuntimeException("Could not get attrib location for uSTMatrix") val textures = IntArray(1) GLES20.glGenTextures(1, textures, 0) textureId = textures[0] GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId) checkGlError("glBindTexture mTextureID") GLES20.glTexParameterf( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST.toFloat() GLES20.glTexParameterf( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR.toFloat() GLES20.glTexParameteri( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE GLES20.glTexParameteri( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE checkGlError("glTexParameter") // bitmap shader mBitmapProgram = createProgram(VERTEX_SHADER, FRAGMENT_BITMAP_SHADER) if (mBitmapProgram == 0) { throw RuntimeException("failed creating program") mBitmapaPositionHandle = GLES20.glGetAttribLocation(mBitmapProgram, "aPosition") checkGlError("glGetAttribLocation aPosition") if (mBitmapaPositionHandle == -1) { throw RuntimeException("Could not get attrib location for aPosition") mBitmapaTextureHandle = GLES20.glGetAttribLocation(mBitmapProgram, "aTextureCoord") checkGlError("glGetAttribLocation aTextureCoord") if (mBitmapaTextureHandle == -1) { throw RuntimeException("Could not get attrib location for aTextureCoord") mBitmapuMVPMatrixHandle = GLES20.glGetUniformLocation(mBitmapProgram, "uMVPMatrix") checkGlError("glGetUniformLocation uMVPMatrix") if (mBitmapuMVPMatrixHandle == -1) { throw RuntimeException("Could not get attrib location for uMVPMatrix") mBitmapuSTMatrixHandle = GLES20.glGetUniformLocation(mBitmapProgram, "uSTMatrix") checkGlError("glGetUniformLocation uSTMatrix") if (mBitmapuSTMatrixHandle == -1) { throw RuntimeException("Could not get attrib location for uSTMatrix") val bmpTextures = IntArray(1) GLES20.glGenTextures(1, bmpTextures, 0) bitmapTextureId = textures[0] GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, bitmapTextureId) checkGlError("glBindTexture bitmapTextureId") GLES20.glTexParameterf( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST.toFloat() GLES20.glTexParameterf( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR.toFloat() GLES20.glTexParameteri( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE GLES20.glTexParameteri( GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE checkGlError("glTexParameter") * Replaces the fragment shader. fun changeFragmentShader(fragmentShader: String) { if (gles == 2) { GLES20.glDeleteProgram(mProgram) mProgram = createProgram(VERTEX_SHADER, fragmentShader) if (mProgram == 0) { throw RuntimeException("failed creating program") private fun loadShader(shaderType: Int, source: String): Int { if (gles == 2) { var shader = GLES20.glCreateShader(shaderType) checkGlError("glCreateShader type=$shaderType") GLES20.glShaderSource(shader, source) GLES20.glCompileShader(shader) val compiled = IntArray(1) GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0) if (compiled[0] == 0) { Log.e(TAG, "Could not compile shader $shaderType:") Log.e(TAG, " " + GLES20.glGetShaderInfoLog(shader)) GLES20.glDeleteShader(shader) shader = 0 return shader private fun createProgram(vertexSource: String, fragmentSource: String): Int { if (gles == 2) { val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource) if (vertexShader == 0) { return 0 val pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource) if (pixelShader == 0) { return 0 var program = GLES20.glCreateProgram() checkGlError("glCreateProgram") if (program == 0) { Log.e(TAG, "Could not create program") GLES20.glAttachShader(program, vertexShader) checkGlError("glAttachShader") GLES20.glAttachShader(program, pixelShader) checkGlError("glAttachShader") GLES20.glLinkProgram(program) val linkStatus = IntArray(1) GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0) if (linkStatus[0] != GLES20.GL_TRUE) { Log.e(TAG, "Could not link program: ") Log.e(TAG, GLES20.glGetProgramInfoLog(program)) GLES20.glDeleteProgram(program) program = 0 return program fun checkGlError(op: String) { if (gles == 2) { var error: Int while (GLES20.glGetError().also { error = it } != GLES20.GL_NO_ERROR) { Log.e(TAG, "$op: glError $error") throw RuntimeException("$op: glError $error") companion object { private const val TAG = "TextureRender" private const val FLOAT_SIZE_BYTES = 4 private const val TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES private const val TRIANGLE_VERTICES_DATA_POS_OFFSET = 0 private const val TRIANGLE_VERTICES_DATA_UV_OFFSET = 3 private const val VERTEX_SHADER = "uniform mat4 uMVPMatrix;\n" + "uniform mat4 uSTMatrix;\n" + "attribute vec4 aPosition;\n" + "attribute vec4 aTextureCoord;\n" + "varying vec2 vTextureCoord;\n" + "void main() {\n" + " gl_Position = uMVPMatrix * aPosition;\n" + " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" + "}\n" private const val FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + // highp here doesn't seem to matter "varying vec2 vTextureCoord;\n" + "uniform samplerExternalOES sTexture;\n" + "void main() {\n" + " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + "}\n" private const val FRAGMENT_BITMAP_SHADER = "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + // highp here doesn't seem to matter "varying vec2 vTextureCoord;\n" + "uniform sampler2D uTexture;\n" + "void main() {\n" + " gl_FragColor = texture2D(uTexture, vTextureCoord);\n" + "}\n" init { mTriangleVertices = ByteBuffer.allocateDirect( mTriangleVerticesData.size * FLOAT_SIZE_BYTES .order(ByteOrder.nativeOrder()).asFloatBuffer() mTriangleVertices.put(mTriangleVerticesData).position(0) Matrix.setIdentityM(mSTMatrix, 0) mBitmapTriangleVertices = ByteBuffer.allocateDirect( mBitmapTriangleVerticesData.size * FLOAT_SIZE_BYTES .order(ByteOrder.nativeOrder()).asFloatBuffer() mBitmapTriangleVertices.put(mBitmapTriangleVerticesData).position(0) Matrix.setIdentityM(mBitmapSTMatrix, 0)

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.

  •