Købmanden
Købmanden

Reputation: 117

Flipping RGBA output of glReadPixels (C++ OpenGL)

I would like to flip the output image from glReadPixel. I have checked the output from glReadPixels by saving it to a file. The image is correct, however its upside down.

What I tried

int patchSize = 50;
std::vector<unsigned char> rgbdata(4*patchSize*patchSize);
glReadPixels(x-(patchSize/2), y-(patchSize/2)/2), patchSize,patchSize,GL_RGBA,GL_UNSIGNED_BYTE, &rgbdata[0]);

std::vector< unsigned char > temp_rgbdata = rgbdata; // Create a copy of the data
    rgbdata.clear(); // Reset the array
    for (int i=patchSize-1; i >= 0; i--) // Count backwards in order to flip
    {
        for( int j = 0; j < patchSize; j++) {

            rgbdata.push_back(temp_rgbdata[i*patchSize+j*4]);
            rgbdata.push_back(temp_rgbdata[i*patchSize+j*4+1]);
            rgbdata.push_back(temp_rgbdata[i*patchSize+j*4+2]);
            rgbdata.push_back(temp_rgbdata[i*patchSize+j*4+3]);

        }
    }
    temp_rgbdata.clear(); // Clear the temporary array

What is wrong

The images turns out all wrong:

Output from trying to flip image

Any help and comments are appreciated. Thank you.

Solution (by Boris)

int patchSize = 50;
std::vector<unsigned char> rgbdata(4*patchSize*patchSize);
glReadPixels(x-(patchSize/2), y-(patchSize/2)), patchSize,patchSize,GL_RGBA,GL_UNSIGNED_BYTE, &rgbdata[0]);

std::vector< unsigned char > temp_rgbdata = rgbdata; // Create a copy of the data
    rgbdata.clear(); // Reset the array
    for (int i=patchSize-1; i >= 0; i--) // Count backwards in order to flip
    {
        for( int j = 0; j < patchSize; j++) {

            rgbdata.push_back(temp_rgbdata[(i*patchSize+j)*4]);
            rgbdata.push_back(temp_rgbdata[(i*patchSize+j)*4+1]);
            rgbdata.push_back(temp_rgbdata[(i*patchSize+j)*4+2]);
            rgbdata.push_back(temp_rgbdata[(i*patchSize+j)*4+3]);

        }
    }
    temp_rgbdata.clear(); // Clear the temporary array

Upvotes: 2

Views: 6824

Answers (5)

Song tingyu
Song tingyu

Reputation: 11

I use this sentence to capture the screen,

glReadPixels(0,0,SCR_WIDTH,SCR_HEIGHT,GL_RGB,GL_UNSIGNED_BYTE,buf);

and the buf's type is uint8_t*,

so I do it like this

void flip(uint8_t** buf)
{
    int totalLength = SCR_HEIGHT*SCR_WIDTH*3;
    int oneLineLength = SCR_WIDTH*3;
    uint8_t* tmp = (uint8_t*)malloc(SCR_HEIGHT*SCR_WIDTH*3);
    memcpy(tmp,*buf,SCR_WIDTH*SCR_HEIGHT*3);
    memset(*buf,0,sizeof(uint8_t)*SCR_HEIGHT*SCR_WIDTH*3);
    for(int i = 0; i < SCR_HEIGHT;i++){
        memcpy(*buf+oneLineLength*i,tmp+totalLength-oneLineLength*(i+1),oneLineLength);
    }
  
}

Upvotes: 0

Goblinhack
Goblinhack

Reputation: 3088

Here is a full example, built ontop of Stypox's great snippet - this does the actual writing also. Of course, swapping your co-ord system is another workaround but I find 0,0 at the top left more natural.

// https://github.com/nothings/stb/blob/master/stb_image_write.h
#include "stb_image_write.h"

glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadBuffer(GL_BACK_LEFT);

std::vector<uint8_t> pixels(3 * w * h);
glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels.data());

for(int line = 0; line != h/2; ++line) {
    std::swap_ranges(pixels.begin() + 3 * w * line,
                     pixels.begin() + 3 * w * (line+1),
                     pixels.begin() + 3 * w * (h-line-1));
}

int components = 3;
stbi_write_png("out.png", w, h, components, pixels.data(), 3 * w);
stbi_write_tga("out.tga", w, h, components, pixels.data());

Upvotes: 3

Stypox
Stypox

Reputation: 1200

A simpler way to flip the output of glReadPixels without temporary vectors using C++17 swap_ranges. The code below is not specific to what was asked, and can be used with any values of x, y, w and h.

std::vector<uint8_t> pixels(3 * w * h);
glReadPixels(x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels.data());

for(int line = 0; line != h/2; ++line) {
    std::swap_ranges(
            pixels.begin() + 3 * w * line,
            pixels.begin() + 3 * w * (line+1),
            pixels.begin() + 3 * w * (h-line-1));
}

Upvotes: 2

Boris Dalstein
Boris Dalstein

Reputation: 7748

You need parentheses in all those lines:

temp_rgbdata[ (i*patchSize+j)*4 ]

By the way, you can be much more efficient by:

  1. Getting the upside down image into the temporary array (avoid a useless copy)

  2. Initialize rgbdata of final size and copy a whole line at once:

Here is how you would do:

int patchSize = 50;
std::vector<unsigned char> temp_rgbdata(4*patchSize*patchSize);
glReadPixels(x-(patchSize/2), y-(patchSize/2)),patchSize,patchSize,GL_RGBA,GL_UNSIGNED_BYTE, &temp_rgbdata[0]);

std::vector< unsigned char > rgbdata(4*patchSize*patchSize);
for (int i=0; i < patchSize; i++) // Doesn't matter the order now
    memcpy(&rgbdata[i*patchSize*4],                    // address of destination
           &temp_rgbdata[(patchSize-i-1)*patchSize*4], // address of source
           patchSize*4*sizeof(unsigned char) );        // number of bytes to copy

temp_rgbdata.clear(); // Clear the temporary array

Upvotes: 2

Ephemera
Ephemera

Reputation: 8970

Are you sure you're reading and writing from/to the correct coordinate system? The following is taken from OpenGL.org:

Another common pitfall related to 2D rendering APIs having an upper left-hand coordinate system is that 2D image file formats start the image at the top scan line, not the bottom scan line. OpenGL assumes images start at the bottom scan line by default. If you do need to flip an image when rendering, you can use glPixelZoom(1,-1).

If your saved image is always appearing upside down, this is probably what's going wrong. Make sure you know where your (0,0) pixel is (upper left or lower left) and then adjust your image alrogithm(s) accordingly.

Upvotes: 2

Related Questions