Search code examples
androidopengl-espixmapnative-activitygldrawpixels

Native Activity, GLES2.0: Uploading pixel array to screen


I created a pixmap using, for example, this code

//...
// Native Activity

#define HEIGHT 600
#define WIDTH 600
uint32_t texDat[WIDTH*HEIGHT*4];
static double it = 0;
uint8_t color = sin(it)*256;

for (int i = 0; i < WIDTH; i++) for (int j = 0; j < HEIGHT; j++)
texDat [ i + j*WIDTH ] = (i << 16)  | (j << 8) | (color<< 0);
it+=0.01;
if(it >= M_PI) it = 0;

On my Linux pc this pixmap can be shown with glDrawPixels or with glTexImage2D. But I can't find glDrawPixels or something same on Android GLES. I tried to copy image to screen with this

ANativeWindow_Buffer pbuffer;
if (ANativeWindow_lock(engine->app->window, &pbuffer, NULL) == 0) {
    memcpy(pbuffer.bits, texDat,  WIDTH*HEIGHT);
    ANativeWindow_unlockAndPost(engine->app->window);
}

But it fails even if copying size is set to 1. How can I upload pixmap to screen?

Thanks.


Solution

  • GLES shader-based solution. Based on Android test

    #include <stdio.h>
    #include <stdbool.h>
    #include <stdlib.h>
    #include <EGL/egl.h>
    #include <GLES2/gl2.h>
    
    #define LOGI(...) (printf("(I): " __VA_ARGS__))
    #define LOGW(...) (printf("(W): " __VA_ARGS__))
    #define LOGE(...) (printf("(E): " __VA_ARGS__))
    
    static GLuint gTextureProgram;
    static GLuint gvTexturePositionHandle;
    static GLuint gvTextureTexCoordsHandle;
    static GLuint gvTextureSamplerHandle;
    
    static const char gSimpleVS[] =
        "attribute vec4 position;\n"
        "attribute vec2 texCoords;\n"
        "varying vec2 outTexCoords;\n"
        "\nvoid main(void) {\n"
        "   outTexCoords = texCoords;\n"
        "   gl_Position = position;\n"
        "}\n\n";
    static const char gSimpleFS[] =
        "precision mediump float;\n\n"
        "varying vec2 outTexCoords;\n"
        "uniform sampler2D texture;\n"
        "\nvoid main(void) {\n"
        "   gl_FragColor = texture2D(texture, outTexCoords);\n"
        "}\n\n";
    
    static void checkGlError(const char* op) {
        GLint error;
        for (error = glGetError(); error; error = glGetError()) {
            LOGE("after %s() glError (0x%x)\n", op, error);
        }
    }
    static GLuint loadShader(GLenum shaderType, const char* pSource) {
        GLuint shader = glCreateShader(shaderType);
        if (shader) {
            glShaderSource(shader, 1, &pSource, NULL);
            glCompileShader(shader);
            GLint compiled = 0;
            glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
            if (!compiled) {
                GLint infoLen = 0;
                glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
                if (infoLen) {
                    char* buf = (char*) malloc(infoLen);
                    if (buf) {
                        glGetShaderInfoLog(shader, infoLen, NULL, buf);
                        LOGE("Could not compile shader %d:\n%s\n",
                                shaderType, buf);
                        free(buf);
                    }
                    glDeleteShader(shader);
                    shader = 0;
                }
            }
        }
        return shader;
    }
    
    GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
        GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
        if (!vertexShader) {
            return 0;
        }
    
        GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
        if (!pixelShader) {
            return 0;
        }
    
        GLuint program = glCreateProgram();
        if (program) {
            glAttachShader(program, vertexShader);
            checkGlError("glAttachShader");
            glAttachShader(program, pixelShader);
            checkGlError("glAttachShader");
            glLinkProgram(program);
            GLint linkStatus = GL_FALSE;
            glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
            if (linkStatus != GL_TRUE) {
                GLint bufLength = 0;
                glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
                if (bufLength) {
                    char* buf = (char*) malloc(bufLength);
                    if (buf) {
                        glGetProgramInfoLog(program, bufLength, NULL, buf);
                        LOGE("Could not link program:\n%s\n", buf);
                        free(buf);
                    }
                }
                glDeleteProgram(program);
                program = 0;
            }
        }
        return program;
    }
    
    bool setupGraphics(void) {
    
        gTextureProgram = createProgram(gSimpleVS, gSimpleFS);
        if (!gTextureProgram) {
            return false;
        }
        gvTexturePositionHandle = glGetAttribLocation(gTextureProgram, "position"); checkGlError("glGetAttribLocation");
        gvTextureTexCoordsHandle = glGetAttribLocation(gTextureProgram, "texCoords"); checkGlError("glGetAttribLocation");
        gvTextureSamplerHandle = glGetUniformLocation(gTextureProgram, "texture"); checkGlError("glGetAttribLocation");
    
        return true;
    }
    
    const GLfloat gTriangleVertices[] = { 0.0f, 0.5f, -0.5f, -0.5f,
            0.5f, -0.5f };
    
    #define FLOAT_SIZE_BYTES 4;
    const GLint TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
    const GLfloat gTriangleVerticesData[] = {
        // 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,
    };
    
    void glDrawTex(void) {
        if (!gTextureProgram) {
            if(!setupGraphics()) {
                LOGE("Could not set up graphics.\n");
                return;
            }
        }
        glUseProgram(gTextureProgram); checkGlError("glUseProgram");
    
        glVertexAttribPointer(gvTexturePositionHandle, 3, GL_FLOAT, GL_FALSE,
                TRIANGLE_VERTICES_DATA_STRIDE_BYTES, gTriangleVerticesData);
        checkGlError("glVertexAttribPointer");
        glVertexAttribPointer(gvTextureTexCoordsHandle, 2, GL_FLOAT, GL_FALSE,
                TRIANGLE_VERTICES_DATA_STRIDE_BYTES, &gTriangleVerticesData[3]);
        checkGlError("glVertexAttribPointer");
        glEnableVertexAttribArray(gvTexturePositionHandle);
        glEnableVertexAttribArray(gvTextureTexCoordsHandle);
        checkGlError("glEnableVertexAttribArray");
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        checkGlError("glDrawArrays");
    
        glUseProgram(0); checkGlError("glUseProgram");
    }
    
    static GLuint gPixelsTexture;
    
    void glDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid * data){
        if(!gPixelsTexture) glGenTextures(1, &gPixelsTexture);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, gPixelsTexture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, type, data);
        glDrawTex();
        glBindTexture(GL_TEXTURE_2D, 0);
    }