Basic swim GUI for LPC4088

Fork of DMBasicGUI by Embedded Artists

Revision:
5:f4de114c31c3
Child:
10:651861441108
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SlideShow/SlideShow.cpp	Sun Dec 21 13:53:07 2014 +0100
@@ -0,0 +1,914 @@
+#include "SlideShow.h"
+#include "Image.h"
+#include "mbed_debug.h"
+#include "DMBoard.h"
+
+/******************************************************************************
+ * Defines and typedefs
+ *****************************************************************************/
+
+#define SLIDESHOW_DBG             0
+
+#define NO_SLOT   -12   /* Some constant to indicate that no slot is in use */
+
+/* Helper macros for the Fade transition */
+#define FADE_XRED(__in)   (((__in)>>11)&0x1f)
+#define FADE_XGREEN(__in) (((__in)>>6)&0x3f)
+#define FADE_XBLUE(__in)  ((__in)&0x1f)
+#define FADE_COMBINE(__old, __new, __mul) \
+        ( ((((FADE_XRED(__old)*(8-(__mul)))+(FADE_XRED(__new)*(__mul)))>>3)<<11) \
+        | ((((FADE_XGREEN(__old)*(8-(__mul)))+(FADE_XGREEN(__new)*(__mul)))>>3)<<5) \
+        | (((FADE_XBLUE(__old)*(8-(__mul)))+(FADE_XBLUE(__new)*(__mul)))>>3) )
+
+
+/******************************************************************************
+ * Global variables
+ *****************************************************************************/
+
+extern volatile uint32_t msTicks;
+
+/******************************************************************************
+ * Private Functions
+ *****************************************************************************/
+
+void SlideShow::Command::print()
+{
+    switch(type)
+    {
+        case Clear:
+            printf("CMD: Clear screen\n");
+            break;
+        case Goto:
+            printf("CMD: Goto command %d\n", information);
+            break;
+        case LoadImage:
+            printf("CMD: Load file %s into [%d]\n", fname, information);
+            break;
+        case Show:
+            printf("CMD: Show image [%d] with %s transition\n", information, transition->typeString());
+            break;
+        case Wait:
+            printf("CMD: Wait %d ms\n", information);
+            break;
+        case Callout:
+            printf("CMD: Callout %d\n", information);
+            break;
+        default:
+            printf("Unknown command\n");
+    }
+}
+
+SlideShow::SlideShowError SlideShow::Command::handle(SlideShow* ss, int* seqIdx, int* lastTime)
+{
+    SlideShowError result = Ok;
+
+    //printf("[%03d] ", *seqIdx); print();
+    switch (type)
+    {
+        case Clear:
+            // Use the 3rd back buffer as a fake image for the transition
+            Image::ImageData_t d;
+            d.height = ss->screenHeight;
+            d.width = ss->screenWidth;
+            d.pixels = &ss->ImageBackBuffer[ss->screenPixels*2];
+            d.pointerToFree = NULL;
+            memset(d.pixels, information, ss->screenBytes);
+            if (ss->CurrentSlot == NO_SLOT) {
+                result = transition->execute(ss, NULL, &d);
+            } else {
+                result = transition->execute(ss, &(ss->PreparedImages[ss->CurrentSlot]), &d);
+            }
+            *lastTime = msTicks;
+            *seqIdx+=1;
+            break;
+
+        case Goto:
+            *seqIdx = information;
+            break;
+
+        case LoadImage:
+            if ((result = ss->loadImage(fname, information)) != Ok)
+            {
+                printf("Failed to load image. Aborting...\n");
+                break;
+            }
+            *seqIdx+=1;
+            break;
+
+        case Show:
+            if (ss->CurrentSlot == NO_SLOT) {
+                result = transition->execute(ss, NULL, &(ss->PreparedImages[information]));
+            } else {
+                result = transition->execute(ss, &(ss->PreparedImages[ss->CurrentSlot]), &(ss->PreparedImages[information]));
+            }
+            if (result != Ok) {
+                printf("Failed to show image. Aborting...\n");
+                break;
+            }
+            ss->CurrentSlot = information;
+            *lastTime = msTicks;
+            *seqIdx+=1;
+            break;
+
+        case Wait:
+            ss->delay(*lastTime, information);
+            *lastTime = msTicks;
+            *seqIdx+=1;
+            break;
+
+        case Callout:
+            if (ss->callout != NULL) {
+                result = ss->callout(ss->calloutId, ss, information);
+            } else {
+                // Silently accept that no callout listener is registered
+            }
+            *seqIdx+=1;
+            break;
+
+        default:
+            printf("Found unknown command at index %d\n", *seqIdx);
+            result = InvalidScript;
+    }
+    return result;
+}
+
+SlideShow::SlideShowError SlideShow::Transition::execute(SlideShow* ss, Image::ImageData_t* CurrentImage, Image::ImageData_t* NewImage)
+{
+  SlideShowError result = Ok;
+
+  do {
+
+    // TODO: This would be a good place to handle rendering of differently sized images,
+    //       could unregister+register if NewImage is different from CurrentImage
+
+    // Register with the Renderer if needed.
+    if (ss->rendHnd == 0) {
+      if (ss->rend == NULL) {
+        printf("No registered renderer\n");
+        result = RuntimeError;
+        break;
+      }
+
+      // time to register with the renderer
+      ss->rendHnd = ss->rend->registerUser(ss->layer, ss->drawXoff, ss->drawYoff,
+                                           NewImage->width, NewImage->height);
+      if (ss->rendHnd == 0) {
+        printf("Failed to register with renderer\n");
+        result = RuntimeError;
+        break;
+      }
+    }
+
+    switch (t) {
+      case None:
+      {
+        ss->rend->setFramebuffer(ss->rendHnd, NewImage->pixels);
+      }
+      break;
+
+      case LeftRight:  // TODO: Note that this transition is only implemented for fullscreen mode
+      {
+        // Create a buffer with the old image
+        if (CurrentImage == NULL) {
+          memset(ss->ImageBackBuffer, 0, ss->screenBytes);
+        } else {
+          memcpy(ss->ImageBackBuffer, CurrentImage->pixels, ss->screenBytes);
+        }
+        int end = ss->screenWidth - LeftRight_PixelsToSkip;
+        for (int x = 0; x < end; x += LeftRight_PixelsToSkip)
+        {
+          int off = 0;
+          for (int y = 0; y < ss->screenHeight; y++)
+          {
+            memcpy(ss->ImageBackBuffer + (off+x), NewImage->pixels + (off+x),
+                   LeftRight_PixelsToSkip*sizeof(uint16_t));
+            off += ss->screenWidth;
+          }
+
+          // Show the updated image
+          ss->rend->setFramebuffer(ss->rendHnd, ss->ImageBackBuffer);
+
+          // Sleep and do over again
+          wait_ms(LeftRight_DelayMs);
+        }
+
+        // Show final image
+        ss->rend->setFramebuffer(ss->rendHnd, NewImage->pixels);
+      }
+      break;
+
+      case DownUp:  // TODO: Note that this transition is only implemented for fullscreen mode
+      {
+        // Create a buffer with the two images after each other, NewImage below
+        if (CurrentImage == NULL) {
+          memset(ss->ImageBackBuffer, 0, ss->screenBytes);
+        } else {
+          memcpy(ss->ImageBackBuffer, CurrentImage->pixels, ss->screenBytes);
+        }
+        memcpy(ss->ImageBackBuffer+ss->screenPixels, NewImage->pixels, ss->screenBytes);
+
+        // We will be using a back buffer
+        for (int i = DownUp_LineSkip/2; i < (ss->screenHeight-1); i+=DownUp_LineSkip)
+        {
+          // Show image by advancing what is shown one line at a time
+          ss->rend->setFramebuffer(ss->rendHnd, ss->ImageBackBuffer + i*ss->screenWidth);
+
+          // Sleep and do over again
+          wait_ms(DownUp_DelayMs);
+        }
+
+        // show final image
+        ss->rend->setFramebuffer(ss->rendHnd, NewImage->pixels);
+      }
+      break;
+
+      case TopDown:  // TODO: Note that this transition is only implemented for fullscreen mode
+      {
+        // Create a buffer with the two images after each other, NewImage above
+        if (CurrentImage == NULL) {
+          memset(ss->ImageBackBuffer+ss->screenPixels, 0, ss->screenBytes);
+        } else {
+          memcpy(ss->ImageBackBuffer+ss->screenPixels, CurrentImage->pixels, ss->screenBytes);
+        }
+        memcpy(ss->ImageBackBuffer, NewImage->pixels, ss->screenBytes);
+
+        // We will be using a back buffer
+        for (int i = ss->screenHeight - TopDown_LineSkip/2; i > 0; i-=TopDown_LineSkip)
+        {
+          // Show image by advancing what is shown one line at a time
+          ss->rend->setFramebuffer(ss->rendHnd, ss->ImageBackBuffer + i*ss->screenWidth);
+
+          // Sleep and do over again
+          wait_ms(TopDown_DelayMs);
+        }
+
+        // show final image
+        ss->rend->setFramebuffer(ss->rendHnd, NewImage->pixels);
+      }
+      break;
+
+      case Blinds:
+      {
+        int i;
+        int blockNumPixels = Blinds_LinesPerBlock * ss->screenWidth;
+        int blockNumBytes = blockNumPixels * sizeof(uint16_t);
+        image_t beamBlock = ss->ImageBackBuffer + ss->screenPixels;
+        image_t bkgBlock = beamBlock + blockNumPixels;
+
+        // Create a buffer with the old image
+        if (CurrentImage == NULL) {
+          memset(ss->ImageBackBuffer, 0, ss->screenBytes);
+        } else {
+          memcpy(ss->ImageBackBuffer, CurrentImage->pixels, ss->screenBytes);
+        }
+
+        // Create the two coloured blocks
+        memset(beamBlock, Blinds_BeamColor, blockNumBytes);
+        memset(bkgBlock, Blinds_BackColor, blockNumBytes);
+
+        for (i = 0; i < ss->screenPixels; i += blockNumPixels)
+        {
+          // Draw the moving beam, erasing the old image
+          memcpy(ss->ImageBackBuffer+i, beamBlock, blockNumBytes);
+
+          // Fill upp behind the beam with background color
+          if (i > 0) {
+            memcpy(ss->ImageBackBuffer+i-blockNumPixels, bkgBlock, blockNumBytes);
+          }
+
+          // Show the updated image
+          ss->rend->setFramebuffer(ss->rendHnd, ss->ImageBackBuffer);
+
+          // Sleep and do over again
+          wait_ms(Blinds_DelayMs);
+        }
+        memcpy(ss->ImageBackBuffer+i-blockNumPixels, bkgBlock, blockNumBytes);
+        ss->rend->setFramebuffer(ss->rendHnd, ss->ImageBackBuffer);
+        wait_ms(Blinds_DelayMs);
+
+        for (i = 0; i < ss->screenPixels; i += blockNumPixels)
+        {
+          // Draw the moving beam, erasing the old image
+          memcpy(ss->ImageBackBuffer+i, beamBlock, blockNumBytes);
+
+          // Fill upp behind the beam with the new image
+          if (i > 0) {
+            memcpy(ss->ImageBackBuffer+i-blockNumPixels, NewImage->pixels+i-blockNumPixels, blockNumBytes);
+          }
+
+          // Show image by advancing what is shown one line at a time
+          ss->rend->setFramebuffer(ss->rendHnd, ss->ImageBackBuffer);
+
+          // Sleep and do over again
+          wait_ms(Blinds_DelayMs);
+        }
+
+        // show final image
+        ss->rend->setFramebuffer(ss->rendHnd, NewImage->pixels);
+      }
+      break;
+
+      case Fade:
+      {
+        // Create a buffer with the old image
+        if (CurrentImage == NULL) {
+          memset(ss->ImageBackBuffer, 0, ss->screenBytes * 2); // use an extra backbuffer
+        } else {
+          memcpy(ss->ImageBackBuffer, CurrentImage->pixels, ss->screenBytes);
+        }
+
+        int firstY = 0;
+        int lastY = NewImage->height;
+        for (int y = 0, off=0; y < NewImage->height; y++) {
+          for (int x = 0; x < NewImage->width; x++) {
+            off++;
+            if (NewImage->pixels[off] != ss->ImageBackBuffer[off]) {
+              firstY = y;
+              y = NewImage->height;
+              break;
+            }
+          }
+        }
+        for (int y = NewImage->height-1, off=NewImage->height*NewImage->width-1; y > firstY; y--) {
+          for (int x = 0; x < NewImage->width; x++) {
+            off--;
+            if (NewImage->pixels[off] != ss->ImageBackBuffer[off]) {
+              lastY = y;
+              y = -1;
+              break;
+            }
+          }
+        }
+
+        // Gradually fade between the old and new images
+        for (int pass = 1; pass < 8; pass++)
+        {
+          uint16_t* oldImg = CurrentImage==NULL ? &ss->ImageBackBuffer[ss->screenPixels] : &CurrentImage->pixels[firstY*NewImage->width];
+          uint16_t* newImg = &NewImage->pixels[firstY*NewImage->width];
+          uint16_t* dstImg = &ss->ImageBackBuffer[firstY*NewImage->width];
+          for (int y = firstY; y <= lastY; y++)
+          {
+            for (int x = 0; x < NewImage->width; x++)
+            {
+              if (*oldImg != *newImg) {
+                *dstImg = FADE_COMBINE(*oldImg, *newImg, pass);
+              }
+              oldImg++;
+              newImg++;
+              dstImg++;
+            }
+          }
+          // Show the updated image
+          ss->rend->setFramebuffer(ss->rendHnd, ss->ImageBackBuffer);
+
+          // Sleep and do over again
+          wait_ms(Fade_DelayMs);
+        }
+
+        // show final image
+        ss->rend->setFramebuffer(ss->rendHnd, NewImage->pixels);
+      }
+      break;
+
+      case Unknown:
+      default:
+        result = RuntimeError;
+    }
+  } while(0);
+
+  return result;
+}
+
+SlideShow::SlideShowError SlideShow::loadFile(const char* path, uint8_t** pData, uint32_t* pSize)
+{
+    FILE* f = NULL;
+    uint32_t pos, size, num;
+    SlideShowError result = Ok;
+
+    *pData = NULL;
+    *pSize = 0;
+
+    if (fileMutex != NULL) {
+        fileMutex->lock();
+    }
+    do
+    {
+        f = fopen(path, "r");
+        if (f == NULL) {
+            printf("Failed to open file %s for reading\n", path);
+            result = FileError;
+            break;
+        }
+
+        // Determine file size
+        pos = ftell(f);
+        fseek(f, 0, SEEK_END);
+        size = ftell(f);
+        fseek(f, pos, SEEK_SET);
+
+        // Allocate memory to read into
+        *pData = (unsigned char*)malloc(size);
+        if (*pData == NULL) {
+            printf("Failed to allocate %u bytes to load %s into\n", size, path);
+            result = OutOfMemory;
+            break;
+        }
+
+        // Read entire file
+        *pSize = size;
+        pos = 0;
+        do {
+            num = fread(*pData + pos, 1, size, f);
+            if (num > 0) {
+                size -= num;
+                pos += num;
+            }
+        } while ((num > 0) && (size > 0));
+
+        if (size != 0) {
+            printf("Failed to read entire %s, got %u of %ul\n", path, pos, *pSize);
+            result = FileError;
+            break;
+        }
+
+        // All OK
+
+    } while(0);
+
+    if (f != NULL) {
+        fclose(f);
+    }
+    if (result != Ok) {
+        if (*pData != NULL) {
+            free(*pData);
+            *pData = NULL;
+            *pSize = 0;
+        }
+    }
+
+    if (fileMutex != NULL) {
+        fileMutex->unlock();
+    }
+
+    return result;
+}
+
+
+// pBuf in, pOffset in/out, pLine out
+// returns 0 as long as a token is found
+int SlideShow::getNextLine(char* pBuf, int* pOffset, char** ppLine)
+{
+    int pos = *pOffset;
+    int result = -1;
+
+    // trim whitespace from start of line
+    while ((pBuf[pos] == ' ') || (pBuf[pos] == '\t'))
+    {
+        pos++;
+    }
+    *ppLine = &(pBuf[pos]);
+
+    while (pBuf[pos] != '\0')
+    {
+        if ((pBuf[pos] == '\r') || (pBuf[pos] == '\n'))
+        {
+            // found the next end of line
+            pBuf[pos++] = '\0';
+            result = 0;
+
+            // move past all end-of-line characters
+            while ((pBuf[pos] == '\r') || (pBuf[pos] == '\n'))
+            {
+                pos++;
+            }
+            break;
+        }
+        pos++;
+    }
+
+    *pOffset = pos;
+    return result;
+}
+
+// pLine in, ppPart1 out, ppPart2 out, ppPart3 out
+// returns number of found parts
+int SlideShow::splitLine(char* pLine, char** ppPart1, char** ppPart2, char** ppPart3)
+{
+    int pos = 0;
+    int found = 0;
+
+    *ppPart1 = NULL;
+    *ppPart2 = NULL;
+    *ppPart3 = NULL;
+
+    if (*pLine != '\0')
+    {
+        *ppPart1 = &(pLine[0]);
+        found++;
+
+        while (pLine[pos] != '\0')
+        {
+            if (pLine[pos] == ' ')
+            {
+                // found the next token separator
+                pLine[pos++] = '\0';
+                found++;
+
+                // move past all token separator characters
+                while (pLine[pos] == ' ')
+                {
+                    pos++;
+                }
+
+                // start looking for end of next token
+                if (found == 2)
+                {
+                    *ppPart2 = &(pLine[pos]);
+                }
+                else if (found == 3)
+                {
+                    *ppPart3 = &(pLine[pos]);
+                }
+            }
+            pos++;
+        }
+    }
+
+    return found;
+}
+
+// returns index of pLabel or -1 if it doesn't exist
+int SlideShow::findLabel(LabelInfo* pLabels, int numLabels, const char* pLabel)
+{
+    int i;
+    for (i = 0; i < numLabels; i++)
+    {
+        if (strcmp(pLabels[i].pLabel, pLabel) == 0)
+        {
+            return pLabels[i].index;
+        }
+    }
+    return -1;
+}
+
+void SlideShow::freeSequence(void)
+{
+    if (allocatedSequenceItems > 0) {
+        for (int i = 0; i < usedSequenceItems; i++) {
+            delete Sequence[i];
+        }
+        free(Sequence);
+        Sequence = NULL;
+        allocatedSequenceItems = 0;
+        usedSequenceItems = 0;
+    }
+}
+
+SlideShow::SlideShowError SlideShow::expandSequence()
+{
+    int newSize = allocatedSequenceItems + 20;
+    Command** newPtr = (Command**)realloc(Sequence, newSize * sizeof(Command*));
+    if (newPtr != NULL) {
+        Sequence = newPtr;
+        allocatedSequenceItems = newSize;
+        return Ok;
+    } else {
+        return OutOfMemory;
+    }
+}
+
+SlideShow::SlideShowError SlideShow::parseScript(char* pBuf)
+{
+    char* pLine = NULL;
+    int offset = 0;
+    LabelInfo Labels[10] = {0};
+    int numLabels = 0;
+    LabelInfo UnresolvedGotos[10] = {0};
+    int numUnresolvedGotos = 0;
+    int i;
+    SlideShowError result;
+
+    // cleanup old sequences
+    freeSequence();
+
+    // prepare the new one
+    result = expandSequence();
+    if (result != Ok) {
+        return result;
+    }
+
+    // start parsing the new sequence
+    while (getNextLine(pBuf, &offset, &pLine) == 0)
+    {
+        if (*pLine == '#')
+        {
+            // found a comment line
+        }
+        else
+        {
+            char* pCommand;
+            char* pArg1;
+            char* pArg2;
+            int num = splitLine(pLine, &pCommand, &pArg1, &pArg2);
+
+            if ((num >= 1) && (num <= 3) && (strcmp(pCommand, "clear") == 0))
+            {
+                if (num == 1) {
+                    Sequence[usedSequenceItems] = new Command(Command::Clear, 0xff, new Transition("none"));
+                } else if (num == 2) {
+                    Sequence[usedSequenceItems] = new Command(Command::Clear, strtol(pArg1, NULL, 16), new Transition("none"));
+                } else {
+                    Transition* t = new Transition(pArg2);
+                    if (t->type() == Transition::Unknown) {
+                        printf("Found invalid transition '%s'. Aborting...\n", pArg2);
+                        result = InvalidScript;
+                        break;
+                    }
+                    Sequence[usedSequenceItems] = new Command(Command::Clear, strtol(pArg1, NULL, 16), t);
+                }
+            }
+            else if ((num == 3) && (strcmp(pCommand, "show") == 0))
+            {
+                Transition* t = new Transition(pArg2);
+                if (t->type() == Transition::Unknown) {
+                    printf("Found invalid transition '%s'. Aborting...\n", pArg2);
+                    result = InvalidScript;
+                    break;
+                }
+                Sequence[usedSequenceItems] = new Command(Command::Show, atoi(pArg1), t);
+            }
+            else if ((num == 2) && (strcmp(pCommand, "wait") == 0))
+            {
+                Sequence[usedSequenceItems] = new Command(Command::Wait, atoi(pArg1));
+            }
+            else if ((num == 2) && (strcmp(pCommand, "callout") == 0))
+            {
+                Sequence[usedSequenceItems] = new Command(Command::Callout, atoi(pArg1));
+            }
+            else if ((num == 2) && (strcmp(pCommand, "label") == 0))
+            {
+                int index = findLabel(&(Labels[0]), numLabels, pArg1);
+                if (index == -1)
+                {
+                    // found a new label
+                    Labels[numLabels].index = usedSequenceItems;
+                    Labels[numLabels].pLabel = pArg1;
+                    numLabels++;
+
+                    // A label doesn't occupy a slot in the sequence
+                    usedSequenceItems--;
+                }
+                else
+                {
+                    // label already declared
+                    printf("Found a second declaration of label '%s'. Aborting...\n", pArg1);
+                    result = InvalidScript;
+                    break;
+                }
+            }
+            else if ((num == 2) && (strcmp(pCommand, "goto") == 0))
+            {
+                int index = findLabel(&(Labels[0]), numLabels, pArg1);
+                if (index == -1)
+                {
+                    // couldn't find the label we are looking for so we
+                    // wait for now
+                    UnresolvedGotos[numUnresolvedGotos].index = usedSequenceItems;
+                    UnresolvedGotos[numUnresolvedGotos].pLabel = pArg1;
+                    numUnresolvedGotos++;
+                }
+
+                // Create the command
+                Sequence[usedSequenceItems] = new Command(Command::Goto, index);
+            }
+            else if ((num == 3) && (strcmp(pCommand, "load") == 0))
+            {
+                Sequence[usedSequenceItems] = new Command(Command::LoadImage, atoi(pArg2), NULL, pArg1, pathPrefix);
+                if (Sequence[usedSequenceItems]->info() >= MaxNumPreparedImages) {
+                    printf("Attempting to load into invalid slot %d, have 0..%d. Aborting...\n", Sequence[usedSequenceItems]->info(), MaxNumPreparedImages);
+                    result = InvalidScript;
+                    break;
+                }
+            }
+            else
+            {
+                // unknown command
+                printf("Found unknown command '%s'. Aborting...\n", pCommand);
+                result = InvalidScript;
+                break;
+            }
+
+            // start looking for next part in the sequence
+            usedSequenceItems++;
+
+            // assure we don't pass memory limit
+            if (usedSequenceItems >= allocatedSequenceItems)
+            {
+                result = expandSequence();
+                if (result != Ok) {
+                    printf("Failed to allocate memory to hold sequence. Aborting...\n");
+                    break;
+                }
+            }
+        }
+    }
+
+    // Resolve any unresolved gotos. Happens when the label is on
+    // a line with a higher line number than the goto statement.
+    for (i = 0; i < numUnresolvedGotos && result==Ok; i++)
+    {
+        int index = findLabel(&(Labels[0]), numLabels, UnresolvedGotos[i].pLabel);
+        if (index == -1)
+        {
+            printf("Unable to find label '%s' used in goto statement. Aborting...\n", UnresolvedGotos[i].pLabel);
+            result = InvalidScript;
+        }
+        else
+        {
+            // Update the goto element with the correct index of the label
+            Sequence[UnresolvedGotos[i].index]->updateInfo(index);
+        }
+    }
+
+    if (result==Ok && usedSequenceItems == 0)
+    {
+        printf("Found no sequence. Aborting...\n");
+        result = InvalidScript;
+    }
+    return result;
+}
+
+SlideShow::SlideShowError SlideShow::loadImage(const char* pFileName, int slot)
+{
+    SlideShowError result = Ok;
+
+    if (PreparedImages[slot].pointerToFree != NULL) {
+        free(PreparedImages[slot].pointerToFree);
+        PreparedImages[slot].pointerToFree = NULL;
+    }
+
+    if (Image::decode(pFileName, Image::RES_16BIT, &(PreparedImages[slot]), fileMutex) != 0) {
+        printf("Failed to decode file %s as image\n", pFileName);
+        result = FileError;
+    }
+
+    return result;
+}
+void SlideShow::delay(int lastTime, int millis)
+{
+    int timeToWait = (lastTime + millis) - msTicks;
+    if (timeToWait > 0) {
+        wait_ms(timeToWait);
+    }
+}
+
+SlideShow::SlideShowError SlideShow::runScript()
+{
+    SlideShowError result = Ok;
+    int seqIndex = 0;
+    int lastTime = 0;
+
+    while ((result == Ok) && (seqIndex < this->usedSequenceItems))
+    {
+        result = Sequence[seqIndex]->handle(this, &seqIndex, &lastTime);
+
+        if (abortBeforeNextStep) {
+            break;
+        }
+    }
+
+//    if (*pAbort)
+//    {
+//    return SLIDE_USER_ABORT;
+//    }
+//    else
+//    {
+//    return SLIDE_SCRIPT_END;
+//    }
+    return Ok;
+}
+
+
+/******************************************************************************
+ * Public Functions
+ *****************************************************************************/
+
+SlideShow::SlideShow(Renderer* r, /*LcdController::Config* screen,*/ const char* pathPrefix, uint8_t* bkg, int xoff, int yoff, int layer, Mutex* fileMutex)
+{
+    Display* disp = DMBoard::instance().display();
+    this->screenWidth = disp->width();
+    this->screenHeight = disp->height();
+    this->screenPixels = this->screenWidth * this->screenHeight;
+    this->drawXoff = xoff;
+    this->drawYoff = yoff;
+
+    // Assume screen->bpp == Bpp_16
+    this->screenBytes = 2 * this->screenPixels;
+
+    this->ImageBackBuffer = NULL;
+    this->Sequence = NULL;
+    this->allocatedSequenceItems = 0;
+    this->usedSequenceItems = 0;
+
+    this->pathPrefix = pathPrefix;
+
+    this->CurrentSlot = NO_SLOT;
+
+    memset(PreparedImages, 0, MaxNumPreparedImages * sizeof(Image::ImageData_t));
+
+    this->fileMutex = fileMutex;
+
+    this->rend = r;
+    this->rendHnd = 0;
+    this->layer = layer;
+
+    this->callout = NULL;
+
+    this->abortBeforeNextStep = false;
+}
+
+SlideShow::~SlideShow()
+{
+    if (ImageBackBuffer != NULL) {
+        free(ImageBackBuffer);
+        ImageBackBuffer = NULL;
+    }
+    for (int i = 0; i < MaxNumPreparedImages; i++) {
+        if (PreparedImages[i].pointerToFree != NULL) {
+            free(PreparedImages[i].pointerToFree);
+        }
+    }
+
+    freeSequence();
+
+    if ((rendHnd != 0) && (rend != NULL)) {
+      rend->unregisterUser(rendHnd);
+    }
+
+    //memset(PreparedImages, 0, MaxNumPreparedImages * sizeof(Image::ImageData_t));
+}
+
+SlideShow::SlideShowError SlideShow::prepare(const char* scriptFile)
+{
+    uint8_t* pBuf = NULL;
+    uint32_t size = 0;
+    SlideShowError result = InvalidScript;
+
+    do
+    {
+        if (ImageBackBuffer == NULL) {
+            // Back buffer will be able to hold three images
+            ImageBackBuffer = (image_t)malloc(screenBytes * 3);
+            if (ImageBackBuffer == NULL) {
+                result = OutOfMemory;
+                break;
+            }
+        }
+
+        // Read the contents of the file into a newly allocated buffer
+        result = loadFile(scriptFile, &pBuf, &size);
+        if (result != Ok)
+        {
+            break;
+        }
+
+        //printf("Parsing buffer...\n");
+
+        // Parse buffer to create the script sequence
+        result = parseScript((char*)pBuf);
+
+    } while (0);
+
+    // Release resources
+    if (pBuf != NULL)
+    {
+        free(pBuf);
+        pBuf = NULL;
+    }
+    if (result != Ok) {
+        freeSequence();
+    }
+
+    return result;
+}
+
+SlideShow::SlideShowError SlideShow::run()
+{
+  //printf("Executing script...\n");
+  this->abortBeforeNextStep = false;
+  return runScript();
+}
+
+void SlideShow::setCalloutHandler(calloutFunc func, int calloutId)
+{
+  this->callout = func;
+  this->calloutId = calloutId;
+}
+
+void SlideShow::releaseScreen(void)
+{
+  if ((rendHnd != 0) && (rend != NULL)) {
+    rend->unregisterUser(rendHnd);
+  }
+  rendHnd = 0;
+}