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.

Tuesday 23 February 2016

Modifying Ctrl+Alt+Del behavior in Debian Jessie vs Wheezy

Finally got up to testing Raspbian Jessie and being a mixed user of Linux and Windows ran into an issue with the 'three finger salute' Ctrl-Alt-Del (C-D-A) key combination. In Windows this is normally used to lock up the UI when going away from the computer and often comes automatically 'from the spine'. In Linux it nowadays seems to normally initiates a reboot ...in the windowing systems this usually pops up a dialog asking to confirm but when working in the console only it just reboots. Which is of course fairly annoying when not intentional :|

The previous release of Raspbian - based on Debian Wheezy - is built on the sysvinit style boot and the Ctrl-Alt-Del behavior is defined in the /etc/inittab file and can be disabled by commenting out the line that looks something like:
...
# What to do when CTRL-ALT-DEL is pressed.
ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now
...
So:
sudo nano /etc/inittab
*** find the line with 'ctrlaltdel' (Ctrl-W for search)
*** comment out by adding the hash '#' at beginning of line
*** save file (Ctrl-O) and exit (Ctrl-X)
sudo init q
...the last command 're-reads' the inittab file and applies the change.

But Jessie is built on the systemd style boot and there is no more /etc/inittab. Surprisingly I did not find a clear, full answer to this using Google - some links like Securing Debian Manual (4.8 Restricting system reboots through the consol) are outdated for Jessie - some are unanswered - some only offer partial information - and some discuss slightly different variation of behavior wanted. Combining the information from those it appears the behavior is controlled by /lib/systemd/system/ctrl-alt-del.target (not /etc/systemd/system/*):
ls -la /lib/systemd/system/ctrl*

lrwxrwxrwx 1 root root 13 Aug 30 23:04 /lib/systemd/system/ctrl-alt-del.target -> reboot.target
To change this we need to remove the current link and replace:
sudo rm /lib/systemd/system/ctrl-alt-del.target
sudo ln -s /dev/null /lib/systemd/system/ctrl-alt-del.target
sudo systemctl daemon-reload
...so to disable Ctrl-Alt-Del we point the target to /dev/null i.e. to be completely ignored and again the last command 're-reads' the configuration and applies the change.

Thursday 11 February 2016

Low-level Graphics on Raspberry Pi (even more palette)

Another effect with palette: fade in/out. We can - in addition to rotating the entries - modify the palette entry RGB values on the fly and make the pixels drawn in this color to fade in or out.

Draw some background stuff (blue and white checkerboard), a black block to hold the actual piece and something to fade:
...
#define DEF_COLOR 14
#define MOD_COLOR (16 + DEF_COLOR)
...
    int x, y;
    int t = vinfo.xres / 24;

    // t x t pix black and white checkerboard
    for (y = 0; y < vinfo.yres; y++) {
        int xoffset = y / t % 2;
        for (x = 0; x < vinfo.xres; x++) {

            // color based on the tile
            int c = (((x / t + xoffset) % 2) == 0) ? 1 : 15;

            // draw pixel
            put_pixel(x, y, c);

        }
    }
    // black block in the middle
    for (y = (vinfo.yres / 2); y < (vinfo.yres / 2 + t); y++) {
        for (x = (vinfo.xres / 4 - t / 2); 
             x < (vinfo.xres / 4 * 3 + t / 2); x++) 
        {
            put_pixel(x, y, 0);
        }
    }
    // something in the color in the extended palette
    for (y = (vinfo.yres / 2 + t / 4); 
         y < (vinfo.yres / 2 + t - t / 4); y++) 
    {
        int n = 0;
        for (x = (vinfo.xres / 4); x < (vinfo.xres / 4 * 3); x++) {
            if (n % (t / 4)) {
                put_pixel(x, y, MOD_COLOR);
            }
            n++;
        }
    }
...
And then modify the associated palette entry:
...
        // fade palette entry out (to black)" and back in
        int j;
        int fps = 60; // frames per second
        int f = 255;
        int fd = -2;
        while (f < 256) {
            r[DEF_COLOR] = (def_r[14] & f) << 8;
            g[DEF_COLOR] = (def_g[14] & f) << 8;
            b[DEF_COLOR] = (def_b[14] & f) << 8;
            // change fade
            f += fd;
            // check bounds
            if (f < 0) {
                // change direction
                fd *= -1;
                f += fd;
            }
            // Note that we set up the 'pal' structure earlier
            // and it still points to the r, g, b arrays,
            // so we can just reuse 'pal' here
            if (ioctl(fbfd, FBIOPUTCMAP, &pal) != 0) {
                printf("Error setting palette.\n");
            }
            usleep(1000000 / fps);
        }
...
Compile with 'gcc -o fbtest5z fbtest5z.c' and run with './fbtest5y'. The yellow bars should fade out and back in:
...imagine for example black background and white text fading in and out... movie titles :D

Full code available in GitHub.

Wednesday 10 February 2016

Low-level Graphics on Raspberry Pi (more palette)

In a previous post we briefly looked at palette animation. Now (hopefully) a slightly more appetising example of what could be done with this technique.

Let's draw some 'rainbow striped' blocks and customise the palette to include 16 colors sliding from red to yellow:
...
void draw() {
    ...
    // colored blocks
    for (y = 0; y < vinfo.yres; y += t) {
        int xoffset = y / t % 2;
        for (x = t * xoffset; x < vinfo.xres; x += t * 2) {
            int x2, y2;
            for (y2 = 0; y2 < t; y2++) {
                for (x2 = 0; x2 < t; x2++) {

                    // color based on y2 value
                    // using the custom colors (16+)
                    int c = 16 + (y2 % 16);

                    // draw pixel
                    put_pixel(x + x2, y + y2, c);

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

    // Set palette
    ...
    for(i = 0; i < 16; i++) {
        // red-yellow gradient
        // note that Linux provides more precision (0-65535),
        // so we multiply ours (0-255) by 256
        r[i] = 255 << 8;
        g[i] = ((15 - i) * 16) << 8;
        b[i] = 0;
    }

...

And then animate the palette by rotating the custom color entries:
...
        int j;
        int fps = 30; // frames per second
        int d = 5; // duration in seconds
        // repeat for given time
        for(j = 0; j < fps * d; j++) {
            // store color 0 in temp variables
            int rt = r[0];
            int gt = g[0];
            int bt = b[0];
            // replace colors by copying the next
            for(i = 0; i < 15; i++) {
                r[i] = r[i+1];
                g[i] = g[i+1];
                b[i] = b[i+1];
            }
            // restore last one from temp
            r[15] = rt;
            g[15] = gt;
            b[15] = bt;
            // Note that we set up the 'pal' structure earlier
            // and it still points to the r, g, b arrays,
            // so we can just reuse 'pal' here
            if (ioctl(fbfd, FBIOPUTCMAP, &pal) != 0) {
                printf("Error setting palette.\n");
            }
            usleep(1000000 / fps);
        }
...
Compile with 'gcc -o fbtest5y fbtest5y.c' and run with './fbtest5y'. The blue checker board with holes looks like floating on top of flowing lava waves ;)

Full code available in GitHub.

[Continued in next part Even more palette]