Using QImage with OpenGL
Asked Answered
A

3

9

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;
}
Antimatter answered 17/3, 2011 at 5:25 Comment(3)
Could you elaborate a bit more on what you mean with "stopped" working? The texture is not displayed anymore? Did you change any parameters for glTexImage2D except the obvious GL_formatted_image.xxx? Does your image supply an alpha channel, i.e. RGBA?Waadt
changing GL_RGBA to GL_RGB gives the same result By stopped working, I mean the texture is not displayedAntimatter
Is the code you posted first part of one function? How does your draw loop really look like? Please also check fa's post to see whether you did the initialization in a similar way.Waadt
S
6

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);        
}
Shuffle answered 18/3, 2011 at 1:8 Comment(1)
You are doing too much in the paint function. There are no needs to set projection/view matrices. It should be done only in the resize function. Also you are sending data to GPU on every repaint.Rossanarosse
G
6

This is my solution for conversion from Qt to GL This also can work in reverse with little changes; Cheers -- Daimon

void Image::Convert32bitARGBtoRGBA()
{
    if(!isQtImage()) return;
    QImage& q = *(m_data->image);
    U32 count=0, max=(U32)(q.height()*q.width());
    U32* p = (U32*)(q.bits());
    U32 n;
    while( count<max )
    {
        n = p[count];   //n = ARGB
        p[count] =  0x00000000 |
                ((n<<8)  & 0x0000ff00) |
                ((n<<8)  & 0x00ff0000) |
                ((n<<8)  & 0xff000000) |
                ((n>>24) & 0x000000ff);
                // p[count] = RGBA
        count++;
    }
}

void Image::Convert32bitRGBAtoARGB()
{
    if(!isQtImage()) return;
    QImage& q = *(m_data->image);
    U32 count=0, max=(U32)(q.height()*q.width());
    U32* p = (U32*)(q.bits());
    U32 n;
    while( count<max )
    {
        n = p[count];   //n = RGBA
        p[count] =  0x00000000 |
                ((n>>8)  & 0x000000ff) |
                ((n>>8)  & 0x0000ff00) |
                ((n>>8)  & 0x00ff0000) |
                ((n<<24) & 0xff000000);
                // p[count] = ARGB
        count++;
    }
}
Gneiss answered 15/12, 2011 at 18:43 Comment(2)
Or use QImage::rgbSwapped()?Becka
@Dreiven: No, rgbSwapped() converts ARGB to ABGR.Hardaway
R
4

It looks like your problem is not here, as I have no problem with the following code. You should check your GL init and display setup.

Have you a glEnable(GL_TEXTURE_2D) somewhere ?

Also note glTexCoord2f must be before glVertex2f.

#include <GL/glut.h>
#include <QtOpenGL/qgl.h>
#include <iostream>

GLuint texture[1] ;

void LoadGLTextures( const char * name )
{
    QImage img;
    if( ! img.load( name ) )
    {
        std::cerr << "error loading " << name << std::endl ;
        exit( 1 );
    }

    QImage GL_formatted_image;
    GL_formatted_image = QGLWidget::convertToGLFormat(img);
    if( GL_formatted_image.isNull() )
    {
        std::cerr << "error GL_formatted_image" << std::endl ;
        exit( 1 );
    }

    glGenTextures(1, texture);
    glBindTexture(GL_TEXTURE_2D, texture[0]);
    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() );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); 
}

void resize(int Width, int Height)
{
    glViewport( 0 , 0 , Width , Height );
}

void draw()
{
    glClearColor( 0.0f, 0.0f, 0.0f, 0.0f);
    glClear( GL_COLOR_BUFFER_BIT );

    gluOrtho2D( -1 , 1 , -1 , 1 );

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();

    glShadeModel( GL_FLAT );

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture[0]);

    glBegin(GL_TRIANGLES);
    glTexCoord2f( 1.0f, 0.0f);
    glVertex2f( 1.0f, 0.0f);
    glTexCoord2f( 0.0f, 1.0f);
    glVertex2f( 0.0f, 1.0f);
    glTexCoord2f( 0.0f, 0.0f);
    glVertex2f( 0.0f, 0.0f);
    glEnd();

    glutSwapBuffers();
}

int main(int argc, char **argv) 
{  
    glutInit(&argc, argv);  
    glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA );  
    glutInitWindowSize(640, 480);

    glutCreateWindow("Texture"); 

    LoadGLTextures( "star.png" );

    glutDisplayFunc( & draw );  
    glutReshapeFunc( & resize );
    glutMainLoop();  

    return 1;
}
Rillet answered 17/3, 2011 at 22:5 Comment(2)
You are missing glEnable(GL_TEXTURE_2D); and glDisable(GL_TEXTURE_2D); at several places, but this suggestion is the superior to other answers.Rossanarosse
also, watch out for images, who's dimensions are non-power of 2Rossanarosse

© 2022 - 2024 — McMap. All rights reserved.