Saturday, June 12, 2010

glDrawPixels + Qt (Part 2)

glDrawPixels + Qt (Part 2)

In this part, we are going to use Qt to load an image from a file and display it with QGLWidget by using glDrawPixels.
The process is generally the same, except the order of function calls and the use of Qt to load an image file.

First, take a look at the constructor

GLImage2_1::GLImage2_1(QGLWidget *parent)
    : QGLWidget(parent)
{
    makeImage();
    m_nCurrWidth = m_nWidth;
    m_nCurrHeight = m_nHeight;
}


The function makeImage() is called early because we want to know image dimensions and give a correct size hint to a QGLWidget.
initializeGL is, therefore, leaner.

void GLImage2_1::initializeGL()
{
    // Set coordinate system
    glOrtho(0, m_nWidth, 0, m_nHeight, 0, 1);

    // Basic initialization
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_FLAT);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}


Now, let's get to makeImage(), the most important part of this article.

void GLImage2_1::makeImage() {
    string fileName("/home/pinyo/temp/beach.jpg");

    QImage qImage;
    qImage.load(QString(fileName.c_str()));

    m_nWidth = qImage.width();
    m_nHeight = qImage.height();

    chkImage = new GLubyte[m_nHeight * m_nWidth * 3];

    for (int h = 0; h < m_nHeight; ++h) {
        int nImgH = m_nHeight - h - 1;

        for (int w = 0; w < m_nWidth; ++w) {
            QRgb color = qImage.pixel(w, nImgH);

            chkImage[3* (h *m_nWidth + w) + 0] = (GLubyte) qRed(color);
            chkImage[3*(h*m_nWidth + w) + 1] = (GLubyte) qGreen(color);
            chkImage[3*(h*m_nWidth + w) + 2] = (GLubyte) qBlue(color);
        }
    }

    return;
}

There are a few things we have to pay attention to.
  1. QImage is used and (declared in qimage.h)
  2. We have no need to explicitly supply the image file type to its load function.  Qt first relies on file extension (.jpg, .png, for example).  If it fails or there is no file extension, Qt will make a guess based on file header.
  3. Notice the use of int nImgH = m_nHeight - h - 1;  This is essential because the image coordinate system in OpenGL is different from most computer imaging systems, including Qt.  In Qt the origin is at top left and the y-axis points downward, while in OpenGL, the origin is at bottom left and the y-axis points upward.  Since the x-axes in Qt and OpenGL are the same, there is no need to convert x-coordinates.


========================================

An example Eclipse CDT / Qt project is stored at a public folder here.
The project file is valid for Eclipse CDT on Linux with Qt integration (Ubuntu 9.04 Juanty), but the main part should be applicable to most C++ environment.


Pinyo Taeprasartsit
September 2009

1 comment:

supermelon said...

Great, just what I needed! Thank you.