Pixel binary layout

May 5, 2013

Greetings coders

This post is about pixels. A pixel represents a color that is displayed on the computer screen. Pixels are displayed in a rectangular grid indicated by width and height.

For example, this 4 x 1 image contains a red, green, blue, and gray pixel.

RedGreenBlueGray_sRGB__400x100.jpg

But, how does a computer represent the color of each pixel? Numbers are used to represent a specific color. To help make this easy to understand, let's get back to basics. The color black can be represented by the number 0 and white can be represented by 1. In a black and white image, a single bit can represent a pixel.

Now, lets extend this concept to include gray colors in between black and white. This approach is called grayscale and it means that each pixel is represented by a range of values instead of just two values. For an 8bit grayscale image, a pixel can have a value in the range 0 to 255. The 0 value still indicates black and now 255 indicates white. The values in the range 1 to 254 represent gray colors that get darker the closer the number is to zero.

Gray Scale Image

This basic concept of a number in the range 0 to 255 as a pixel value is extended again to support colors. With a 24bit RGB representation, a pixel is a collection of three number values. In the color example above, the color components (R G B) for the red pixel are (255 0 0). The green pixel (R G B) is (0 255 0) and the blue pixel is (0 0 255). The gray pixel (R G B) values are (145 145 145).

So, how does one actually store these 3 color components as 24 bits of data? Easy, simply encode the numeric values as bits inside a 32bit word. Here is some example C code that encodes three component values into a word.

#include <stdint.h> // for uint32_t
#include <stdio.h> // for printf()

uint32_t rgb_to_pixel(uint8_t red, uint8_t green, uint8_t blue)
{
  return (red << 16) | (green << 8) | blue;
}

void print_pixel_rgb(char *desc, uint32_t pixel)
{
  uint32_t red = (pixel >> 16) & 0xFF;
  uint32_t green = (pixel >> 8) & 0xFF;
  uint32_t blue = (pixel >> 0) & 0xFF;
  
  printf("%6s pixel 0x%.8X : (R G B) (%d, %d, %d)\n",
         desc, pixel, red, green, blue);
}

int main(int argc, char **argv)
{
  uint32_t red = rgb_to_pixel(255, 0, 0);
  print_pixel_rgb("red", red);
  
  uint32_t green = rgb_to_pixel(0, 255, 0);
  print_pixel_rgb("green", green);

  uint32_t blue = rgb_to_pixel(0, 0, 255);
  print_pixel_rgb("blue", blue);
  
  uint32_t black = rgb_to_pixel(0, 0, 0);
  print_pixel_rgb("black", black);

  uint32_t white = rgb_to_pixel(255, 255, 255);
  print_pixel_rgb("white", white);
  
  return 0;
}

Compile the source with gcc like so:

$ gcc -o encode_decode_pixels encode_decode_pixels.c

$ ./encode_decode_pixels
   red pixel 0x00FF0000 : (R G B) (255, 0, 0)
 green pixel 0x0000FF00 : (R G B) (0, 255, 0)
  blue pixel 0x000000FF : (R G B) (0, 0, 255)
 black pixel 0x00000000 : (R G B) (0, 0, 0)
 white pixel 0x00FFFFFF : (R G B) (255, 255, 255)

That is all there is to it! This approach could be used to generate images in memory of various sizes and with various colors.