Basic swim GUI for LPC4088
Fork of DMBasicGUI by
SlideShow/SlideShow.cpp
- Committer:
- embeddedartists
- Date:
- 2014-12-21
- Revision:
- 5:f4de114c31c3
- Child:
- 10:651861441108
File content as of revision 5:f4de114c31c3:
#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; }