使用OpenGL进行屏幕截图并将其保存为PNG格式。

5

我正在尝试全屏截图并将其保存为png格式。我找到了一个代码在这里,然后稍作修改。我使用OpenGL和Glut进行截屏,并使用c语言的gd库进行png格式保存。但是,我得到的只是一张黑色的png图片,而且我无法找出原因。我在stackoverflow上搜索了一些帖子,但不幸的是它们没有帮助我解决问题。其中一个建议我使用glReadBuffer( GL_FRONT);而不是glReadBuffer(GL_BACK);我尝试了两个都用,但都没有成功。以下是我的代码:

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);
}

我错过了什么?

1
你在哪里以及如何渲染到OpenGL帧缓冲区?那段代码丢失了。 - datenwolf
@datenwolf 您的意思是什么?我不是用 glReadBuffer(GL_FRONT) 读取显卡帧缓冲区吗? - user1656466
嗯,不,你正在阅读的是OpenGL上下文绑定到的窗口的帧缓冲区。而且OpenGL上下文是为了匹配窗口的可视化而创建的。你不能使用OpenGL来制作任意截图。 OpenGL帧缓冲区的内容仅在OpenGL上下文所绑定的可绘制对象内定义,并且仅适用于使用OpenGL绘制的图像。你是否尝试过在没有为其创建OpenGL上下文或窗口的情况下进行OpenGL调用? - datenwolf
你创建了一个全屏的OpenGL窗口,还是基本上试图抓取桌面?GLUT和OpenGL可以帮助你完成前者,但不能完成后者。正如@datenwolf所指出的那样,GL不知道系统其余像素的情况 - 只知道它控制的像素。 - Drew Hall
2个回答

2
您可以使用devil图像库并使用以下命令截取屏幕截图:
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");

我也在尝试找到一个简单的方法来做这件事。我已经尝试了你的代码,但是当它到达ilSaveImage行时,我得到了一个EXC_BAD_ACCESS信号。我不确定如何进一步调试它 - 有什么想法吗? - N. Virgo
我决定使用GDK而不是OpenGL。也许这会对其他人有所帮助。 - user1656466
当执行ilSaveImage时,我遇到了相同的错误EXC_BAD_ACCESS。我不确定这个库是否是最好的选择。 - Adrian
我找到了问题所在。为了初始化Devil上下文,您应该在ilGetImage()之前使用**ilInit();**。 - Adrian
嗨,Adrian,如果它能帮助其他人,欢迎您编辑答案:https://stackoverflow.com/posts/13517172/edit - mrucci

1
如果您不排斥使用C++库,可以尝试PNGwriter!它会逐像素地写入图片和它们的RGB值。由于PNGwriter从左上角开始,而glReadPixels()从左下角开始,因此您的代码可能会像这样:
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. 我也尝试过libgd,但它似乎只能将一个图像文件(硬盘或内存中的)转换为另一种图像格式。但我认为当您想要将许多PNG文件转换为GIF格式以创建GIF动画时,它仍然很有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接