banner
Dave Horner's Website - Yet another perspective on things...
Home Tech Talk Programming OpenGL - Writing the framebuffer to disk
If you appreciate the information found on this website, please drop me a line!

Who's Online

We have 30 guests online
Content View Hits : 1157227
moon and stars

More from me...

How did you find my site?
 
How often do you answer random online questions?
 

Random Quote

The number of UNIX installations has grown to 10, with more expected.
--The Unix Programmer's Manual, 2nd Edition, June 1972
P1010005
007_8_002
108_0821
IMG_2433

OpenGL - Writing the framebuffer to disk

Thursday, 10 November 2005 19:15
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;
}

Last Updated on Sunday, 09 August 2009 07:37