Any efficient way of converting XImage data to pixel map (e.g. array of RGB quads)?

末鹿安然 提交于 2019-12-05 03:55:52

问题


I'm trying to capture images with XGetImage. Everything fine but I need to send the data to a module which expects an array of RGB quads. Calling XGetPixel for every pixel in the image is very slow (0.5 seconds on a i5 for 1440x900 resolution). I've looked up the XGetPixel source code in xlib and the reason is obvious, a lot of computations are done for each pixel. Is there any efficient (or maybe completely different) way of doing this?


回答1:


With the MIT Shared Memory Extension you can store an image in a shared memory area. This way you avoid going through the Xlib IPC channel when processing the image. You can get or set the contents of a drawable with XShmGetImage and XShmPutImage respectively. You can't resize the shared memory area, you have to destroy it and create a new one.

The next utility takes a screenshot, sets the alpha channel of all pixels to 0xFF and saves it to the specified file. It assumes the pixels are 32 bits BGRA, you should handle all packed pixel formats.

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

You can compile it like this:

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

You can capture and save a screenshot like this:

screenshot screenshot.png

If you don't want to deal with the png library and you are not interested in taking a screenshot, delete initpngimage and savepng functions, plus the following lines:

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

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

And compile it like this:

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


来源:https://stackoverflow.com/questions/34176795/any-efficient-way-of-converting-ximage-data-to-pixel-map-e-g-array-of-rgb-quad

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!