4

I am trying to understand how the data obtained from XGetImage is disposed in memory:

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

Now suppose I want to decompose each pixel value in red, blue, green channels. How can I do this in a portable way? The following is an example, but it depends on a particular configuration of the XServer and does not work in every case:

for (int x = 0; x < width; x++)
    for (int y = 0; y < height; y++) {
        unsigned long pixel = XGetPixel(img, x, y);   
        unsigned char blue = pixel & blue_mask;
        unsigned char green = (pixel & green_mask) >> 8;
        unsigned char red = (pixel & red_mask) >> 16;
        //...    
    }

In the above example I am assuming a particular order of the RGB channels in pixel and also that pixels are 24bit-depth: in facts, I have img->depth=24 and img->bits_per_pixels=32 (the screen is also 24-bit depth). But this is not a generic case.

As a second step I want to get rid of XGetPixel and use or describe img->data directly. The first thing I need to know is if there is anything in Xlib which exactly gives me all the informations I need to interpret how the image is built starting from the img->data field, which are:

  1. the order of R,G,B channels in each pixel;
  2. the number of bits for each pixels;
  3. the numbbe of bits for each channel;
  4. if possible, a corresponding FOURCC
Martin
  • 9,089
  • 11
  • 52
  • 87

2 Answers2

4

The shift is a simple function of the mask:

    int get_shift (int mask) {
      shift = 0;
      while (mask) {
        if (mask & 1) break;
        shift++;
        mask >>=1;
      }
      return shift;
    }

Number of bits in each channel is just the number of 1 bits in its mask (count them). The channel order is determined by the shifts (if red shift is 0, the the first channel is R, etc).

I think the valid values for bits_per_pixel are 1, 2, 4, 8, 15, 16, 24 and 32 (15 and 16 bits are the same 2 bytes per pixel format, but the former has 1 bit unused). I don't think it's worth anyone's time to support anything but 24 and 32 bpp.

X11 is not concerned with media files, so no 4CC code.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
2

This can be read from the XImage structure itself.

  1. the order of R,G,B channels in each pixel;

This is contained in this field of the XImage structure:

int byte_order;          /* data byte order, LSBFirst, MSBFirst */

which tells you whether it's RGB or BGR (because it only depends on the endianness of the machine).

  1. the number of bits for each pixels;

can be obtained from this field:

int bits_per_pixel;      /* bits per pixel (ZPixmap) */

which is basically the number of bits set in each of the channel masks:

unsigned long red_mask;      /* bits in z arrangement */
unsigned long green_mask;
unsigned long blue_mask;
  1. the numbbe of bits for each channel;

See above, or you can use the code from @n.m.'s answer to count the bits yourself.

Yeah, it would be great if they put the bit shift constants in that structure too, but apparently they decided not to, since the pixels are aligned to bytes anyway, in "standard order" (RGB). Xlib makes sure to convert it to that order for you when it retrieves the data from the X server, even if they are stored internally in a different format server-side. So it's always in RGB format, byte-aligned, but depending on the endianness of the machine, the bytes inside an unsigned long can appear in a reverse order, hence the byte_order field to tell you about that.

So in order to extract these channels, just use the 0, 8 and 16 shifts after masking with red_mask, green_mask and blue_mask, just make sure you shift the right bytes depending on the byte_order and it should work fine.

SasQ
  • 14,009
  • 7
  • 43
  • 43