Reputation: 117
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:
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
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
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
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
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:
Getting the upside down image into the temporary array (avoid a useless copy)
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
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