x11 - 将 XImage 数据转换为像素图(例如 RGB 四边形数组)的任何有效方法?

标签 x11 xlib xorg

我正在尝试使用 XGetImage 捕捉图像。一切都很好,但我需要将数据发送到一个需要 RGB 四边形数组的模块。为图像中的每个像素调用 XGetPixel 非常慢(在 1440x900 分辨率的 i5 上为 0.5 秒)。我查看了 xlib 中的 XGetPixel 源代码,原因很明显,每个像素都进行了大量计算。 有什么有效的(或者可能完全不同的)方法可以做到这一点吗?

最佳答案

使用 MIT 共享内存扩展,您可以将图像存储在共享内存区域中。这样您就可以避免在处理图像时通过 Xlib IPC channel 。 您可以分别使用 XShmGetImage 和 XShmPutImage 获取或设置可绘制对象的内容。 您不能调整共享内存区域的大小,您必须销毁它并创建一个新的。

下一个实用程序截屏,将所有像素的 alpha channel 设置为 0xFF 并将其保存到指定文件。 它假定像素是 32 位 BGRA,您应该处理所有打包的像素格式。

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <stdbool.h>
#include <png.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>

#define NAME  "screenshot"
#define BPP   4

struct shmimage
{
    XShmSegmentInfo shminfo ;
    XImage * ximage ;
    unsigned int * data ; // will point to the image's BGRA packed pixels
} ;

void initimage( struct shmimage * image )
{
    image->ximage = NULL ;
    image->shminfo.shmaddr = (char *) -1 ;
}

void destroyimage( Display * dsp, struct shmimage * image )
{
    if( image->ximage )
    {
        XShmDetach( dsp, &image->shminfo ) ;
        XDestroyImage( image->ximage ) ;
        image->ximage = NULL ;
    }

    if( image->shminfo.shmaddr != ( char * ) -1 )
    {
        shmdt( image->shminfo.shmaddr ) ;
        image->shminfo.shmaddr = ( char * ) -1 ;
    }
}

int createimage( Display * dsp, struct shmimage * image, int width, int height )
{
    // Create a shared memory area 
    image->shminfo.shmid = shmget( IPC_PRIVATE, width * height * BPP, IPC_CREAT | 0600 ) ;
    if( image->shminfo.shmid == -1 )
    {
        perror( NAME ) ;
        return false ;
    }

    // Map the shared memory segment into the address space of this process
    image->shminfo.shmaddr = (char *) shmat( image->shminfo.shmid, 0, 0 ) ;
    if( image->shminfo.shmaddr == (char *) -1 )
    {
        perror( NAME ) ;
        return false ;
    }

    image->data = (unsigned int*) image->shminfo.shmaddr ;
    image->shminfo.readOnly = false ;

    // Mark the shared memory segment for removal
    // It will be removed even if this program crashes
    shmctl( image->shminfo.shmid, IPC_RMID, 0 ) ;

    // Allocate the memory needed for the XImage structure
    image->ximage = XShmCreateImage( dsp, XDefaultVisual( dsp, XDefaultScreen( dsp ) ),
                        DefaultDepth( dsp, XDefaultScreen( dsp ) ), ZPixmap, 0,
                        &image->shminfo, 0, 0 ) ;
    if( !image->ximage )
    {
        destroyimage( dsp, image ) ;
        printf( NAME ": could not allocate the XImage structure\n" ) ;
        return false ;
    }

    image->ximage->data = (char *)image->data ;
    image->ximage->width = width ;
    image->ximage->height = height ;

    // Ask the X server to attach the shared memory segment and sync
    XShmAttach( dsp, &image->shminfo ) ;
    XSync( dsp, false ) ;
    return true ;
}

void getrootwindow( Display * dsp, struct shmimage * image )
{
    XShmGetImage( dsp, XDefaultRootWindow( dsp ), image->ximage, 0, 0, AllPlanes ) ;
    // This is how you access the image's BGRA packed pixels
    // Lets set the alpha channel of each pixel to 0xff
    int x, y ;
    unsigned int * p = image->data ;
    for( y = 0 ; y < image->ximage->height; ++y )
    {
        for( x = 0 ; x < image->ximage->width; ++x )
        {
            *p++ |= 0xff000000 ;
        }
    }
}

void initpngimage( png_image * pi, struct shmimage * image )
{
    bzero( pi, sizeof( png_image ) ) ;
    pi->version = PNG_IMAGE_VERSION ;
    pi->width = image->ximage->width ;
    pi->height = image->ximage->height ;
    pi->format = PNG_FORMAT_BGRA ;
}

int savepng( struct shmimage * image, char * path )
{
    FILE * f = fopen( path, "w" ) ;
    if( !f )
    {
        perror( NAME ) ;
        return false ;
    }
    png_image pi ;
    initpngimage( &pi, image ) ;
    unsigned int scanline = pi.width * BPP ;
    if( !png_image_write_to_stdio( &pi, f, 0, image->data, scanline, NULL) )
    {
        fclose( f ) ;
        printf( NAME ": could not save the png image\n" ) ;
        return false ;
    }
    fclose( f ) ;
    return true ;
}

int main( int argc, char * argv[] )
{
    if( argc != 2 )
    {
        printf( "Usage:\n" ) ;
        printf( "      " NAME " file\n\n" ) ;
        return 0 ;
    }

    Display * dsp = XOpenDisplay( NULL ) ;
    if( !dsp )
    {
        printf( NAME ": could not open a connection to the X server\n" ) ;
        return 1 ;
    }

    if( !XShmQueryExtension( dsp ) )
    {
        XCloseDisplay( dsp ) ;
        printf( NAME ": the X server does not support the XSHM extension\n" ) ;
        return 1 ;
    }

    int screen = XDefaultScreen( dsp ) ;
    struct shmimage image ;
    initimage( &image ) ;
    if( !createimage( dsp, &image, XDisplayWidth( dsp, screen ), XDisplayHeight( dsp, screen ) ) )
    {
        XCloseDisplay( dsp ) ;
        return 1 ;
    }

    getrootwindow( dsp, &image ) ;
    if( !savepng( &image, argv[1] ) )
    {
        destroyimage( dsp, &image ) ;
        XCloseDisplay( dsp ) ;
        return 1 ;
    }

    destroyimage( dsp, &image ) ;
    XCloseDisplay( dsp ) ;
    return 0 ;
}

你可以这样编译它:

gcc screenshot.c -o screenshot -std=c99 -I/usr/X11R6/include -I/usr/local/include -L/usr/X11R6/lib -L/usr/local/lib -lX11 -lXext -lpng

您可以像这样捕获并保存屏幕截图:

screenshot screenshot.png

如果您不想处理 png 库并且对截屏不感兴趣,请删除 initpngimage 和 savepng 函数,加上以下行:

#include <strings.h>
#include <png.h>

if( savepng( &image, argv[1] ) == FALSE )
{
    destroyimage( dsp, &image ) ;
    XCloseDisplay( dsp ) ;
    return FALSE ;
}

然后像这样编译它:

gcc screenshot.c -o screenshot -std=c99 -I/usr/X11R6/include -L/usr/X11R6/lib -lX11 -lXext

关于x11 - 将 XImage 数据转换为像素图(例如 RGB 四边形数组)的任何有效方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34176795/

相关文章:

c - XCB:映射一次后窗口不会取消映射

keyboard-shortcuts - 让X客户端重新加载.Xcompose?

检查是否在 X Window 中运行

linux - Master/Slave AMD + NVidia中的GPU和linux

c - Xlib 和 BadWindow

linux - 没有 X 的硬件加速

韦兰的选择

xorg - 解析 X 服务器授权文件

python - 使用 python Xlib 重命名窗口

x11 - 从隐藏屏幕获取图像