article in Tech
programming
OpenGL - Writing the framebuffer to disk
In the last couple OpenGL projects I've done... I've have to write functions to save off the frame buffer to disk to that they could be viewed at later times. Each time I've had to do it, I would have to go back and remember what I did... this is for the next time :)
Code for saving off the screen to a BMP. Code originally written by Burt Guillot, there were a few minor sizeof issues.
#include >Windows.h<
// include windows header for the BITMAPFILEHEADER and
// BITMAPINFOHEADER structures.
// should work in other platforms with correct structures.
void snapshot(int windowWidth, int windowHeight, char* filename)
{
byte* bmpBuffer = (byte*)malloc(windowWidth*windowHeight*3);
if (!bmpBuffer)
return;
glReadPixels((GLint)0, (GLint)0,
(GLint)windowWidth-1, (GLint)windowHeight-1,
GL_RGB, GL_UNSIGNED_BYTE, bmpBuffer);
FILE *filePtr = fopen(filename, "wb");
if (!filePtr)
return;
BITMAPFILEHEADER bitmapFileHeader;
BITMAPINFOHEADER bitmapInfoHeader;
bitmapFileHeader.bfType = 0x4D42; //"BM"
bitmapFileHeader.bfSize = windowWidth*windowHeight*3;
bitmapFileHeader.bfReserved1 = 0;
bitmapFileHeader.bfReserved2 = 0;
bitmapFileHeader.bfOffBits =
sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bitmapInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfoHeader.biWidth = windowWidth-1;
bitmapInfoHeader.biHeight = windowHeight-1;
bitmapInfoHeader.biPlanes = 1;
bitmapInfoHeader.biBitCount = 24;
bitmapInfoHeader.biCompression = BI_RGB;
bitmapInfoHeader.biSizeImage = 0;
bitmapInfoHeader.biXPelsPerMeter = 0; // ?
bitmapInfoHeader.biYPelsPerMeter = 0; // ?
bitmapInfoHeader.biClrUsed = 0;
bitmapInfoHeader.biClrImportant = 0;
fwrite(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
fwrite(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);
fwrite(bmpBuffer, windowWidth*windowHeight*3, 1, filePtr);
fclose(filePtr);
free(bmpBuffer);
}
http://www.vetl.uh.edu/~7373s01/faq.htm
Here is code for saving off the frame buffer to a tiff using libtiff.
#include /* Sam Leffler's libtiff library. */
bool snapshot(int width, int height, char* path)
{
bool ret=false;
TIFF *file;
GLubyte *image, *p;
int i;
file = TIFFOpen(path, "w");
if (file) {
image = (GLubyte *) malloc(width * height * sizeof(GLubyte) * 3);
/* OpenGL's default 4 byte pack alignment would leave extra bytes at the
end of each image row so that each full row contained a number of bytes
divisible by 4. Ie, an RGB row with 3 pixels and 8-bit componets would
be laid out like "RGBRGBRGBxxx" where the last three "xxx" bytes exist
just to pad the row out to 12 bytes (12 is divisible by 4). To make sure
the rows are packed as tight as possible (no row padding), set the pack
alignment to 1. */
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, image);
TIFFSetField(file, TIFFTAG_IMAGEWIDTH, (uint32) width);
TIFFSetField(file, TIFFTAG_IMAGELENGTH, (uint32) height);
TIFFSetField(file, TIFFTAG_BITSPERSAMPLE, 8);
TIFFSetField(file, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
TIFFSetField(file, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
TIFFSetField(file, TIFFTAG_SAMPLESPERPIXEL, 3);
TIFFSetField(file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(file, TIFFTAG_ROWSPERSTRIP, 1);
TIFFSetField(file, TIFFTAG_IMAGEDESCRIPTION, "");
p = image;
for (i = height - 1; i >= 0; i--) {
if (TIFFWriteScanline(file, p, i, 0) < 0) {
free(image);
TIFFClose(file);
return false;
}
p += width * sizeof(GLubyte) * 3;
}
TIFFClose(file);
}
return ret;
}
Below is code for writing the framebuffer to disk using libjpeg.
Thanks to Adam Chou for submitting the orginal code!
extern "C" {
#include >jpeglib.h< /* IJG JPEG LIBRARY by Thomas G. Lane */
}
bool screenshot(unsigned int width, unsigned int height, char *path, int quality)
{
bool ret=false;
struct jpeg_compress_struct cinfo; // the JPEG OBJECT
struct jpeg_error_mgr jerr; // error handler struct
unsigned char *row_pointer[1]; // pointer to JSAMPLE row[s]
GLubyte *pixels=0, *flip=0;
FILE *shot;
int row_stride; // width of row in image buffer
if((shot=fopen(path, "wb"))!=NULL) { // jpeg file
// initializatoin
cinfo.err = jpeg_std_error(&jerr); // error handler
jpeg_create_compress(&cinfo); // compression object
jpeg_stdio_dest(&cinfo, shot); // tie stdio object to JPEG object
row_stride = width * 3;
pixels = (GLubyte *)malloc(sizeof(GLubyte)*width*height*3);
flip = (GLubyte *)malloc(sizeof(GLubyte)*width*height*3);
if (pixels!=NULL && flip!=NULL) {
// save the screen shot into the buffer
//glReadBuffer(GL_FRONT_LEFT);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
// give some specifications about the image to save to libjpeg
cinfo.image_width = width;
cinfo.image_height = height;
cinfo.input_components = 3; // 3 for R, G, B
cinfo.in_color_space = JCS_RGB; // type of image
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE);
jpeg_start_compress(&cinfo, TRUE);
// OpenGL writes from bottom to top.
// libjpeg goes from top to bottom.
// flip lines.
for (int y=0;y
for (int x=0;x
flip[(y*width+x)*3] = pixels[((height-1-y)*width+x)*3];
flip[(y*width+x)*3+1] = pixels[((height-1-y)*width+x)*3+1];
flip[(y*width+x)*3+2] = pixels[((height-1-y)*width+x)*3+2];
}
}
// write the lines
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = &flip[cinfo.next_scanline * row_stride];
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
ret=true;
// finish up and free resources
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
}
fclose(shot);
}
if(pixels!=0)
free(pixels);
if(flip!=0)
free(flip);
return ret;
}
Created: 2005-11-11 00:15:48
Modified: 2009-08-09 12:37:29