如何使用xlib正确地截屏?

31

我正在尝试捕捉屏幕图像用于录屏。因此,我需要一个快速的解决方案,不能依赖于像import或xwd这样的shell程序。

这是我迄今为止编写的代码,但它失败了,并给我一个垃圾图像,似乎只显示了几个图像片段,颜色奇怪地混在一起。

输入图像描述

你们有什么想法我做错了什么吗?

#include <X11/Xlib.h>
#include <X11/X.h>

#include <cstdio>
#include <CImg.h>
using namespace cimg_library;

int main()
{
   Display *display = XOpenDisplay(NULL);
   Window root = DefaultRootWindow(display);

   XWindowAttributes gwa;

   XGetWindowAttributes(display, root, &gwa);
   int width = gwa.width;
   int height = gwa.height;


   XImage *image = XGetImage(display,root, 0,0 , width,height,AllPlanes, ZPixmap);

   unsigned char *array = new unsigned char[width * height * 3];

   unsigned long red_mask = image->red_mask;
   unsigned long green_mask = image->green_mask;
   unsigned long blue_mask = image->blue_mask;

   for (int x = 0; x < width; x++)
      for (int y = 0; y < height ; y++)
      {
         unsigned long pixel = XGetPixel(image,x,y);

         unsigned char blue = pixel & blue_mask;
         unsigned char green = (pixel & green_mask) >> 8;
         unsigned char red = (pixel & red_mask) >> 16;

         array[(x + width * y) * 3] = red;
         array[(x + width * y) * 3+1] = green;
         array[(x + width * y) * 3+2] = blue;
      }

   CImg<unsigned char> pic(array,width,height,1,3);
   pic.save_png("blah.png");

   printf("%ld %ld %ld\n",red_mask>> 16, green_mask>>8, blue_mask);

   return 0;
}

1
嗨@lalaland,你能分享一下你的最终代码吗?这个支持多屏幕吗? - Noitidart
2
@Noitidart 是的,我支持多个显示器。我认为 https://github.com/Lalaland/ScreenCap/blob/master/src/screenCapturerImpl.cpp 可能是正确的文件。虽然我已经多年没有碰过这段代码了,但这段代码写得很糟糕,但如果你想使用它,我会在那里加上BSD许可证。 - user406009
非常感谢@lalaland的快速回复!如果您有时间,是否可以在您的问题页面上发布以便我了解如何在多个监视器上工作。 - Noitidart
1
@Noitidart 说实话,我已经不知道这段代码是如何工作的了。我甚至不知道这段代码是否能在现代 Linux 系统上运行。 - user406009
啊哈哈,谢谢你的提醒。我认为它不行,因为我找不到一些使用的函数,比如 XFixesGetCursorImage - Noitidart
但是关于 alpha 通道呢? - Newtron Malayalam
3个回答

19
你对数组在内存中的布局方式有误,可以通过在循环之前声明img并将此printf添加到内部循环中来查明情况。
printf("%ld %ld %u %u %u\n",x,y,pic.offset(x,y,0),pic.offset(x,y,1),pic.offset(x,y,2));

这会在我的 1920x1200 屏幕上产生以下结果:

0 0 0 2304000 4608000
0 1 1920 2305920 4609920
0 2 3840 2307840 4611840

等等,这意味着红/绿/蓝子图像被"组合"在一起,而不是单个像素的三个颜色分量相邻。

内置的CImg访问器将使您的代码正常工作:

pic(x,y,0) = red;
pic(x,y,1) = green;
pic(x,y,2) = blue;

5
你可以使用libpng。
int code = 0;
FILE *fp;
png_structp png_ptr;
png_infop png_info_ptr;
png_bytep png_row;

// Open file
fp = fopen ("test.png", "wb");
if (fp == NULL){
    fprintf (stderr, "Could not open file for writing\n");
    code = 1;
}

// Initialize write structure
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL){
    fprintf (stderr, "Could not allocate write struct\n");
    code = 1;
}

// Initialize info structure
png_info_ptr = png_create_info_struct (png_ptr);
if (png_info_ptr == NULL){
    fprintf (stderr, "Could not allocate info struct\n");
    code = 1;
 }

// Setup Exception handling
if (setjmp (png_jmpbuf (png_ptr))){
    fprintf(stderr, "Error during png creation\n");
   code = 1;
}

png_init_io (png_ptr, fp);

// Write header (8 bit colour depth)
png_set_IHDR (png_ptr, png_info_ptr, width, height,
     8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
     PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

// Set title
char *title = "Screenshot";
if (title != NULL){
    png_text title_text;
    title_text.compression = PNG_TEXT_COMPRESSION_NONE;
    title_text.key = "Title";
    title_text.text = title;
    png_set_text (png_ptr, png_info_ptr, &title_text, 1);
}

png_write_info (png_ptr, png_info_ptr);

// Allocate memory for one row (3 bytes per pixel - RGB)
png_row = (png_bytep) malloc (3 * width * sizeof (png_byte));

// Write image data
int x, y;
for (y = 0; y < height; y++){
    for (x = 0; x < width; x++){
        unsigned long pixel = XGetPixel (image, x, y);
        unsigned char blue = pixel & blue_mask;
        unsigned char green = (pixel & green_mask) >> 8; 
        unsigned char red = (pixel & red_mask) >> 16;
        png_byte *ptr = &(png_row[x*3]);
        ptr[0] = red;
        ptr[1] = green;
        ptr[2] = blue;
    }
    png_write_row (png_ptr, png_row);
}

// End write
png_write_end (png_ptr, NULL);

// Free
fclose (fp);
if (png_info_ptr != NULL) png_free_data (png_ptr, png_info_ptr, PNG_FREE_ALL, -1);
if (png_ptr != NULL) png_destroy_write_struct (&png_ptr, (png_infopp)NULL);
if (png_row != NULL) free (png_row);

2

图片需要以 R1R2R3R4R5R6......G1G2G3G4G5G6.......B1B2B3B4B5B6 的形式存储在内存中。

CImg 存储

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