Using QImage with OpenGL

Prime picture Prime · Mar 17, 2011 · Viewed 25.8k times · Source

I've very recently picked up Qt and am using it with OpenGL The thing though is that when moving my SDL code to Qt and changing the texture code to use QImage it stops working.

The image does load correctly, as shown through the error checking code.

Thanks!

P.S: Please don't suggest I use glDrawPixels, I need to fix the problem at hand. Some of the reasons for that being 1. slow 2. android (which this code may be running on eventually) is OpenGL ES and does not support glDrawPixels

Here's the code:

//original image
QImage img;
if(!img.load(":/star.png"))
{
    //loads correctly
    qWarning("ERROR LOADING IMAGE");
}

//array for holding texture ID
GLuint texture[1];

//get the OpenGL-friendly image
QImage GL_formatted_image;
GL_formatted_image = QGLWidget::convertToGLFormat(img);

//make sure its not null
if(GL_formatted_image.isNull())
    qWarning("IMAGE IS NULL");
else
    qWarning("IMAGE NOT NULL");

//generate the texture name
glGenTextures(1, texture);

//bind the texture ID
glBindTexture(GL_TEXTURE_2D, texture[0]);

//generate the texture
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, GL_formatted_image.width(),
              GL_formatted_image.height(),
              0, GL_RGBA, GL_UNSIGNED_BYTE, GL_formatted_image.bits() );

//texture parameters
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);



//draw the texture
glPushMatrix();
glTranslatef(-2.0f, 0.0f, 0.0f);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_TRIANGLES);
    glVertex2f(1.0f, 0.0f);
 glTexCoord2f(1.0f, 0.0f);
    glVertex2f(0.0f, 1.0f);
 glTexCoord2f(0.0f, 1.0f);
    glVertex2f(0.0f, 0.0f);
 glTexCoord2f(0.0f, 0.0f);
glEnd();
glPopMatrix();

Here's the original texture loading function with SDL:

GLuint loadTexturewithSDL(const char* FILE, GLenum texture_format)
{
    GLuint texture;         // This is a handle to our texture object
    SDL_Surface *surface;   // This surface will tell us the details of the image
    GLint  nOfColors;



    if ( (surface = SDL_LoadBMP(FILE)) ) {

    // Check that the image's width is a power of 2
    if ( (surface->w & (surface->w - 1)) != 0 ) {
        printf("warning: image's width is not a power of 2\n");
    }

    // Also check if the height is a power of 2
    if ( (surface->h & (surface->h - 1)) != 0 ) {
        printf("warning: image's height is not a power of 2\n");
    }

        // get the number of channels in the SDL surface
        nOfColors = surface->format->BytesPerPixel;
        if (nOfColors == 4)     // contains an alpha channel
        {
                if (surface->format->Rmask == 0x000000ff)
                        texture_format = GL_RGBA;
                else
                        texture_format = GL_BGRA;
        } else if (nOfColors == 3)     // no alpha channel
        {
                if (surface->format->Rmask == 0x000000ff)
                        texture_format = GL_RGB;
                else
                        texture_format = GL_BGR;
        } else {
                printf("warning: the image is not truecolor..  this will probably break\n");
                // this error should not go unhandled
        }

    // Have OpenGL generate a texture object handle for us
    glGenTextures( 1, &texture );

    // Bind the texture object
    glBindTexture( GL_TEXTURE_2D, texture );

    // Set the texture's stretching properties
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

    // Edit the texture object's image data using the information SDL_Surface gives us
    glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, surface->w, surface->h, 0,
                      texture_format, GL_UNSIGNED_BYTE, surface->pixels );
}
else {
    printf("SDL could not load image %s\n", SDL_GetError());
    SDL_Quit();
    return 1;
}

// Free the SDL_Surface only if it was successfully created
if ( surface ) {
    SDL_FreeSurface( surface );
}

    return texture;
}

Answer

Martin Beckett picture Martin Beckett · Mar 18, 2011

I have similar code that works but uses glTexSubImage2D :

void Widget::paintGL() 
{
    glClear (GL_COLOR_BUFFER_BIT);       
    glDisable(GL_DEPTH_TEST);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();        
    gluOrtho2D(0,win.width(),0,win.height());
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();        
    glEnable(GL_TEXTURE_2D);

    glBindTexture(GL_TEXTURE_2D,texture); 
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0,0 , image.width(), image.height(),  glFormat, glType, image.bits() );       
    glBegin(GL_QUADS);   // in theory triangles are better
    glTexCoord2i(0,0); glVertex2i(0,win.height());
    glTexCoord2i(0,1); glVertex2i(0,0);
    glTexCoord2i(1,1); glVertex2i(win.width(),0);
    glTexCoord2i(1,0); glVertex2i(win.width(),win.height());
    glEnd();             

    glFlush();
}


void Widget::initializeGL() 
{
    glClearColor (0.0,0.0,0.0,1.0);
    glDisable(GL_DEPTH_TEST);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();        
    gluOrtho2D(0,win.width(),0,win.height());
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();        

    glEnable(GL_TEXTURE_2D);
    glGenTextures(3,&texture);
    glBindTexture(GL_TEXTURE_2D,texture);       
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);          
    glBindTexture(GL_TEXTURE_2D,texture);               
    glTexImage2D(GL_TEXTURE_2D, 0, glFormat, image.width(), image.height(), 0, glFormat, glType, NULL );    

    glDisable(GL_TEXTURE_2D);
}

And a few perfomance tweaks in the ctor

void Widget::setDisplayOptions()
{
    glFormat = GL_RGB;  //  QImage RGBA is BGRA
    glType = GL_UNSIGNED_BYTE;

    QGL::setPreferredPaintEngine(QPaintEngine::OpenGL2);

    QGLFormat glFmt;
    glFmt.setSwapInterval(1); // 1= vsync on 
    glFmt.setAlpha(GL_RGBA==glFormat);
    glFmt.setRgba(GL_RGBA==glFormat); 
    glFmt.setDoubleBuffer(true); // default
    glFmt.setOverlay(false);
    glFmt.setSampleBuffers(false);
    QGLFormat::setDefaultFormat(glFmt);

    setAttribute(Qt::WA_OpaquePaintEvent,true);
    setAttribute(Qt::WA_PaintOnScreen,true);        
}