PiPics: slideshow app with touchscreen support 1


On February 5th, we lost out oldest dog Reba.  Reba was a 14 1/2 year old rescue and the prettiest Golden Retriever (or more likely Nova Scotia Duck Tolling retriever) mix we’ve ever seen.  Adopted from a shelter when she was perhaps 3 or 4 months old with her twin sister Phoebe (who died very young, sadly), Reba was a lover and a sneak who for some reason thought she was a cat.  She was a joy and friend for many years.  You will be sorely missed, our love.

Reba passed a couple of days before a PiBox Meetup meeting.  Being very close to my dogs – we still have Bailey and Cody – I was very distraught and wasn’t sure I would make the meeting.  So I decided to dive into a PiBox project that was well suited the situation:  creating a digital photo frame with PiBox and a Raspberry Pi 7″ touchscreen display.  Having some work to focus on was the only way to help ease the thoughts of Reba’s passing.  And it gave me a way to help remember her always.

The photo frame project (which I noted as the In Memoriam project on the Meetup group) needed two new apps:  a photo slideshow and a video player that looped a video collection.  The existing video player, VideoFE, got a kiosk mode to handle cycling through random videos without user interaction.  That work led to the long needed scalability update to the launcher app, a task that turned out to be easier than expected using gdk_pixbuf and Cairo. Both of these tasks I’ll cover in separate articles.  That left just the slideshow app, which I call PiPics.

PiPics is an app that searches for images on USB media sticks and displays each one for 10 seconds before moving to the next image, looping back to the start when it has shown all images.  The fact that the app would need to find all files of a given type on a USB media stick made it appear to be a near duplicate of what VideoFE does.  VideoFE searches for database files (created with the VideoLib java app) and calls an external player, omxplayer, to play a user selected file.

The first pass at PiPics utilized a large amount of VideoFE.  I switched to looking for a variety of static image file formats instead of video file formats.   Also, I no longer needed a database because I only have filenames to track.  There is no photo metadata.  VideoFE’s code for launching an external player was modified to launch a different app, feh.  This is a static image player that utilized a simple signal-based mechanism for moving to the next or previous image in a list.  Seemed like a perfect fit for this use case.  But it didn’t work.

Feh, as good as it is, crashed often.  While it displayed images fine it wasn’t happy moving to and fro between images using the signal mechanism.  So I dropped that plan pretty quick.

Thinking about how much I’d learned about the Cairo graphics library recently I decided I could easily build an app to draw images with that library, but only if I found some way to read more than just PNG format files.  Cairo is limited in this respect.  It reads PNG files, but nothing else.  The average user would probably be using JPEG or GIF files and perhaps a few others.  So I started looking for other libraries.

The wooden stand for this project comes from Eleduino and was purchased through Amazon.

GDK Pixbuf and Cairo

Enter gdk_pixbuf.  For a digital frame we really want the most popular formats:  gif, jpeg, png and perhaps tiff.  The gdk_pixbuf library has a single function for reading a multitude of file formats including the ones I needed.  More importantly it provided two additional features.  The first was an easy way to translate gdk_pixbuf data to Cairo data and the other was an easy way to scale the image data.

Loading an image starts with passing a filename to gdk_pixbuf.  The pointer returned is then passed to another gdk_pixbuf function to handle automatic rotation based on image metadata.

    image  = gdk_pixbuf_new_from_file(imagePath, NULL);
    newimage = gdk_pixbuf_apply_embedded_orientation (image);
    g_object_unref(image);
    image = newimage;

The second call returns a new pointer or a new reference.  By removing the reference to the original we keep from leaking references.  We then return the new pointer/reference to the original variable for later use.

The size of the image is retrieved so we can choose a scaling factor.  We want to fit the image to the display without losing the aspect ratio.  I found a good algorithm for scaling while keeping aspect ratio online.

    iwidth  = gdk_pixbuf_get_width(image);
    iheight = gdk_pixbuf_get_height(image);

    /* Compute scaling to keep aspect ratio but fit in the screen */
    ri = iwidth / iheight;
    rs = req.width / req.height;
    if ( rs > ri )
    {
        swidth = (gint)(iwidth * req.height/iheight);
        sheight = req.height;
    }
    else
    {
        swidth = req.width;
        sheight = (gint)(iheight * req.width/iwidth);
    }

The gdk_pixbuf is scaled first, then it’s set as the source for a Cairo surface and the surface is displayed.

    newimage = gdk_pixbuf_scale_simple(image, swidth, sheight, GDK_INTERP_BILINEAR);
    iwidth  = gdk_pixbuf_get_width(newimage);
    iheight = gdk_pixbuf_get_height(newimage);
    piboxLogger(LOG_INFO, "image w/h: %d / %d\n", iwidth, iheight);

    /* Center on the display */
    offset_x = (double) (((double)req.width - (double)iwidth) / (double)2.0);
    offset_y = (double) (((double)req.height - (double)iheight) / (double)2.0);
    gdk_cairo_set_source_pixbuf(cr, newimage, offset_x, offset_y);

Cairo is not strictly required here but it’s easier than working within the bounds of widget layout of GTK+.  With Cairo and a drawing area widget I can just paint on the screen.

Touchscreen Navigation

The digital photo frame uses the same launcher as the PiBox Media Center, but it only has two apps: the new PiPics app and the updated VideoFE in kiosk mode.

PiPics is very simplistic.  It’s designed to run in a kiosk mode where images are simply displayed but little user interaction is needed.  However, it does support skipping forward and backward in the list of pictures.  The usual PiBox key presses are supported – ESC to exit, left and right arrows do what you’d expect – but the point here was to integrate the new touch screen support from libpibox.  The original implementation of this support was tested with VideoFE so, again, I was able to port example code from that project.

The touchscreen support provides two ways to identify touches.  The first is an absolute screen coordinate.  This is useful for the launcher which needs to know exactly which icon was touched in order to launch a specific application.  The other method is to get a screen region.  The touchscreen is divided into 9 equal regions in a 3×3 cell matrix.  This provides a nice consumer orientation for the touchscreen while using as much screen space as possible.

The first thing you need is to initialize the touch screen library.

    if ( piboxGetDisplayType() == PIBOX_LCD )
    {   
        piboxLogger(LOG_INFO, "Registering imageTouch.\n");
        piboxTouchRegisterCB(imageTouch, TOUCH_REGION);
        piboxTouchStartProcessor();
    }

The pibox functions are a new libpibox features.  Here we register a callback that will use touch regions from the touch screen library and starts the thread that handles touch screen notifications.

Due to the way GTK+ works with threads, you can’t update the screen directly in the imageTouch callback.  Instead, this callback simply adds a callback to the glib idle loop so it gets called the next chance the main loop has an opportunity.

PiPics uses the region feature of the touch screen library.  Prev and next images are accessed using cells in the middle of the display on the left and right, respectively.  Exit, to be consistent with other apps ported to use the touchscreen library, is the upper right and causes the launcher to regain control of the UI.  Pause and play are in the middle of the screen.

 --------------------------------
 | 0: N/A | 1: N/A    | 2: Exit |
 --------------------------------
 | 3: Prev| 4: N/A    | 5: Next |
 --------------------------------
 | 6: N/A | 7: N/A    | 8: N/A  |
 --------------------------------

Deployment

PiPics, like all PiBox apps, is built as an opkg.  Installation just requires copying the package over as root and running the installation.

opkg-cl install pipics_0.10_arm.opk

The recent updates to the launcher app allow me to use only a couple of apps and they will be displayed centered on their section of the launcher display.  So the digital photo frame now has a more polished appearance, all based on the original PiBox Media Center work.

 


Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

One thought on “PiPics: slideshow app with touchscreen support