树莓派raspberry pi3使用opengl渲染图片


运行环境:raspberry pi3 官方jessie系统,QT 5.3.2

本文应用场景:FFMPEG解码视频获得图片帧,使用opengl进行显示。

代码:glplayer.h

#ifndef GLPLAYER_H
#define GLPLAYER_H

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include "bcm_host.h"

typedef struct
{
    // Handle to a program object
    GLuint programObject;
   // Attribute locations
   GLint  positionLoc;
   GLint  texCoordLoc;
   // Sampler location
   GLint samplerLoc;
   // Texture handle
   GLuint textureId;
} UserData;

typedef struct CUBE_STATE_T
{
    uint32_t width;
    uint32_t height;

    EGLDisplay display;
    EGLSurface surface;
    EGLContext context;

    EGL_DISPMANX_WINDOW_T nativewindow;
    UserData *user_data;

    DISPMANX_ELEMENT_HANDLE_T dispman_element;
    DISPMANX_DISPLAY_HANDLE_T dispman_display;
    DISPMANX_UPDATE_HANDLE_T dispman_update;
} CUBE_STATE_T;

class GlPlayer{
private:

    GLuint createSimpleTexture2D();
    GLuint loadShader(GLenum type, const char *shaderSrc);
    GLuint loadProgram (const char *vertShaderSrc, const char *fragShaderSrc);
    void initEGL( int width, int height);
    int init();
    void releaseEGL();
public:
    GlPlayer(int width, int height);
    ~GlPlayer();
    void draw(char * image);

public:
    int m_width;
    int m_height;
    CUBE_STATE_T *p_state;
    UserData *user_data;
};

#endif // GLPLAYER_H


GlPlayer.cpp
#include "glplayer.h"
#include 
#include 
#include 

GlPlayer::GlPlayer(int width, int height) :
    user_data(new UserData()),
    p_state(new CUBE_STATE_T()),
    m_width(width),
    m_height(height)
{
    p_state->user_data = user_data;
    initEGL(m_width, m_height);
    if(!init()) exit(1);
}

GlPlayer::~GlPlayer(){
    fprintf(stderr, "~GlPlayer()\n");
    releaseEGL();
    if(user_data != NULL) delete user_data;
    if(p_state != NULL) delete p_state;
}

// Create a simple width x height texture image with four different colors
//
GLuint GlPlayer::createSimpleTexture2D(){
   // Texture object handle
   GLuint textureId;
   // Use tightly packed data
   glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 );
   // Generate a texture object
   glGenTextures ( 1, &textureId );
   // Bind the texture object
   glBindTexture ( GL_TEXTURE_2D, textureId );
   // Set the filtering mode
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
   return textureId;
}


// Create a shader object, load the shader source, and
// compile the shader.
//
GLuint GlPlayer::loadShader(GLenum type, const char *shaderSrc){
    GLuint shader;
    GLint compiled;
    // Create the shader object
    shader = glCreateShader(type);
    if(shader == 0)
    return 0;
    // Load the shader source
    glShaderSource(shader, 1, &shaderSrc, NULL);
    // Compile the shader
    glCompileShader(shader);
    // Check the compile status
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    if(!compiled){
        GLint infoLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
        if(infoLen > 1){
            char* infoLog = (char*)malloc(sizeof(char) * infoLen);
            glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
            fprintf(stderr, "Error compiling shader:\n%s\n", infoLog);
            free(infoLog);
        }
        glDeleteShader(shader);
        return 0;
    }
    return shader;
}

GLuint GlPlayer::loadProgram ( const char *vertShaderSrc, const char *fragShaderSrc ){
   GLuint vertexShader;
   GLuint fragmentShader;
   GLuint programObject;
   GLint linked;

   // Load the vertex/fragment shaders
   vertexShader = loadShader ( GL_VERTEX_SHADER, vertShaderSrc );
   if ( vertexShader == 0 ) return 0;

   fragmentShader = loadShader ( GL_FRAGMENT_SHADER, fragShaderSrc );
   if ( fragmentShader == 0 ){
      glDeleteShader( vertexShader );
      return 0;
   }

   // Create the program object
   programObject = glCreateProgram ();
   if ( programObject == 0 ) return 0;

   glAttachShader ( programObject, vertexShader );
   glAttachShader ( programObject, fragmentShader );

   // Link the program
   glLinkProgram ( programObject );

   // Check the link status
   glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );
   if ( !linked ){
      GLint infoLen = 0;
      glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );
      if ( infoLen > 1 ){
         char* infoLog = (char*)malloc (sizeof(char) * infoLen );
         glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
         fprintf (stderr, "Error linking program:\n%s\n", infoLog );
         free ( infoLog );
      }

      glDeleteProgram ( programObject );
      return 0;
   }

   // Free up no longer needed shader resources
   glDeleteShader ( vertexShader );
   glDeleteShader ( fragmentShader );

   return programObject;
}

void GlPlayer::initEGL(int width, int height){
    int32_t success = 0;
    EGLBoolean result;
    EGLint num_config;

    bcm_host_init();

    VC_RECT_T dst_rect;
    VC_RECT_T src_rect;

    static const EGLint attribute_list[] =
    {
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
        EGL_NONE
    };

    static const EGLint context_attributes[] =
    {
        EGL_CONTEXT_CLIENT_VERSION, 2,
        EGL_NONE
    };

    EGLConfig config;

    // get an EGL display connection
    p_state->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

    // initialize the EGL display connection
    result = eglInitialize(p_state->display, NULL, NULL);

    // get an appropriate EGL frame buffer configuration
    result = eglChooseConfig(p_state->display, attribute_list, &config, 1, &num_config);
    assert(EGL_FALSE != result);

    // get an appropriate EGL frame buffer configuration
    result = eglBindAPI(EGL_OPENGL_ES_API);
    assert(EGL_FALSE != result);

    // create an EGL rendering context
    p_state->context = eglCreateContext(p_state->display, config, EGL_NO_CONTEXT, context_attributes);
    assert(p_state->context!=EGL_NO_CONTEXT);

    // create an EGL window surface
    success = graphics_get_display_size(0 /* LCD */, &p_state->width, &p_state->height);
    assert( success >= 0 );

    p_state->width = width;
    p_state->height = height;

    QDesktopWidget *dwsktopwidget = QApplication::desktop();
    QRect deskrect = dwsktopwidget->availableGeometry();

    dst_rect.x = 0;
    dst_rect.y = 0;
    dst_rect.width = deskrect.width();
    dst_rect.height = deskrect.height();

    src_rect.x = 0;
    src_rect.y = 0;
    src_rect.width = p_state->width;
    src_rect.height = p_state->height;

    p_state->dispman_display = vc_dispmanx_display_open( 0 /* LCD */);
    p_state->dispman_update = vc_dispmanx_update_start( 0 );

    p_state->dispman_element =
    vc_dispmanx_element_add(p_state->dispman_update, p_state->dispman_display,
                0/*layer*/, &dst_rect, 0/*src*/,
                &src_rect, DISPMANX_PROTECTION_NONE,
                0 /*alpha*/, 0/*clamp*/, DISPMANX_NO_ROTATE/*transform*/);

    p_state->nativewindow.element = p_state->dispman_element;
    p_state->nativewindow.width = deskrect.width();
    p_state->nativewindow.height = deskrect.height();
    vc_dispmanx_update_submit_sync( p_state->dispman_update );

    p_state->surface = eglCreateWindowSurface( p_state->display, config, &(p_state->nativewindow), NULL );
    assert(p_state->surface != EGL_NO_SURFACE);

    // connect the context to the surface
    result = eglMakeCurrent(p_state->display, p_state->surface, p_state->surface, p_state->context);
    assert(EGL_FALSE != result);
}

///
// Initialize the shader and program object
//
int GlPlayer::init(){
   UserData *userData = p_state->user_data;
   memset(userData, 0, sizeof(UserData));
   char vShaderStr[] =
      "attribute vec4 a_position;   \n"
      "attribute vec2 a_texCoord;   \n"
      "varying vec2 v_texCoord;     \n"
      "void main()                  \n"
      "{                            \n"
      "   gl_Position = a_position; \n"
      "   v_texCoord = a_texCoord;  \n"
      "}                            \n";

   char fShaderStr[] =
      "precision mediump float;                            \n"
      "varying vec2 v_texCoord;                            \n"
      "uniform sampler2D s_texture;                        \n"
      "void main()                                         \n"
      "{                                                   \n"
      "  gl_FragColor = texture2D( s_texture, v_texCoord );\n"
      "}                                                   \n";

   // Load the shaders and get a linked program object
   userData->programObject = loadProgram ( vShaderStr, fShaderStr );
   // Get the attribute locations
   userData->positionLoc = glGetAttribLocation ( userData->programObject, "a_position" );
   userData->texCoordLoc = glGetAttribLocation ( userData->programObject, "a_texCoord" );
   // Get the sampler location
   userData->samplerLoc = glGetUniformLocation ( userData->programObject, "s_texture" );
   // Load the texture
   userData->textureId = createSimpleTexture2D ();

   // Set the viewport
   QDesktopWidget *dwsktopwidget = QApplication::desktop();
   QRect deskrect = dwsktopwidget->availableGeometry();
   glViewport (0, 0, deskrect.width(), deskrect.height());
   glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f );

   // Clear the color buffer
   glClear ( GL_COLOR_BUFFER_BIT );

   // Use the program object
   glUseProgram ( userData->programObject );
   // Bind the texture
   glActiveTexture ( GL_TEXTURE0 );
   glBindTexture ( GL_TEXTURE_2D, userData->textureId );

   glEnableVertexAttribArray ( userData->positionLoc );
   glEnableVertexAttribArray ( userData->texCoordLoc );
   // Set the sampler texture unit to 0
   glUniform1i ( userData->samplerLoc, 0 );
   return GL_TRUE;
}

// Draw triangles using the shader pair created in Init()
//
void GlPlayer::draw(char * image){
    if(image == NULL){
        printf("image error\n");
        return;
    }
    if(p_state == NULL){
        printf("p_state error\n");
        return;
    }
    UserData *userData = p_state->user_data;
    if(userData == NULL){
        printf("userData error\n");
        return;
    }
    printf("draw image, size %dx%d\n",m_width,m_height);
#if 0
    QImage img((uchar *)image,m_width,m_height,QImage::Format_ARGB32);
    QDateTime time = QDateTime::currentDateTime();
    QString filename = "/home/pi/image/"+time.toString("yyyy-MM-dd-hh-mm-ss")+".png";
    img.save(filename);
#endif


    glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);
    GLfloat vVertices[] = { -1.0f,  1.0f, 0.0f,  // Position 0
                            0.0f,  0.0f,        // TexCoord 0
                           -1.0f, -1.0f, 0.0f,  // Position 1
                            0.0f,  1.0f,        // TexCoord 1
                            1.0f, -1.0f, 0.0f,  // Position 2
                            1.0f,  1.0f,        // TexCoord 2
                            1.0f,  1.0f, 0.0f,  // Position 3
                            1.0f,  0.0f         // TexCoord 3
                          };
     GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
    //GLushort indices[] = {1, 0, 3, 0, 2, 0, 1 };

    // Load the vertex position
    glVertexAttribPointer ( userData->positionLoc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vVertices );
    // Load the texture coordinate
    glVertexAttribPointer ( userData->texCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &vVertices[3] );

    glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices );
    eglSwapBuffers(p_state->display, p_state->surface);
}

void GlPlayer::releaseEGL(){
    eglMakeCurrent(p_state->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroySurface(p_state->display, p_state->surface);
    eglDestroyContext(p_state->display, p_state->context);
    eglTerminate(p_state->display);
}

调用方式:

GlPlayer* glplayer = new GlPlayer(width, height);

glplayer->draw(image_addr);

delete glplayer;


参考:

https://jan.newmarch.name/LinuxSound/Diversions/RaspberryPiOpenGL/


你可能感兴趣的:(树莓派)