opengl - displayed image sheared

Viewed 131

I'm trying to use OpenGL to display images read from disk by Microsoft CImage library.

The image now can be roughly seen in the window, but clearly there are something wrong. The image is gray, and it's sheared. I can't figure out where is the problem because every pixel should be corresponding to the its slot in _data.

OpenGL image: opengl image

Original image: original image

Below is my code:

int _w = 300;
int _h = 300;
GLubyte ***_data;

void init() {
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, _w, 0.0, _h);
}

void display() {
    glClear(GL_COLOR_BUFFER_BIT);
    glRasterPos2i(0, 0);
    glDrawPixels(_w, _h, GL_RGB,
        GL_UNSIGNED_BYTE, _data);
    glFlush();
}

int main(int argc, char** argv) {
    CImage img_handler;
    img_handler.Load(_T("bg.jpg"));
    _w = img_handler.GetWidth();
    _h = img_handler.GetHeight();

    COLORREF pixel;
    _data = new GLubyte**[_h];

    for (int y = 0; y < _h; y++) {
        _data[_h-y-1] = new GLubyte*[_w];
        for (int x = 0; x < _w; x++) {
            pixel = img_handler.GetPixel(x, y);

            _data[_h-y-1][x] = new GLubyte[3];
            _data[_h-y][x][0] = (GLubyte)GetRValue(pixel);
            _data[_h-y][x][1] = (GLubyte)GetGValue(pixel);
            _data[_h-y][x][2] = (GLubyte)GetBValue(pixel);
        }
    }

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH/*enable depth buffer*/);
    glutInitWindowSize(_w, _h);
    glutCreateWindow("test");

    init();
    glutDisplayFunc(display);
    glutMainLoop();

    return 0;
}
1 Answers

According to this OpenGL doc on glDrawPixels (emphasis added):

width x height pixels are read from memory, starting at location data. By default, these pixels are taken from adjacent memory locations, except that after all width pixels are read, the read pointer is advanced to the next four-byte boundary. The four-byte row alignment is specified by glPixelStore with argument GL_UNPACK_ALIGNMENT, and it can be set to one, two, four, or eight bytes.

Note that your colors are defined as 3 GLuint values - only 3 bytes! Each row of pixels contains 3 * 897 = 2691 bytes. The next multiple of 4 after 2691 is 2692 and so the way glDrawPixels is defined, GL skips one byte after each row. This is why the image is sheared and why it appears gray (if you zoom in, you'll see that it is not gray, but rather every third row has the correct colors and the ones in between are hue shifted by 120 degrees).

The way to fix this is to make a glPixelStore call ahead of the glDrawPixels call. It's sufficient to change

glDrawPixels(_w, _h, GL_RGB,
    GL_UNSIGNED_BYTE, _data);

to

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glDrawPixels(_w, _h, GL_RGB,
    GL_UNSIGNED_BYTE, _data);

Also, the code inside your for loop is not correct. _h-y should be _h-y-1 so that when y=0 you do not write outside of the array's bounds and similarly when y=_h-1 you write to the 0th index of the array.

Also, do look into dynamic memory allocation, instead of allocating an array of static size. This will make your code less prone to access violations i.e. reading / writing memory that does not in fact belong to you, leading to crashes and other problems.

Related