Embedded Artists
We are the leading providers of products and services around prototyping, evaluation and OEM platforms using NXP's ARM-based microcontrollers.
LPC4088DM Using the SlideShow Engine
The DMBasicGUI library comes with, among other things, a slideshow engine supporting:
- Continuous or one time slideshows
- Script controlled slideshow so it can be changed without recompiling the program
- Several different transitions to choose between
- Slideshow can be full screen or in part of the screen
- Nested slideshows
- Pre-loading of images while waiting for the next slide
- "Video" support
Add the slideshow engine by importing the DMBasicGUI library:
Import libraryDMBasicGUI
A basic graphics package for the LPC4088 Display Module.
Example¶
There is an AppSlideShow.cpp in DMBasicGUI showing how to use the SlideShow class, but this is basically what is needed:
#include "mbed.h" #include "SlideShow.h" #include "Renderer.h" static void tRender(void const *args) { Renderer* s = (Renderer*)args; s->render(); } int main(void) { // initialize the display ... // create the renderer that will handle the display Renderer r; // create the slideshow SlideShow s(&r); // parse and validate the script SlideShow::SlideShowError err = s.prepare("/mci/myscript.txt"); if (err != SlideShow::Ok) { // handle error here } // Create the thread to handle the display Thread tr(tRender, &r, osPriorityHigh); // Run the slideshow s.run(); }
Place two images called img1.png and img2.png in the root of the uSD card together with this file:
myscript.txt
load /mci/img1.png 1 show 1 none load /mci/img2.png 2 wait 1500 show 2 none
Classes¶
The engine consists of one Renderer serving one or more SlideShows.
The Renderer is responsible for putting together the result and showing it on the display. It handles the different layers and possibly different concurrent slideshows. The user code only have to create the Renderer, no further interaction is needed.
Import library
Public Member Functions |
|
uint32_t | registerUser (int layer, int xoff, int yoff, int width, int height) |
Registers a part of the screen on a specific layer to be in use.
|
|
uint32_t | registerFullscreenUser (int layer) |
Registers the entire screen on a specific layer to be in use.
|
|
void | unregisterUser (uint32_t handle) |
Removes a previously registered user.
|
|
void | setFramebuffer (uint32_t handle, const uint16_t *data) |
Informs the renderer that there is new data to render.
|
|
void | render () |
Run the renderer.
|
The SlideShow class parses and executes slideshow scripts, handling delays and transitions.
Import library
Public Types |
|
typedef SlideShowError(* | calloutFunc )(int calloutId, SlideShow *ss, int identifier) |
A function that the script can call during execution.
|
|
Public Member Functions |
|
SlideShow ( Renderer *r, const char *pathPrefix=NULL, int xoff=0, int yoff=0, int layer=0, Mutex *fileMutex=NULL) | |
Create a new slideshow.
|
|
SlideShowError | prepare (const char *scriptFile) |
Parses the script file and prepares internal structures.
|
|
SlideShowError | run () |
Run the slideshow.
|
|
void | setCalloutHandler ( calloutFunc func, int calloutId) |
Register a function that the script can call.
|
|
void | releaseScreen (void) |
Decouples this
SlideShow
from the
Renderer
.
|
|
void | stop () |
Stops the
SlideShow
.
|
Scripting¶
At the heart of the SlideShow engine is the script specifying what to do and it which order. An example of a script:
load /slides/img1.png 1 show 1 none load /slides/img2.png 2 wait 1500 show 2 none
The script above loads and shows the first image and then loads image two. After 1.5 seconds the second image is displayed. There are no transitions used, the new image replaces the old immediately. The script will execute once.
File Format¶
The script files:
- Ignores leading whitespace
- Uses \n and/or \r as line termination
- Treats lines starting with # as comments
- All commands must be in lowercase
Supported commands:
Command | Description |
---|---|
# something | Lines starting with # are treated as comments and are ignored |
clear clear <color> clear <color> <trans> | Clears the slideshow area. <color> is a hex value of the color to clear with, default is white <trans> the transition to use, default is none |
show <slot> <trans> | Shows image in the specified slot with the specified transition |
wait <ms> | Waits up to ms milliseconds (explained below) |
callout <id> | Calls a user function passing along the specified id |
label <string> | Defines a position in the slideshow sequence that can be referenced by a goto command. Labels must be given unique names within the script |
goto <label> | Sets the next command in the script to be the command after the specified label |
load <file> <slot> | Loads the specified image file into the specified slot, overwriting old content if any |
Loading Images¶
Images are loaded from files into slots using the load command. Slots can be reused by loading a new image into an existing slot. If there is enough memory, it could be a good idea to preload all images before the slideshow start:
load /mci/img1 1 load /mci/img2 2 ... label start show 1 none wait 1000 show 2 none ... goto start
The paths of the images in the script above are both hardcoded to be loaded from an uSD card (the /mci/ part of the path). Sometime it is better to keep the exact location of the images out of the script and that is what the pathPrefix parameter in the SlideShow constructor is for:
load /slides/img1 1 load /slides/img2 2 ... label start show 1 none wait 1000 show 2 none ... goto start
... Renderer r; SlideShow s(&r, "/mci/demo"); ...
The result of the SlideShow constructor above is that the images in the script will be loaded from "/mci/demo/slides/img1.png" and "/mci/demo/slides/img2.png".
Transitions¶
The slideshow engine supports the following transitions:
none | The new image will immediately replace the old one |
left-right | The new image is rolled in from the left, gradually covering more and more of the old image |
down-up | The old image is gradually shifted upwards as the new image is shifted in |
top-down | The old image is gradually shifted downwards as the new image is shifted in |
blinds | A curtain is pulled down over the old image and as it is rolled up again the new image appears |
fade | The old image gradually fades into the new image |
All transitions have predefined execution times, see the SlideShow.h file for exact times.
Which transition to use is specified in the script's show and clear commands:
show 1 top-down clear 001f fade show 2 none show 3 blinds
Delays¶
It is essential to be able to specify how long a slide show be shown before switching to the next one. This is controlled in the script file with the wait command.
The current time is stored each time an image is show (show), the screen is cleared (clear) or a delay (wait) has finished executing. The delay is then counted from the stored value and not from the current time.
In this example img2 will be show 2 seconds after img1 is shown
load /img1.png 1 load /img2.png 2 show 1 none wait 2000 show 2 none
In this example img2 will be show 2 seconds after img1 is shown. Note that the time it takes to load img2 does not effect the time when img2 is shown (of course assuming that it is less than 2 seconds).
load /img1.png 1 show 1 none load /img2.png 2 wait 2000 show 2 none
If the images are quick to load then this can be used to load images while showing the first few slides without the user noticing:
load /img1.png 1 show 1 none load /img2.png 2 load /img3.png 3 load /img4.png 4 load /img5.png 5 load /img6.png 6 wait 4000 show 2 none load /img7.png 7 load /img8.png 8 load /img9.png 9 load /img10.png 10 load /img11.png 11 wait 4000 show 3 none ...
Advanced¶
The SlideShow engine supports multiple slideshows running at the same time on the same display. An example could be to have a background slideshow running and when it comes to slide 3 it pauses and executes another slideshow perhaps showing a video on part of the screen. When the video has completed the main slideshow continues.
To handle nesting of slideshows in that way there must be a way for them to interact. This is handled with the callout command in the script and the setCalloutHandler() function in the code.
A simple example letting the slideshow print a counter on each loop of the presentation:
load /slides/img1.png 1 load /slides/img2.png 2 label start show 1 fade wait 1000 show 2 blinds callout 100 wait 2000 goto start
#include "SlideShow.h" #include "DMBoard.h" static SlideShow::SlideShowError handler(int calloutId, SlideShow* ss, int identifier) { static int counter = 0; DMBoard::instance().logger()->printf("counter = %d\n", counter++); } void main() { ... SlideShow s(...); s.setCalloutHandler(&handler, 42); ... }
The example above would result in the following printouts (roughly 3 seconds apart):
counter = 0 counter = 1 ...
The callout function is very versatile and can be used to delay execution or pass different sorts of messages:
static SlideShow::SlideShowError handler(int calloutId, SlideShow* ss, int identifier) { switch (calloutid) { case 42: switch (identifier) { case 1: // Slideshow has finished preloading of images break; case 2: // Slideshow has completed yet another loop break; case 3: // allow the delay to be controlled from code instead of from script wait_ms(1000); break; break; case ... }
The callout can also be used to synchronize two or more presentations:
The first "master" script shows slides 1 and 2 and then lets the other script run before continuing.
... label a show 1 fade wait 1000 show 2 fade callout 50 show 3 fade wait 2000 goto a
The "video" script asks for permission to start (callout 0) and notifies when done (callout 100).
... label start callout 0 show 1 none show 2 none show 3 none show 4 none show 5 none callout 100 goto start
The corresponding code could be:
#include "SlideShow.h" #include "DMBoard.h" #define ID_MAIN 10 #define ID_VIDEO 20 // This is just for the example, a better way is to use signalling or a Mutex static bool volatile allowedToStartVideo = false; static bool volatile videoFinished = true; static SlideShow::SlideShowError handler(int calloutId, SlideShow* ss, int identifier) { if (calloutid == ID_MAIN) { if (identifier == 50) { // main slideshow wants to start video slideshow videoFinished = false; allowedToStartVideo = true; // wait for it to complete while (!videoFinished) { // only as example, should use signalling instead } } } else if (calloutid == ID_VIDEO) { if (identifier == 0) { // wait for permission to start while (!allowedToStartVideo) { // only as example, should use signalling instead } allowedToStartVideo = false; } else if (identifier == 100) { // let the main slideshow know that the video is done videoFinished = true; } } } static void slideTask(const void* args) { SlideShow* ss = (SlideShow*)args; ss->run(); } void main() { ... SlideShow sMain(...); sMain.setCalloutHandler(&handler, ID_MAIN); sMain.prepare("/mci/main.txt"); Thread tMain(slideTask, &sMain); SlideShow sVideo(...); sVideo.setCalloutHandler(&handler, ID_VIDEO); sVideo.prepare("/mci/video.txt"); Thread tVideo(slideTask, &sVideo); ... }