Friday, 4 March 2016

Low-level Graphics on Raspberry Pi vs other Linux distributions

In the very first post on this subject I suggested the examples would apply to other systems as well - luckily I did add the caveat 'some of the code' ;)

As it appears that the support for the framebuffer driver and the functionality it provides seems to in fact differ a lot between Linux distributions and graphics hardware specific drivers. This was way simpler back in the day I started with Linux fb some 20 years ago... So unfortunately cannot promise the examples will fully work on even another Debian based system. Some of you readers had already found this out some time ago, apologies if it seemed I promised more than I delivered.

I have recently played with a Debian 8.3.0 distribution on a desktop PC. I found out that for example changing the bits_per_pixel does not return errors and querying the information back after setting looks like it went through. But trying to plot pixels will not produce the expected result - it appears that the fb is still in 32 bit mode even when it reports 8 bit :/ Of course any 32 bit pixel plotting code works fine...

I modified the original test program a bit to output some more information. One additional bit (well, a string actually :D ) of information is the fb_fix_screeninfo.id which should give us the fb driver 'name' - for my setup this is inteldrmfb:

The 'drm' part there suggests that the driver is implementing the more recent Direct Rendering Manager (DRM) model and the fbdev support is not complete. If running other distributions and interested in low level graphics it would be worth investigation the DRM API libdrm.

Wednesday, 24 February 2016

Low-level Graphics on Raspberry Pi (more images)

In an earlier post we looked at converting an image to 16 bit 5:6:5 format for direct copying to framebuffer. The obvious drawback of that approach is the requirement on the image size to exactly match the display/framebuffer size (or the width at least).

Modifying the PPM reading code to read into an image object in memory (I chose to use the struct fb_image from linux/fb.h as teh image object) and combining that with the familiar framebuffer drawing code (put_pixel_RGB565 from part 6)
...
int read_ppm(char *fpath, struct fb_image *image) {
...
    image->data = malloc(width * height * bytes_per_pixel);
...
        // store pixel in memory
        unsigned int pix_offset = (y * width + x ) * bytes_per_pixel;
        *((unsigned short *)(image->data + pix_offset)) = rgb565;
...

void draw(struct fb_image *image) {
    int y;
    int x;
    unsigned char rgb[3];

    for (y = 0; y < image->height; y++) {
        for (x = 0; x < image->width; x++) {
            // get pixel from image
            unsigned int img_pix_offset = (y * image->width + x) * 2;
            unsigned short c = 
                *(unsigned short *)(image->data + img_pix_offset);
            // plot pixel to screen
            unsigned int fb_pix_offset = 
                x * 2 + y * finfo.line_length;
            *((unsigned short*)(fbp + fb_pix_offset)) = c;
        }
    }
}

...
int main(int argc, char* argv[])
{

    // read the image file
    int ret = read_ppm(argv[1], &image);
    if (ret != 0) {
        printf("Reading image failed.\n");
        return ret;
    }
...

Make sure you have a 24 bit PPM image file to start with - convert one from for example PNG using pngtopnm (any bit depth source file should do) - and make sure the image fits the screen (no bounds checking in the code). Then compile and run:
gcc -O2 -o ppmtofbimg ppmtofbimg.c
./ppmtofbimg test24.ppm
Full source and a test image (test24.ppm) in GitHub.