fdermishin
fdermishin

Reputation: 3696

How to render Bitmap off-screen in Android using OpenGL?

I need to render a bitmap without displaying it on the screen. For that I create OpenGL context using EGL14 as described in this answer. Then I save OpenGL surface to bitmap using GLES20.glReadPixels. But for some reason it is not rendered as expected and is just transparent.

import android.graphics.Bitmap
import android.opengl.*
import android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION
import java.nio.ByteBuffer


class Renderer {

    private lateinit var display: EGLDisplay
    private lateinit var surface: EGLSurface
    private lateinit var eglContext: EGLContext

    fun draw() {
        // Just a stub that fills the bitmap with red color
        GLES20.glClearColor(1f, 0f, 0f, 1f)
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
    }

    fun saveBitmap(): Bitmap {
        val width = 320
        val height = 240
        val mPixelBuf = ByteBuffer.allocate(width * height * 4)
        GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPixelBuf)
        return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    }

    private fun initializeEglContext() {
        display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)
        if (display == EGL14.EGL_NO_DISPLAY) {
            throw RuntimeException("eglGetDisplay failed ${EGL14.eglGetError()}")
        }

        val versions = IntArray(2)
        if (!EGL14.eglInitialize(display, versions, 0, versions, 1)) {
            throw RuntimeException("eglInitialize failed ${EGL14.eglGetError()}")
        }

        val configAttr = intArrayOf(
            EGL14.EGL_COLOR_BUFFER_TYPE, EGL14.EGL_RGB_BUFFER,
            EGL14.EGL_LEVEL, 0,
            EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
            EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
            EGL14.EGL_NONE
        )
        val configs: Array<EGLConfig?> = arrayOfNulls(1)
        val numConfig = IntArray(1)
        EGL14.eglChooseConfig(
            display, configAttr, 0,
            configs, 0, 1, numConfig, 0
        )
        if (numConfig[0] == 0) {
            throw RuntimeException("No configs found")
        }
        val config: EGLConfig? = configs[0]

        val surfAttr = intArrayOf(
            EGL14.EGL_WIDTH, 320,
            EGL14.EGL_HEIGHT, 240,
            EGL14.EGL_NONE
        )
        surface = EGL14.eglCreatePbufferSurface(display, config, surfAttr, 0)

        val contextAttrib = intArrayOf(
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL14.EGL_NONE
        )
        eglContext = EGL14.eglCreateContext(display, config, EGL14.EGL_NO_CONTEXT, contextAttrib, 0)

        EGL14.eglMakeCurrent(display, surface, surface, eglContext)
    }

    fun destroy() {
        EGL14.eglMakeCurrent(display, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
            EGL14.EGL_NO_CONTEXT)
        EGL14.eglDestroySurface(display, surface)
        EGL14.eglDestroyContext(display, eglContext)
        EGL14.eglTerminate(display)
    }
}

This is how I use it:

val renderer = Renderer()
renderer.initializeEglContext()
renderer.draw()
val bitmap = renderer.saveBitmap()
renderer.destroy()

The code runs without any errors. I checked that context is created successfully. For example GLES20.glCreateProgram works as expected and returns a valid id. The only warning I get is

W/OpenGLRenderer: Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...

But I'm not sure if it affects the result in any way. However bitmap is not filled with color and is transparent:

val color = bitmap[0, 0]
Log.d("Main", "onCreate: ${Color.valueOf(color)}")
Color(0.0, 0.0, 0.0, 0.0, sRGB IEC61966-2.1)

I guess that I'm missing something, but I can't figure out what. How to make it to actually render?

Upvotes: 2

Views: 1246

Answers (1)

Hihikomori
Hihikomori

Reputation: 990

Pixel buffer must be copied to bitmap:

val mPixelBuf bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
bitmap.copyPixelsFromBuffer(mPixelBuf)
return bitmap

Upvotes: 3

Related Questions