31

I am trying to capture an image of the screen for use in screencasting. Thus I need a fast solution, and cannot rely on shell programs such as import or xwd.

This is the code I have written so far, but it fails and gives me a junk image, which just seems to show fragments of several images with odd colors tossed together.

enter image description here

Any ideas on what I am doing wrong?

#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;
}
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
  • 1
    Hi @lalaland can you please share your final code, does this support multi monitor? – Noitidart Jun 18 '15 at 06:08
  • 2
    @Noitidart Yes, I supported multiple monitors. I think https://github.com/Lalaland/ScreenCap/blob/master/src/screenCapturerImpl.cpp might be the right file. Haven't touched the code in years though. The code is poorly written, but if you want to use it, I'll throw a BSD license on there. –  Jun 18 '15 at 13:16
  • Thanks very much @lalaland for reply so fast! If you have time is it ok if i posted on your issues page so i can understand how it works with multi monitors – Noitidart Jun 18 '15 at 13:18
  • 1
    @Noitidart Truthfully, I have no idea how the code works anymore. I don't even know if the code even runs at all on modern Linux systems. –  Jun 18 '15 at 13:28
  • Ah haha thanks for that note, I think it doesnt because I cant find some of the functions used like `XFixesGetCursorImage` – Noitidart Jun 18 '15 at 13:29
  • but what about alpha channel – Newtron Malayalam Oct 12 '20 at 14:25

3 Answers3

19

You are mistaken about the way array is laid out in memory, as you can find out by declaring img before the loop and adding this printf to your inner loop:

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

This yields (on my 1920x1200 screen):

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

and so on, indicating that the red/green/blue subimages are kept "together" instead of the three color components of a single pixel being adjacent to each other.

The builtin CImg accessors will make your code work:

pic(x,y,0) = red;
pic(x,y,1) = green;
pic(x,y,2) = blue;
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
themel
  • 8,825
  • 2
  • 32
  • 31
5

You can use 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);
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Pioz
  • 6,051
  • 4
  • 48
  • 67
2

image has to stored in memory as R1R2R3R4R5R6......G1G2G3G4G5G6.......B1B2B3B4B5B6. cimg storage

Michael Popovich
  • 301
  • 1
  • 10