使用OpenGL和/或X11进行屏幕截图

8

我试图获取屏幕或窗口的截图。我尝试使用X11的函数,它可以正常工作。问题是从XImage获取像素需要很长时间。然后我试图寻找一些关于如何使用OpenGL进行截图的答案。以下是我的发现:

#include <stdlib.h>
#include <stdio.h>
#include <cstdio>
#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <X11/Xlib.h>


int main(int argc, char **argv)
{

int width=1200;
int height=800; 
//_____________________________----
 Display *dpy;
  Window root;
  GLint att[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
  XVisualInfo *vi;
  GLXContext glc;

  dpy = XOpenDisplay(NULL);

  if ( !dpy ) {
    printf("\n\tcannot connect to X server\n\n");
    exit(0);
  }

  root = DefaultRootWindow(dpy);
  vi = glXChooseVisual(dpy, 0, att);

  if (!vi) {
    printf("\n\tno appropriate visual found\n\n");
    exit(0);
  }

glXMakeCurrent(dpy, root, glc);
  glc = glXCreateContext(dpy, vi, NULL, GL_TRUE);


  printf("vendor: %s\n", (const char*)glGetString(GL_VENDOR));

//____________________________________________
glXMakeCurrent(dpy, root, glc);

glEnable(GL_DEPTH_TEST); 
GLubyte* pixelBuffer = new GLubyte[sizeof(GLubyte)*width*height*3*3];

glReadBuffer(GL_FRONT); 

GLint ReadBuffer;
glGetIntegerv(GL_READ_BUFFER,&ReadBuffer);
glPixelStorei(GL_READ_BUFFER,GL_RGB);

GLint PackAlignment;
glGetIntegerv(GL_PACK_ALIGNMENT,&PackAlignment); 
glPixelStorei(GL_PACK_ALIGNMENT,1);

glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_INT, pixelBuffer);

int i;

for (i=0;i<100;i++) printf("%u\n",((unsigned int *)pixelBuffer)[i]);



return 0;
}

当我运行程序时,它返回一个错误: X 错误: BadAccess(尝试访问被拒绝的私有资源) 失败请求的主要操作码: 199 () 失败请求的次要操作码: 26 失败请求的序列号: 20 输出流中的当前序列号: 20
如果我在 glXMakeCurrent(dpy, root, glc); 这行代码前加上注释,则不会返回错误,但所有像素都为0。
如何解决这个问题?我是OpenGL的新手,也许我漏掉了一些重要的东西。也许还有其他从屏幕或特定窗口获取像素的方法存在吗?

你为什么认为使用GL会使它更快?它应该是一样的。 - Dr. Snoopy
需要多长时间?是否涉及网络传输?您只是在终端查看stdout吗?尝试重定向到文件并使用“time”命令。将字符打印到终端使用的是同一个X服务器,该服务器正在拼命尝试复制图像并通过协议进行编组。海森堡原理可能适用;您的检查技术可能会扭曲您寻求的信息。 - luser droog
3个回答

8

我认为你想要做的事情是不可能的。你不能使用OpenGL读取你没有拥有的窗口的像素,而这些窗口很可能甚至没有使用OpenGL。你需要坚持使用X11。

如果你有XImage,你可以从ximage->data获取原始像素。只要确保你以正确的格式读取它。

http://tronche.com/gui/x/xlib/graphics/images.html


谢谢您的回答。但是有没有可能加快速度呢?我认为XImage的像素不是线性表示的,而是由多个位散布在多个字节中组成的,因此需要进行大量的位操作才能获取像素值。我正在使用XGetPixel函数和XYPixmap,image = XGetImage(dis,DefaultRootWindow(dis),0,0,width,height,AllPlanes,XYPixmap);。也许我应该使用不同的Pixmap,例如ZPixmap?我必须在几毫秒内获得像素值,但是获取所有像素有时需要长达一秒钟。有人有什么建议吗? - user761707
1
使用XShmGetImage()可以获得更快的方法。例如,请参见链接中的代码。 - Christian
1
XYPixmap 是一个非常糟糕的格式。你需要的是 ZPixmap,其中像素通过 ximage->data 缓冲区线性呈现,正如你所说的那样。你可以使用 xcb_image_get(),但我正在使用 xcb_image_shm_get(),我可以在约 600 fps(即小于 2 毫秒)的速度下获取所有的 1920 * 1080 像素(xcb_image_get() 给我 200 fps)。 - étale-cohomology

4

0

在我的平台上,以下代码运行一次可达到140 fps。使用XCB_IMAGE_FORMAT_Z_PIXMAP调用xcb_image_get()会将所有像素像素点一样存储在ximg->data中。在我的平台上,每个像素点有32位,每个通道有8位,共有3个通道(单个像素点8位未被使用)。

/*
gcc ss.c -o ss -lxcb -lxcb-image && ./ss
*/

#include <stdio.h>
#include <xcb/xcb_image.h>

xcb_screen_t* xcb_get_screen(xcb_connection_t* connection){
  const xcb_setup_t* setup = xcb_get_setup(connection);  // I think we don't need to free/destroy this!
  return xcb_setup_roots_iterator(setup).data;
}

void xcb_image_print(xcb_image_t* ximg){
  printf("xcb_image_print()  Printing a (%u,%u) `xcb_image_t` of %u bytes\n\n", ximg->height, ximg->width, ximg->size);
  for(int i=0; i < ximg->size; i += 4){
    printf(" ");
    printf("%02x", ximg->data[i+3]);
    printf("%02x", ximg->data[i+2]);
    printf("%02x", ximg->data[i+1]);
    printf("%02x", ximg->data[i+0]);
  }
  puts("\n");
}

int main(){
  // Connect to the X server
  xcb_connection_t* connection = xcb_connect(NULL, NULL);
  xcb_screen_t* screen         = xcb_get_screen(connection);

  // Get pixels!
  xcb_image_t* ximg = xcb_image_get(connection, screen->root, 0, 0, screen->width_in_pixels, screen->height_in_pixels, 0xffffffff, XCB_IMAGE_FORMAT_Z_PIXMAP);
  // ... Now all pixels are in ximg->data!

  xcb_image_print(ximg);

  // Clean-up
  xcb_image_destroy(ximg);
  xcb_disconnect(connection);
}

嗨,你知道为什么图像大小不是1024 * 768 * 3吗?(3代表RGB)输出:xcb_image_print()打印一个(1024,768)的1572864字节的xcb_image_t,深度为16,bpp为16。 - Suhail Doshi
我认为它始终是1024 * 768 * 4,因为每个像素都被填充以占用确切的4个字节。因此,每个像素具有3个通道(因为RGB),但它占用4个字节(因此一个字节始终为零)。我认为这就是它的工作原理,尽管它可能取决于您的图形驱动程序... - étale-cohomology

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