5

I'm trying to take a screenshot of full screen and save it as a png. I found a code here and modified it a bit. For the screenshot I use openGL and Glut and for the saving in png the gd library for c. All I'm getting is a black png and I can't figure out why. I searched in stackoverflow and found some posts, but unfortunately they didn't help. One of them was to use glReadBuffer( GL_FRONT); instead of glReadBuffer(GL_BACK); I tryed with both of them with no success. Here is my code:

int SVimage2file(char *filename){
    int width = glutGet(GLUT_SCREEN_WIDTH);
    int height = glutGet( GLUT_SCREEN_HEIGHT);
    FILE *png;
    GLubyte *OpenGLimage, *p;
    gdImagePtr image;
    unsigned int r, g, b;
    int i,j,rgb;

    png = fopen(filename, "wb");

    if (png == NULL) {
        printf("*** warning:  unable to write to %s\n",filename);
        return 1;
    }

    OpenGLimage = (GLubyte *) malloc(width * height * sizeof(GLubyte) * 3);
    if(OpenGLimage == NULL){
        printf("error allocating image:%s\n",filename);
        exit(1);
    }

    printf("Saving to: %s .\n",filename);
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    glReadBuffer( GL_FRONT);
    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, OpenGLimage);
    p = OpenGLimage;
    image = gdImageCreateTrueColor(width,height);

    for (i = height-1 ; i>=0; i--) {
        for(j=0;j<width;j++){
                r=*p++; g=*p++; b=*p++;
                rgb = (r<<16)|(g<<8)|b;
                //printf("the rgb color %d\n", rgb );
                gdImageSetPixel(image,j,i,rgb);
        }
    }

    gdImagePng(image,png);
    fclose(png);
    gdImageDestroy(image);
}

What is it that I'm missing?

genpfault
  • 51,148
  • 11
  • 85
  • 139
user1656466
  • 145
  • 1
  • 9
  • 1
    Where and how do you render to the OpenGL framebuffer? That code's missing. – datenwolf Nov 22 '12 at 16:27
  • @datenwolf what do you meen by that. Am I not reading the framebuffer of the graphic card with glReadBuffer(GL_FRONT)? – user1656466 Nov 25 '12 at 18:11
  • Um, no, what you're reading is the framebuffer of the window the OpenGL context has been bound to. And a OpenGL context is created to match the visual of a window. **You can not use OpenGL to make arbitrary screenshots.** The contents of a OpenGL framebuffer are defined only within the drawable the OpenGL context has been bound to, and only for images drawn using OpenGL. Did you actually try to make OpenGL calls without a OpenGL context or a window created for it? – datenwolf Nov 25 '12 at 19:46
  • Have you created a fullscreen OpenGL window, or are you essentially trying to frame-grab the desktop? GLUT & OpenGL can help you with the former, but not the later. As @datenwolf points out, GL doesn't know about the rest of the system's pixels--only those that it controls. – Drew Hall Jul 23 '14 at 14:46

2 Answers2

2

You could use the devil image library and take a screeshot with:

void takeScreenshot(const char* screenshotFile)
{
    ILuint imageID = ilGenImage();
    ilBindImage(imageID);
    ilutGLScreen();
    ilEnable(IL_FILE_OVERWRITE);
    ilSaveImage(screenshotFile);
    ilDeleteImage(imageID);
    printf("Screenshot saved to: %s\n", screenshotFile);
}

takeScreenshot("screenshot.png");
mrucci
  • 4,342
  • 3
  • 33
  • 35
  • I'm also trying to find an easy way to do this. I've tried your code but I get a EXC_BAD_ACCESS signal when it gets to the ilSaveImage line. I'm not sure how to debug it further - any ideas? – N. Virgo Dec 03 '12 at 04:04
  • I decided to use the GDK instead of OpenGL. Maybe this will help someone else: – user1656466 Aug 25 '13 at 10:04
  • I get the same error EXC_BAD_ACCESS when executing ilSaveImage. I am not sure if this library is the best choice. – Adrian Dec 15 '18 at 09:18
  • I figured out what is the problem. You should use **ilInit();** before ilGetImage() in order to initialize Devil context. – Adrian Dec 15 '18 at 09:40
  • Hi Adrian, you are welcome to edit the answer if it can help others: https://stackoverflow.com/posts/13517172/edit – mrucci Dec 15 '18 at 16:49
1

If you don't reject to use C++ library, you should try PNGwriter! It write the picture pixel by pixel and their RGB values. Since the PNGwriter start form left-up corner while the glReadPixels() start from left-bottom, your code while like:

GLfloat* OpenGLimage = new GLfloat[nPixels];
glReadPixels(0.0, 0.0, width, height,GL_RGB, GL_FLOAT, OpenGLimage);
pngwriter PNG(width, height, 1.0, fileName);
size_t x = 1;   // start the top and leftmost point of the window
size_t y = 1;
double R, G, B;
for(size_t i=0; i<npixels; i++)
{
      switch(i%3) //the OpenGLimage array look like [R1, G1, B1, R2, G2, B2,...]
     {
           case 2:
                 B = (double) pixels[i]; break;
           case 1:
                 G = (double) pixels[i]; break;
           case 0:
                 R = (double) pixels[i];
                 PNG.plot(x, y, R, G, B);
                 if( x == width )
                 {
                       x=1;
                       y++;
                  }
                  else
                  { x++; }
                  break;
     }
}
PNG.close();

PS. I had also try libgd, but it seems only convert one image file (in the hard disk or in memory) to another format of image. But I think it still useful while you want to convert many PNG file to GIF format to create a GIF animate.

WhiteRivers
  • 93
  • 1
  • 5