Friday, 8 March 2013

Low-level Graphics on Raspberry Pi (part six)

In the previous posts we have been plotting pixels using a 8 bit, 256 color, palette display mode. In this mode, every byte of the framebuffer (mmap'ed) memory block present one byte and the value of the byte is an index to the palette (see part three). So to get the color bars in the previous examples, we have plotted values of 0 (zero) to the first bar (black), values of 1 (one) to the second bar (blue) and so on... This picture illustrates the idea - each cell presents one pixel (some columns skipped for compacting), showing both the byte value and the resulting color:
Here is the pixel plotting function used:
void put_pixel(int x, int y, int c)
{
    // calculate the pixel's byte offset inside the buffer
    unsigned int pix_offset = x + y * finfo.line_length;

    // now this is about the same as 'fbp[pix_offset] = value'
    *((char*)(fbp + pix_offset)) = c;

}

So we are storing one byte (that 'char *' there) ...obviously we could (/should) have defined the color parameter c as a char too, but... (the above code takes the lowest byte of the four byte integer variable, so works as is).

Well, how about other display modes? We have noticed already (part one) that the default mode on RPi is 16 bit and quite often one comes across mentions of 24 bit and 32 bit modes. I suppose the easiest of these to begin with would be the 24 bit mode: 3 bytes per pixel - one byte per each RGB (red, green, blue) value. The RGB values are very similar to the values in the palette for the 8 bit mode. To illustrate this, in the following image we have the two leftmost pixels of the two first color bars - the first pixel occupies three first bytes of the memory buffer. For the black pixels all three byte values are zeroes - for the blue pixels the 'R' and 'G' bytes are zero and 'B' byte is 255 (= full blue):
This could be implemented as the following pixel plotting code:
void put_pixel_RGB24(int x, int y, int r, int g, int b)
{

    // calculate the pixel's byte offset inside the buffer
    // note: x * 3 as every pixel is 3 consecutive bytes
    unsigned int pix_offset = x * 3 + y * finfo.line_length;

    // now this is about the same as 'fbp[pix_offset] = value'
    *((char*)(fbp + pix_offset)) = r;
    *((char*)(fbp + pix_offset + 1)) = g;
    *((char*)(fbp + pix_offset + 2)) = b;

}


The RPi default 16 bit RGB565 is slightly more complex - there are 2 bytes per pixel and the color components are encoded so that 5 first bits are for the red, 6 middle bits for green and 5 last bits for blue:
In the format similar to the above ones, the memory buffer would look something like this:
...the blue value 31 comes from the fact that there are 5 bits for blue and the binary value of 0b11111 is 31 in decimal. Full red would be 0b1111100000000000 (63488) so the bytes for full red would be 248 and 0 - full green would be 0b0000011111100000 (2016) so the bytes 7 and 224.

The RGB565 pixe plotting function would be something along this:
void put_pixel_RGB565(int x, int y, int r, int g, int b)
{

    // calculate the pixel's byte offset inside the buffer
    // note: x * 2 as every pixel is 2 consecutive bytes
    unsigned int pix_offset = x * 2 + y * finfo.line_length;

    // now this is about the same as 'fbp[pix_offset] = value'
    // but a bit more complicated for RGB565
    unsigned short c = ((r / 8) << 11) + ((g / 4) << 5) + (b / 8);
    // or: c = ((r / 8) * 2048) + ((g / 4) * 32) + (b / 8);
    // write 'two bytes at once'
    *((unsigned short*)(fbp + pix_offset)) = c;

}

The red value has 5 bits, so can be in the range 0-31, therefore divide the original 0-255 value by 8. It is stored in the first 5 bits, so multiply by 2048 or shift 11 bits left. The green has 6 bits, so can be in the range 0-63, divide by 4, and multiply by 32 or shift 5 bits left. Finally the blue has 5 bits and is stored at the last bits, so no need to move.

The 32 bit mode is usually so called ARGB - where there are 4 bytes per pixel, A standing for 'alpha' = transparency and the rest just like in the RGB24. Modifying the put_pixel_RGB24() to put_pixel_ARGB32() should be trivial.

Note that there are several other display (and especially image and video) modes as well, but the ones covered here are the most standard ones for Linux framebuffers.

To test the above pixel plotting functions, we could try the following (continuing from the code in part five):
void draw() {

    int x, y;

    for (y = 0; y < (vinfo.yres / 2); y++) {
        for (x = 0; x < vinfo.xres; x++) {

            // color based on the 16th of the screen width
            int c = 16 * x / vinfo.xres;
    
            if (vinfo.bits_per_pixel == 8) {
                put_pixel(x, y, c);
            }
            else if (vinfo.bits_per_pixel == 16) {
                put_pixel_RGB565(x, y, def_r[c], def_g[c], def_b[c]);
            }
            else if (vinfo.bits_per_pixel == 24) {
                put_pixel_RGB24(x, y,  def_r[c], def_g[c], def_b[c]);
            }

        }
    }

}

...

// in main()

// comment out setting the bit depth

    // Change variable info
    /* use: 'fbset -depth x' to test different bpps
    vinfo.bits_per_pixel = 8;
    if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo)) {
      printf("Error setting variable information.\n");
    }
    */

// also can comment out setting the palette...

// change the calculation of required memory
    // map fb to user mem 
    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;

...
Save the new code to fbtest6.c, compile and then execute the following sequence:
fbset -depth 8
./fbtest6
fbset -depth 24
./fbtest6
fbset -depth 16
./fbtest6
...this should yield three times the exact same color bars. Full source code available in github.

[Continued in part seven]

Thursday, 7 March 2013

Low-level Graphics on Raspberry Pi (part five)

So far we have used some fixed palette values for the colors (see part four) - namely 0-15 to draw the 16 bars. These happen to be the default Linux framebuffer colors (slight variations between distributions to be expected). Since we are using palette indexes, we don't really know what are the actual colors output. The Linux framebuffer interface provides a function FBIOGETCMAP to read the color palette:
  // Get palette information
  unsigned short r[256];
  unsigned short g[256];
  unsigned short b[256];
  unsigned short a[256];
  memset(r, 0, 256 * sizeof(unsigned short));
  memset(g, 0, 256 * sizeof(unsigned short));
  memset(b, 0, 256 * sizeof(unsigned short));
  memset(a, 0, 256 * sizeof(unsigned short));
  struct fb_cmap pal;
  pal.start = 0;
  pal.len = 0;
  pal.red = r;
  pal.green = g;
  pal.blue = b;
  pal.transp = a;
  if (ioctl(fbfd, FBIOGETCMAP, &pal)) {
    printf("Error reading palette.\n");
  }
...would give us the red, green and blue components of the colors in the palette. So most likely r[0]=0, g[0]=0, b[0]=0 for the color index 0 i.e. black (#000000 in HTML) - r[1]=0, g[1]=0, b[1]=255 for color index 1 i.e. blue (#0000FF). Colors from index 16 onwards are most likely all black.

Unfortunately reading this information is not supported by the RPi framebuffer driver ...this surely is not how I wanted these posts to be Raspberry Pi specific :(

After a bit of trial and error (and 'stealing' the initial values from another Linux system using the above code), I managed to get pretty close to the RPi default colors. In this example we initialise some hard-coded values (note that I use the possibly more familiar range 0-255, matching many systems like the HTML color scheme) for the colors (the enum is not used here but might prove handy later on - think of put_pixel(x, y, RED)), set them to the system palette starting at index 16 (i.e. just after the default colors) and then draw the familiar color bars at the top half of the screen - and bars with same colors but using different palette indexes at the lower half for comparison:
...
// after includes

// default framebuffer palette
typedef enum {
  BLACK        =  0, /*   0,   0,   0 */
  BLUE         =  1, /*   0,   0, 172 */
  GREEN        =  2, /*   0, 172,   0 */
  CYAN         =  3, /*   0, 172, 172 */
  RED          =  4, /* 172,   0,   0 */
  PURPLE       =  5, /* 172,   0, 172 */
  ORANGE       =  6, /* 172,  84,   0 */
  LTGREY       =  7, /* 172, 172, 172 */
  GREY         =  8, /*  84,  84,  84 */
  LIGHT_BLUE   =  9, /*  84,  84, 255 */
  LIGHT_GREEN  = 10, /*  84, 255,  84 */
  LIGHT_CYAN   = 11, /*  84, 255, 255 */
  LIGHT_RED    = 12, /* 255,  84,  84 */
  LIGHT_PURPLE = 13, /* 255,  84, 255 */
  YELLOW       = 14, /* 255, 255,  84 */
  WHITE        = 15  /* 255, 255, 255 */
} COLOR_INDEX_T;

static unsigned short def_r[] = 
    { 0,   0,   0,   0, 172, 172, 172, 172,  
     84,  84,  84,  84, 255, 255, 255, 255};
static unsigned short def_g[] = 
    { 0,   0, 172, 172,   0,   0,  84, 172,  
     84,  84, 255, 255,  84,  84, 255, 255};
static unsigned short def_b[] = 
    { 0, 172,   0, 172,   0, 172,   0, 172,  
     84, 255,  84, 255,  84, 255,  84, 255};

...

void draw() {

    int x, y;

    for (y = 0; y < (vinfo.yres / 2); y++) {
        for (x = 0; x < vinfo.xres; x++) {

            // color based on the 16th of the screen width
            int c = 16 * x / vinfo.xres;
    
            // default colors at upper half
            put_pixel(x, y, c);
            // our own colors at lower half
            put_pixel(x, y + (vinfo.yres / 2), c + 16);

        }
    }

}

...
// in main after opening the fb device
    // Set palette
    unsigned short r[256];
    unsigned short g[256];
    unsigned short b[256];
    memset(&r, 0, 256); // initialise with zeros
    memset(&g, 0, 256);
    memset(&b, 0, 256);
    int i;
    for(i = 0; i < 16; i++) {
        // copy the hard-coded values
        // note that Linux provides more precision (0-65535),
        // so we multiply ours (0-255) by 256
        r[i] = def_r[i] << 8;
        g[i] = def_g[i] << 8;
        b[i] = def_b[i] << 8;
    } 
    struct fb_cmap pal;
    pal.start = 16; // start our colors after the default 16
    pal.len = 256; // kludge to force bcm fb drv to commit palette...
    pal.red = r;
    pal.green = g;
    pal.blue = b;
    pal.transp = 0; // we want all colors non-transparent == null
    if (ioctl(fbfd, FBIOPUTCMAP, &pal)) {
        printf("Error setting palette.\n");
    }

...

...I only hope my monitor color tolerance is about ok and your eyes not much sharper than mine ;)

Now you could go and fill the palette with any colors you can come up with and draw something interesting. If you use the first 16 color 'slots' in the palette for your own colors, remember to set the default colors at the end of your program:
  // before closing the fbfd
  // reset palette
  palette.start = 0;
  palette.len = 256;
  palette.red = def_red;
  palette.green = def_green;
  palette.blue = def_blue;
  palette.transp = 0;
  if (ioctl(fbfd, FBIOPUTCMAP, &palette)) {
    printf("Error setting palette.\n");
  }
...otherwise when returning to the console, you might not be able to read any of the text ;) In case this happens (or some other issue with the graphics garbles the screen), one way to fix thing is to type (possibly 'blindly') the commands:
fbset -depth 32
fbset -depth 16
...this (changes the color depth and resets back to the RPi default and) should clear refresh the screen.

[Continued in part six]

Sunday, 3 March 2013

Coding Gold Dust: How to break out from an infinite loop (in Python)

Infinite loops have many uses... In the earlier post I presented a 'Pythonian' way of writing an infinite loop. Obviously in some cases, the loop needs to be 'near infinite' - without the user forcible breaking the execution with Ctrl+C. The simplest way is to use the break command:
while True:
    do_something
    if some_condition:
        break

For example, reading some input from the user ('the oracle answers'):
while True:
    s = raw_input("What's your question?")
    if s == "who are you":
        print "Raspberry Pi!"
    if s == "quit":
        break

Many other programming languages have the break command as well.

But if the body of the while loop grows longer - spanning maybe even more than a 'screenful', it might become not so readable anymore. If there are certain defined exit condition(s), why have a while True declaration at all. Wouldn't it be better to tell the reader of the code that there is going to be something breaking out of the loop? For this we can use an exit variable:
done = False
while not done:
    s = raw_input("What's your question?")
    if s == "who are you":
        print "Raspberry Pi!"
    if s == "quit":
        done = True
...the 'while not done' should read pretty clearly for a human and as not False equals True, the loop runs as long as the variable value is False.

Of course, there might be multiple such loops in a lengthier program and the exit condition might change. So it should make sense to use an exit variable named to tell what is the actual condition - for example when waiting for a GPIO connected hardware button:
button_pressed = False
while not button_pressed:
    # do something
    # do something more
    button_pressed = read_GPIO_button_press()
And so on... Obviously these are no longer that infinite loops, but well, that's how the question was posed originally ;)

Low-level Graphics on Raspberry Pi (part four)

In the part three we saw how to plot individual pixels in the framebuffer. Now let's turn the plot-pixel code into a reusable function.

First we need to move some of the variables outside of the main function, so we can access them in the new function - then we just move the pixel plotting code from the main into the new function and make main to call it. We will also move the 'draw' code into another function to separate it from the main and make it easier to read.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>

// 'global' variables to store screen info
char *fbp = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;

// helper function to 'plot' a pixel in given color
void put_pixel(int x, int y, int c)
{
    // calculate the pixel's byte offset inside the buffer
    unsigned int pix_offset = x + y * finfo.line_length;

    // now this is about the same as 'fbp[pix_offset] = value'
    *((char*)(fbp + pix_offset)) = c;

}

// helper function for drawing - no more need to go mess with
// the main function when just want to change what to draw...
void draw() {

    int x, y;

    for (y = 0; y < (vinfo.yres / 2); y++) {
        for (x = 0; x < vinfo.xres; x++) {

            // color based on the 16th of the screen width
            int c = 16 * x / vinfo.xres;
    
            // call the helper function
            put_pixel(x, y, c);

        }
    }

}

// application entry point
int main(int argc, char* argv[])
{

    int fbfd = 0;
    struct fb_var_screeninfo orig_vinfo;
    long int screensize = 0;


    // Open the file for reading and writing
    fbfd = open("/dev/fb0", O_RDWR);
    if (!fbfd) {
      printf("Error: cannot open framebuffer device.\n");
      return(1);
    }
    printf("The framebuffer device was opened successfully.\n");

    // Get variable screen information
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
      printf("Error reading variable information.\n");
    }
    printf("Original %dx%d, %dbpp\n", vinfo.xres, vinfo.yres, 
       vinfo.bits_per_pixel );

    // Store for reset (copy vinfo to vinfo_orig)
    memcpy(&orig_vinfo, &vinfo, sizeof(struct fb_var_screeninfo));

    // Change variable info
    vinfo.bits_per_pixel = 8;
    if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo)) {
      printf("Error setting variable information.\n");
    }

    // Get fixed screen information
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
      printf("Error reading fixed information.\n");
    }

    // map fb to user mem 
    screensize = vinfo.xres * vinfo.yres;
    fbp = (char*)mmap(0, 
              screensize, 
              PROT_READ | PROT_WRITE, 
              MAP_SHARED, 
              fbfd, 
              0);

    if ((int)fbp == -1) {
        printf("Failed to mmap.\n");
    }
    else {
        // draw...
        draw();
        sleep(5);
    }

    // cleanup
    munmap(fbp, screensize);
    if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &orig_vinfo)) {
        printf("Error re-setting variable information.\n");
    }
    close(fbfd);

    return 0;
  
}

Now save the file as fbtest4.c, compile with make fbtest4.c and execute ./fbtest4 - you should see the same vertical color bars at the upper half of the screen as in part three. Making the put_pixel function should help to come up with new ideas for images to draw...

[Continued in part five]