Wednesday 3 April 2013

Low-level Graphics on Raspberry Pi (part eight)

Last time, we looked at the differences between display modes regarding color depths. Another attribute of a display mode is the resolution - by default, RPi uses the full HD resolution 1920x1080 as we saw in the first part (of course depending on the actual display attached).

Basically this can be changed to (almost) anything if needed/wanted - one obvious reason would be to limit the number of pixels RPi has to handle. You might have noticed that the color gradients in the previous example appear on the screen somewhat slowly (especially if using the full HD resolution)?

Let's experiment a bit... As briefly mentioned before, there is an utility for changing the frame buffer parameters called fbset (see man fbset). Now if we assume the original resolution is the full HD, running fbtest7 would look like:
$ ./fbtest7
The framebuffer device was opened successfully.
Original 1920x1080, 16bpp
$
...and the image appears like a roller blind drawn down. Now try the following sequence of commands (presented with output, so type only the text on lines starting with $ sign, not including the sign):
$ fbset -g 960 540 960 540 16
$ ./fbtest7
The framebuffer device was opened successfully.
Original 960x540, 16bpp
$ fbset -g 640 360 640 360 16
$ ./fbtest7
The framebuffer device was opened successfully.
Original 640x360, 16bpp
$ fbset -g 448 256 448 256 16
$ ./fbtest7
The framebuffer device was opened successfully.
Original 448x256, 16bpp
$
...with about the same image appearing faster and faster. Also note that even though we change the resolution to smaller and smaller, the RPI VideoCore GPU scales the image up to fill the entire display - hardware accelerated up-scaling for free ;)

The effect on the image quality may not be that apparent with the color gradient example, so let's try something possibly a bit more descriptive:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.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;

    // fill the screen with blue
    memset(fbp, 1, vinfo.xres * vinfo.yres);
    
    // white horizontal lines every 10 pixel rows
    for (y = 0; y < (vinfo.yres); y+=10) {
        for (x = 0; x < vinfo.xres; x++) {
            put_pixel(x, y, 15);
        }
    }

    // white vertical lines every 10 pixel columns
    for (x = 0; x < vinfo.xres; x+=10) {
        for (y = 0; y < (vinfo.yres); y++) {
            put_pixel(x, y, 15);
        }
    }
    
    int n;
    // select smaller extent 
    // (just in case of a portrait mode display)
    n = (vinfo.xres < vinfo.yres) ? vinfo.xres : vinfo.yres;
    // red diagonal line from top left
    for (x = 0; x < n; x++) {
        put_pixel(x, x, 4);
    }

}

// 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 - force 8 bit
    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 * vinfo.bits_per_pixel / 8;
    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;
  
}

Save that code as fbtest8.c, compile with make fbtest8 and execute the following command sequence:
$ fbset -g 1920 1080 1920 1080 16
$ ./fbtest8
The framebuffer device was opened successfully.
Original 1920x1080, 16bpp
$ fbset -g 960 540 960 540 16
$ ./fbtest8
The framebuffer device was opened successfully.
Original 960x540, 16bpp
$ fbset -g 640 360 640 360 16
$ ./fbtest8
The framebuffer device was opened successfully.
Original 640x360, 16bpp
$ fbset -g 448 256 448 256 16
$ ./fbtest8
The framebuffer device was opened successfully.
Original 448x256, 16bpp
$
...which should produce images like the ones here (the first one at 1920x1080 and the second at 448x256):

Of course the resolution can be changed programmatically too (no need to leave it to the end-user) - change the variable info setting lines in the main to:
    // Change variable info - force 8 bit and resolution
    vinfo.bits_per_pixel = 8;
    vinfo.xres = 320;
    vinfo.yres = 240;
    vinfo.xres_virtual = vinfo.xres;
    vinfo.yres_virtual = vinfo.yres;
    if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo)) {
      printf("Error setting variable information.\n");
    }
Save as fbtest8b.c, compile with make fbtest8b and execute with ./fbtest8b ...and you should get the same image with even bigger pixels ...centered horizontally at the middle of the display with black borders at sides (if you have the typical widescreen display in landscape mode).

So it seems that VC GPU scales to 'best fit' keeping pixels square - if the specified resolution conforms to the display aspect ratio, the image will fill the entire display - if it does not, there will be black borders at either left&right or top&bottom. You might want to experiment with different xres and yres values (for example 512x256, more ideas maybe here) to find a suitable resolution for your use-case - bearing in mind that the GPU seems (based on the hello_pi examples and experimenting) to expect the xres to be a multiple of 32 and yres of 16 - if not, there may be some side effects... For example, for the 'fire effect' in the screenshot in part two, I had to go as low as 320x240 to get a bearable frame-rate.

Code available also in GitHub

[Continued in part X]

Tuesday 2 April 2013

Low-level Graphics on Raspberry Pi (part seven)

In the previous example, we produced the same image in different display modes (color depths). Let's see if we can find some difference between the modes.

This example draws three color gradient circles partly overlapping:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.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;

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)) = b;
    *((char*)(fbp + pix_offset + 1)) = g;
    *((char*)(fbp + pix_offset + 2)) = r;

}

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);
    unsigned short c = ((r / 8) * 2048) + ((g / 4) * 32) + (b / 8);
    // write 'two bytes at once'
    *((unsigned short*)(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;
    int r, g, b;
    int dr;
    int cr = vinfo.yres / 3;
    int cg = vinfo.yres / 3 + vinfo.yres / 4;
    int cb = vinfo.yres / 3 + vinfo.yres / 4 + vinfo.yres / 4;

    for (y = 0; y < (vinfo.yres); y++) {
        for (x = 0; x < vinfo.xres; x++) {
            dr = (int)sqrt((cr - x)*(cr - x)+(cr - y)*(cr - y));
            r = 255 - 256 * dr / cr;
            r = (r >= 0) ? r : 0;
            dr = (int)sqrt((cg - x)*(cg - x)+(cr - y)*(cr - y));
            g = 255 - 256 * dr / cr;
            g = (g >= 0) ? g : 0;
            dr = (int)sqrt((cb - x)*(cb - x)+(cr - y)*(cr - y));
            b = 255 - 256 * dr / cr;
            b = (b >= 0) ? b : 0;

            if (vinfo.bits_per_pixel == 16) {
                put_pixel_RGB565(x, y, r, g, b);
            }
            else {
                put_pixel_RGB24(x, y, r, g, b);
            }
        }
    }

}

// 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));

    // 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 * vinfo.bits_per_pixel / 8;
    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;
  
}

Save the new code (available also on GitHub) to fbtest7.c and compile using this command (as we now use the sqrt() function the math library, we need to tell the linker this with the '-lm' directive):
gcc -o fbtest7 -lm fbtest7.c
And then execute the following sequence:
fbset -depth 16
./fbtest7
fbset -depth 24
./fbtest7
...note how in the 16 bit mode there are noticeable bands in the color gradients - less so in the 24 bit mode. Note that the above code does not work in the 8 bit mode - it could be modified to produce similar enough image by setting the palette values suitably.

[Continued in part eight]

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]

Tuesday 5 February 2013

Coding Gold Dust: How to write an infinite loop in Python

One of the coding style issues that has come up more than often on the Raspberry Pi forum is: how to write an infinite loop in Python?

For obvious reasons, many RPi examples are about hardware or network interfacing, and often these examples are derived from C language code. Interfacing, due to it's event-driven nature (especially when reading something), warrants the use of an infinite loop - or well, an event-loop to be more precise.

In plain C, there is no boolean data type but instead anything 'non zero' evaluates to 'true'. Hence an infinite loop in C is typically (and rightfully so) implemented as a while loop like this:
while (1) {
    printf("Hello, world!\n");
}

It seems that this does work in Python as well:
while 1:
    print "Hello, world!"

By definition a while loop continues execution as long as the condition evaluates to true. So practically any expression that evaluates to true could be used:
while (1 == 1):
    ...
test = "true"
while (test == "true"):
    ...
while (1 < 2):
    ...
However, it should be obvious that these are neither beautiful, explicit, simple, readable nor obvious - which are what 'The Zen of Python' lists as 'guiding design principles' for Python, and could be taken as guiding principles for any Python code. In addition, especially the string comparison might incur a performance hit.

Many programming languages do define proper boolean types. For example Java has a data type called boolean and defines the values true and false for that type. Hence our loop in Java would be:
while (true) {
    System.out.println("Hello, world!");
}
Python too defines a class bool, which can have two values True and False. And because there is a boolean data type, it should make perfect sense to use that and (for consistency) only that to implement an infinite loop:
while True:
    print "Hello, world!"
I rest my case ;)

In addition to The Zen of Python, there is a Python Style Guide available - definitely worth a read.

P.S. Thanks for fellow RPI forum user alexeames for the idea of 'Gold Dust' :)

Tuesday 22 January 2013

Low-level Graphics on Raspberry Pi (part three)

So, after memmap'ing the framebuffer (see part two), it appears as a contiguous section of RAM - an array of bytes. That is why we declared the 'frame buffer pointer' variable fbp as:
  char *fbp = 0;
and the memmap:
  fbp = (char*)mmap(0, 
                    screensize, 
                    PROT_READ | PROT_WRITE, 
                    MAP_SHARED, 
                    fbfd, 0);
behaves about the same as if we allocated the buffer ourselves like:
#include <stdio.h>

int main(int argc, char* argv[])
{
    char *fbp = 0;
    char fb[100];

    fb[0] = 'a';

    fbp = fb; // similar to mmap ...

    printf("fb[0] = %c\n", fb[0]);
    printf("*fbp = %c\n", *fbp);
}
where the pointer variable fbp points to the first byte of the array fb. You might want to read more about C data-types and arrays vs pointers from your preferred source of C programming information...

Basically all RAM memory can be seen as an array of bytes - something like this:
The same could also be used to illustrate a particular array within the memory, where the array start byte would be the 'box zero' (fb[0] in the above example) and so on until the length of the allocated array at 'box n' (fb[99]).

As the framebuffer represents the two-dimensional display 'surface', we could illustrate the pixels as:
In case of a 8 bpp ('8 bits per pixel', 256 color, one byte per pixel) display mode, this would directly map to the framebuffer memory layout: so the "width'th" byte (fb[width]) in the array would represent the left-most pixel on the second (from the top) scanline and the 5th pixel on the 3rd row (highlighted in grey) would match the byte at '3 x width + 5' (fb[3 * width + 5]).

However, there are numerous other display modes generally used and in fact the default mode of RPi is 16 bpp (as seen in part one). This means that to be able to handle the framebuffer as 'just bytes', we need to switch to 8 bpp display mode.

This can be done from the command-line using the command:
fbset -depth 8
Now run the fbtest from the part one - it should output '..., 8 bpp'. If you try the fbtest2 from part two, you will most likely not get the same result as in 16 bpp mode - probably just a black screen (will have to verify this). To reset bit-depth back to original use '-depth 16' (or whatever fbtest said it was before).

So what's with the drawing now? The reason to the different result is that in 16 bpp mode, the bytes (byte-pairs) in the buffer describe the actual pixel color - in 8 bpp mode, the byte value is an index to a palette. In 16 bpp a value of 0 (in both bytes) would yield black - in 8 bit the value 0 yields the color stored in the palette at index 0 and could basically be anything in the RGB colorspace (incidently, the color 0 in the default Linux framebuffer palette seems to be black ...but it could be set to anything).

Speaking of the default palette: why not take a look what colors it contains? This example (building on the previous examples) changes the display mode to 8 bpp, draws vertical bars of varying colors (the 16 colors of the default palette), pauses for 5 seconds and restores the display settings:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>

// application entry point
int main(int argc, char* argv[])
{
  int fbfd = 0;
  struct fb_var_screeninfo orig_vinfo;
  struct fb_var_screeninfo vinfo;
  struct fb_fix_screeninfo finfo;
  long int screensize = 0;
  char *fbp = 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 = finfo.smem_len;
  fbp = (char*)mmap(0, 
                    screensize, 
                    PROT_READ | PROT_WRITE, 
                    MAP_SHARED, 
                    fbfd, 
                    0);

  if ((int)fbp == -1) {
    printf("Failed to mmap.\n");
  }
  else {
    // draw...
    int x, y;
    unsigned int pix_offset;

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

        // calculate the pixel's byte offset inside the buffer
        // see the image above in the blog...
        pix_offset = x + y * finfo.line_length;

        // now this is about the same as fbp[pix_offset] = value
        *((char*)(fbp + pix_offset)) = 16 * x / vinfo.xres;

      }
    }

    sleep(5);
  }

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

  return 0;
  
}

Save as fbtest3.c, 'make fbtest3' and run as './fbtest3' - this should produce vertical color bars at the upper half of the screen:
Hmm, so far there has been very little Raspberry Pi specific stuff in this series - all the code should work on most (if not all) Linux systems as is ...maybe I should have thought more about the title ;)

[Continues in part four]

Sunday 20 January 2013

Low-level Graphics on Raspberry Pi (part two)

In the part one we looked at how to get hold of the framebuffer and output some basic information about the display.

Not exactly very impressive, but a good start. Now let's actually draw something!

The framebuffer does not provide any functions/methods for drawing - instead, it just gives access to the 'raw' bytes of the buffer. Basically one could just use the standard file redirect '>' to output into the framebuffer:
some-bytes-from-somewhere > /dev/fb0
Obviously this is not very usable, but redirect the other way provides a quick-and-dirty raw screencapture that might come in handy:
cat /dev/fb0 > screenshot.raw
Proper way to access the buffer, is to memory map the file to a section of RAM using the mmap function.

Here we extend the example from part one - after opening the framebuffer device, we get also the fixed display information (to easily get the buffer size), map the file to the user accessible memory and finally draw something just setting the bytes in the buffer to some values:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>


int main(int argc, char* argv[])
{
  int fbfd = 0;
  struct fb_var_screeninfo vinfo;
  struct fb_fix_screeninfo finfo;
  long int screensize = 0;
  char *fbp = 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 fixed screen information
  if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
    printf("Error reading fixed information.\n");
  }

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

  // map framebuffer to user memory 
  screensize = finfo.smem_len;

  fbp = (char*)mmap(0, 
                    screensize, 
                    PROT_READ | PROT_WRITE, 
                    MAP_SHARED, 
                    fbfd, 0);

  if ((int)fbp == -1) {
    printf("Failed to mmap.\n");
  }
  else {
    // draw...
    // just fill upper half of the screen with something
    memset(fbp, 0xff, screensize/2);
    // and lower half with something else
    memset(fbp + screensize/2, 0x18, screensize/2);
  }

  // cleanup
  munmap(fbp, screensize);
  close(fbfd);
  return 0;
}


Save the above code to a file called fbtest2.c - then compile and link:
make fbtest2

Run the executable:
./fbtest2

...you should see the upper half of the display turn white and the lower blue! Hmm, except if you are in some other display mode than 16 bpp, you might get a different result...

With a bit of imaginative manipulation of what byte values to put into the buffer, one could draw more complex images - for example something like this:
...I will be getting into 'pixel plotting' in the following parts - hopefully soon...

[Continues in part three]