使用 openGL 和/或 X11 的屏幕截图

Posted

技术标签:

【中文标题】使用 openGL 和/或 X11 的屏幕截图【英文标题】:screenshot using openGL and/or X11 【发布时间】:2011-05-19 18:38:25 【问题描述】:

我正在尝试获取屏幕或窗口的屏幕截图。我尝试使用 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); 注释该行之前 glc = glXCreateContext(dpy, vi, NULL, GL_TRUE);它没有返回错误,但所有像素都是0。

我应该如何解决这个问题?我是openGL的新手,也许我在这里遗漏了一些重要的东西。也许还有另一种从屏幕或特定窗口获取像素的方法?

【问题讨论】:

是什么让你觉得使用 GL 会更快?应该是一样的。 需要多少时间?是否涉及网络传输?您只是在终端中查看标准输出吗?尝试重定向到一个文件并使用time 命令。将字符打印到终端使用相同的 X 服务器,该服务器拼命尝试复制图像并通过协议对其进行编组。海森堡可以申请;您的检查技术可能会扭曲您所寻找的信息。 【参考方案1】:

我认为您尝试做的事情是不可能的。您不能使用 OpenGL 从您不拥有且可能甚至不使用 OpenGL 的窗口中读取像素。您需要坚持使用 X11。

如果您有XImage,您可以从ximage-&gt;data 获取原始像素。请确保您以正确的格式阅读它。

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

【讨论】:

感谢您的回答。但是可以加快速度吗?我不认为 XImage 的像素是由 ximage->data 线性呈现的。我认为像素是由分散在多个字节中的多个位组成的,因此您需要大量的位操作来获取像素值。我正在使用 XGetPixel 函数和 XYPixmap,image = XGetImage(dis, DefaultRootWindow(dis), 0, 0, width, height, AllPlanes, XYPixmap);。也许我应该使用不同的 Pixmap,例如 ZPixmap?我必须在几毫秒内获得像素值,但获得所有像素有时需要一秒钟。有人有什么建议吗? 使用 XShmGetImage() 获得更快的方法。示例参见link 中的代码 XYPixmap 是一种非常糟糕的格式。你想要的是ZPixmap,正如你所说,ximage-&gt;data 缓冲区线性呈现像素。您可以使用xcb_image_get(),但我使用的是xcb_image_shm_get(),我可以以大约 600 fps(即小于 2 毫秒)的速度获得所有 1920 * 1080 像素(xcb_image_get() 给我 200 fps)【参考方案2】:

您可以使用 XShmGetImage,但您必须先查询 X11 服务器的扩展,以确保 MIT-SHM 扩展可用。您还需要知道如何为此设置和使用共享内存段。

查询扩展:

http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavdevice/x11grab.c#l224

获取图片:

http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavdevice/x11grab.c#l537

【讨论】:

【参考方案3】:

以下内容在我的平台上以 140 fps 运行一次。 xcb_image_get()(用XCB_IMAGE_FORMAT_Z_PIXMAP 调用)将在ximg-&gt;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) xcb_image_t 1572864 字节,深度:16,bpp:16 我认为它总是1024 * 768 * 4,因为每个像素都被填充到正好占据4个字节。因此,每个像素有 3 个通道(因为 RGB),但它占用 4 个字节(所以一个字节始终为零)。我想事情就是这样,虽然它可能取决于你的图形驱动程序......

以上是关于使用 openGL 和/或 X11 的屏幕截图的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL 中的 BMP 屏幕截图

在 OpenGL 中创建屏幕截图不起作用

带有 OpenGL 的 Qt MDI 应用程序:如何获取有效的屏幕截图?

iOS开发- OpenGL ES屏幕截图

Paint 应用程序的 OpenGL ES 内容的屏幕截图

安卓屏幕录像