2011-11-23 1 views
22

J'essaye de capturer une image de l'écran pour l'usage dans le screencasting. J'ai donc besoin d'une solution rapide, et je ne peux pas compter sur des programmes shell tels que import ou xwd.Comment faire une capture d'écran correctement avec xlib?

Ceci est le code que j'ai écrit jusqu'à présent, mais il échoue et me donne une image indésirable, qui semble juste montrer des fragments de plusieurs images avec des couleurs impaires lues ensemble.

http://s9.postimage.org/dqnkgkwr1/blah.png

Toutes les idées sur ce que je fais mal?

#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

Salut @alaland pouvez-vous s'il vous plaît partager votre code final, est-ce compatible multi moniteur? – Noitidart

+2

@Noitidart Oui, j'ai supporté plusieurs moniteurs. Je pense que https://github.com/Lalaland/ScreenCap/blob/master/src/screenCapturerImpl.cpp pourrait être le bon fichier. Je n'ai pas touché le code depuis des années. Le code est mal écrit, mais si vous voulez l'utiliser, je vais lancer une licence BSD là-bas. – Lalaland

+0

Merci beaucoup @alaland pour répondre si vite! Si vous avez le temps, est-ce que je peux publier sur votre page de problèmes afin que je puisse comprendre comment cela fonctionne avec plusieurs moniteurs – Noitidart

Répondre

14

Vous vous trompez sur la façon dont array est mis en mémoire, comme vous pouvez le savoir en déclarant img avant que la boucle et en ajoutant printf à votre boucle intérieure:

printf("%ld %ld %u %u %u\n",x,y,pic.offset(x,y,0),pic.offset(x,y,1),pic.offset(x,y,2)); 

Ce rendement (sur mon écran de 1920x1200):

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

et ainsi de suite, ce qui indique que les sous-images rouge/vert/bleu sont conservés « ensemble » au lieu des trois les composants de couleur d'un seul pixel étant adjacents les uns aux autres.

Les accesseurs builtin CIMG feront de votre travail de code:

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

Vous aviez raison :) – Lalaland

4

Vous pouvez utiliser 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); 
0

l'image doit stockée dans la mémoire comme R1R2R3R4R5R6 ...... G1G2G3G4G5G6 ... .... B1B2B3B4B5B6. cimg storage

Questions connexes