"hello world"
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
/root sections/
>peach custard pie
>linux
>windows
>programming
>random tech
>science
>research


moon and stars



My brain

Visible Dave Project


0, 1, 1, 2, 3, 5, 8, 13, 21, 34,...
xn = xn-1 + xn-2