Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I write PNG files from an openGL screen?

Tags:

c++

image

opengl

So I have this script which reads the display data into a character array pixels:

        typedef unsigned char uchar;
        // we will store the image data here
        uchar *pixels;
        // the thingy we use to write files
        FILE * shot;
        // we get the width/height of the screen into this array
        int screenStats[4];

        // get the width/height of the window
        glGetIntegerv(GL_VIEWPORT, screenStats);

        // generate an array large enough to hold the pixel data 
        // (width*height*bytesPerPixel)
        pixels = new unsigned char[screenStats[2]*screenStats[3]*3];
        // read in the pixel data, TGA's pixels are BGR aligned
        glReadPixels(0, 0, screenStats[2], screenStats[3], 0x80E0, 
        GL_UNSIGNED_BYTE, pixels);

Normally, I save this to a TGA file, but since these get monstrously large I was hoping to use PNG instead as I quickly run out of hard drive space doing it this way (my images are highly monotonous and easily compressible, so the potential gain is huge). So I'm looking at PNG writer but I'm open to other suggestions. The usage example they give at their website is this:

#include <pngwriter.h>


int main()
{
pngwriter image(200, 300, 1.0, "out.png");
image.plot(30, 40, 1.0, 0.0, 0.0); // print a red dot
image.close();
return 0;
}

As I'm somewhat new to image processing I'm a little confused about the form of my pixels array and how I would convert this to a form representable in the above format. As a reference, I've been using the following script to convert my files to TGA:

    //////////////////////////////////////////////////
    // Grab the OpenGL screen and save it as a .tga //
    // Copyright (C) Marius Andra 2001              //
    // http://cone3d.gz.ee  EMAIL: [email protected]    //
    //////////////////////////////////////////////////
    // (modified by me a little)
    int screenShot(int const num)
    {
        typedef unsigned char uchar;
        // we will store the image data here
        uchar *pixels;
        // the thingy we use to write files
        FILE * shot;
        // we get the width/height of the screen into this array
        int screenStats[4];

        // get the width/height of the window
        glGetIntegerv(GL_VIEWPORT, screenStats);

        // generate an array large enough to hold the pixel data 
        // (width*height*bytesPerPixel)
        pixels = new unsigned char[screenStats[2]*screenStats[3]*3];
        // read in the pixel data, TGA's pixels are BGR aligned
        glReadPixels(0, 0, screenStats[2], screenStats[3], 0x80E0, 
        GL_UNSIGNED_BYTE, pixels);

        // open the file for writing. If unsucessful, return 1
        std::string filename = kScreenShotFileNamePrefix + Function::Num2Str(num) + ".tga";

        shot=fopen(filename.c_str(), "wb");

        if (shot == NULL)
            return 1;

        // this is the tga header it must be in the beginning of 
        // every (uncompressed) .tga
        uchar TGAheader[12]={0,0,2,0,0,0,0,0,0,0,0,0};
        // the header that is used to get the dimensions of the .tga
        // header[1]*256+header[0] - width
        // header[3]*256+header[2] - height
        // header[4] - bits per pixel
        // header[5] - ?
        uchar header[6]={((int)(screenStats[2]%256)),
        ((int)(screenStats[2]/256)),
        ((int)(screenStats[3]%256)),
        ((int)(screenStats[3]/256)),24,0};

        // write out the TGA header
        fwrite(TGAheader, sizeof(uchar), 12, shot);
        // write out the header
        fwrite(header, sizeof(uchar), 6, shot);
        // write the pixels
        fwrite(pixels, sizeof(uchar), 
        screenStats[2]*screenStats[3]*3, shot);

        // close the file
        fclose(shot);
        // free the memory
        delete [] pixels;

        // return success
        return 0;
    }

I don't normally like to just dump and bail on these forums but in this instance I'm simply stuck. I'm sure the conversion is close to trivial I just don't understand enough about image processing to get it done. If someone could provide a simple example for how to convert the pixels array into image.plot() in the PNG writer library, or provide a way of achieving this using a different library that would be great! Thanks.

like image 357
quant Avatar asked Oct 04 '13 04:10

quant


1 Answers

Your current implementation does almost all the work. All you have to do is to write into the PNG file the pixel colors returned by OpenGL. Since there is no method in PNG Writer to pass an array of colors, you will have to write the pixels one by one.

Your call to glReadPixels() hides the requested color format. You should use one of the predefined constants (see the format argument) instead of 0x80E0. According to how you build the pixel array, I guess you are requesting red/green/blue components.

Thus, your pixel-to-png code may look like this:

const std::size_t image_width( screenStats[2] );
const std::size_t image_height( screenStats[3] );

pngwriter image( image_width, image_height, /*…*/ );

for ( std::size_t y(0); y != image_height; ++y )
  for ( std::size_t x(0); x != image_width; ++x )
    {
       unsigned char* rgb( pixels + 3 * (y * image_width + x) );
       image.plot( x, y, rgb[0], rgb[1], rgb[2] );
    }

image.close()

As an alternative to PNGwriter, you may have a look at libclaw or use libpng as is.

like image 56
Julien Avatar answered Sep 19 '22 06:09

Julien