diff --git a/src/GifPlayer.h b/lib/MatrixUI/GifPlayer.h similarity index 90% rename from src/GifPlayer.h rename to lib/MatrixUI/GifPlayer.h index 03ff3cb..d8472cb 100644 --- a/src/GifPlayer.h +++ b/lib/MatrixUI/GifPlayer.h @@ -1,657 +1,661 @@ -#ifndef GifPlayer_H -#define GifPlayer_H - -class GifPlayer -{ -public: -#define ERROR_NONE 0 -#define ERROR_FILEOPEN 1 -#define ERROR_FILENOTGIF 2 -#define ERROR_BADGIFFORMAT 3 -#define ERROR_UNKNOWNCONTROLEXT 4 -#define ERROR_FINISHED 5 -#define WIDTH 32 -#define HEIGHT 8 -private: - bool needNewFrame; - long lastFrameTime; - bool firstFrameDone; - int newframeDelay; - int lastFrame[WIDTH * HEIGHT]; - bool lastFrameDrawn = false; - unsigned long nextFrameTime = 0; -#define GIFHDRTAGNORM "GIF87a" -#define GIFHDRTAGNORM1 "GIF89a" -#define GIFHDRSIZE 6 - FastLED_NeoMatrix *mtx; -#define COLORTBLFLAG 0x80 -#define INTERLACEFLAG 0x40 -#define TRANSPARENTFLAG 0x01 -#define NO_TRANSPARENT_INDEX -1 -#define DISPOSAL_NONE 0 -#define DISPOSAL_LEAVE 1 -#define DISPOSAL_BACKGROUND 2 -#define DISPOSAL_RESTORE 3 - - typedef struct - { - byte Red; - byte Green; - byte Blue; - } _RGB; - int lsdWidth; - int lsdHeight; - int lsdPackedField; - int lsdAspectRatio; - int lsdBackgroundIndex; - int offsetX; - int offsetY; - int tbiImageX; - int tbiImageY; - int tbiWidth; - int tbiHeight; - int tbiPackedBits; - boolean tbiInterlaced; - -public: - int frameDelay; - int transparentColorIndex; - int prevBackgroundIndex; - int prevDisposalMethod; - int disposalMethod; - int lzwCodeSize; - boolean keyFrame; - int rectX; - int rectY; - int rectWidth; - int rectHeight; - int colorCount; - _RGB gifPalette[256]; - byte lzwImageData[1280]; - char tempBuffer[260]; - File file; - byte imageData[WIDTH * HEIGHT]; - byte imageDataBU[WIDTH * HEIGHT]; - - void backUpStream(int n) - { - file.seek(file.position() - n, SeekSet); - } - - int readByte() - { - int b = file.read(); - return b; - } - - int readWord() - { - int b0 = readByte(); - int b1 = readByte(); - return (b1 << 8) | b0; - } - - int readIntoBuffer(void *buffer, int numberOfBytes) - { - int result = file.read((uint8_t *)buffer, numberOfBytes); - return result; - } - - void fillImageDataRect(byte colorIndex, int x, int y, int width, int height) - { - int yOffset; - for (int yy = y; yy < height + y; yy++) - { - yOffset = yy * WIDTH; - for (int xx = x; xx < width + x; xx++) - { - imageData[yOffset + xx] = colorIndex; - } - } - } - - void fillImageData(byte colorIndex) - { - memset(imageData, colorIndex, sizeof(imageData)); - } - - void copyImageDataRect(byte *src, byte *dst, int x, int y, int width, int height) - { - - int yOffset, offset; - - for (int yy = y; yy < height + y; yy++) - { - yOffset = yy * WIDTH; - for (int xx = x; xx < width + x; xx++) - { - offset = yOffset + xx; - dst[offset] = src[offset]; - } - } - } - - void parsePlainTextExtension() - { - byte len = readByte(); - readIntoBuffer(tempBuffer, len); - len = readByte(); - while (len != 0) - { - readIntoBuffer(tempBuffer, len); - len = readByte(); - } - } - - void parseGraphicControlExtension() - { - readByte(); - int packedBits = readByte(); - frameDelay = readWord(); - transparentColorIndex = readByte(); - - if ((packedBits & TRANSPARENTFLAG) == 0) - { - // Indicate no transparent index - transparentColorIndex = NO_TRANSPARENT_INDEX; - } - disposalMethod = (packedBits >> 2) & 7; - if (disposalMethod > 3) - { - disposalMethod = 0; - } - - readByte(); // Toss block end - } - - void parseApplicationExtension() - { - memset(tempBuffer, 0, sizeof(tempBuffer)); - byte len = readByte(); - readIntoBuffer(tempBuffer, len); - len = readByte(); - while (len != 0) - { - readIntoBuffer(tempBuffer, len); - len = readByte(); - } - } - - void parseCommentExtension() - { - byte len = readByte(); - while (len != 0) - { - memset(tempBuffer, 0, sizeof(tempBuffer)); - readIntoBuffer(tempBuffer, len); - len = readByte(); - } - } - - int parseGIFFileTerminator() - { - byte b = readByte(); - if (b != 0x3B) - { - return ERROR_BADGIFFORMAT; - } - else - { - return ERROR_NONE; - } - } - - unsigned long parseTableBasedImage() - { - tbiImageX = readWord(); - tbiImageY = readWord(); - tbiWidth = readWord(); - tbiHeight = readWord(); - tbiPackedBits = readByte(); - tbiInterlaced = ((tbiPackedBits & INTERLACEFLAG) != 0); - boolean localColorTable = ((tbiPackedBits & COLORTBLFLAG) != 0); - if (localColorTable) - { - int colorBits = ((tbiPackedBits & 7) + 1); - colorCount = 1 << colorBits; - int colorTableBytes = sizeof(_RGB) * colorCount; - readIntoBuffer(gifPalette, colorTableBytes); - } - - if (keyFrame) - { - if (transparentColorIndex == NO_TRANSPARENT_INDEX) - { - fillImageData(lsdBackgroundIndex); - } - else - { - fillImageData(transparentColorIndex); - } - keyFrame = false; - - rectX = 0; - rectY = 0; - rectWidth = WIDTH; - rectHeight = HEIGHT; - } - - if ((prevDisposalMethod != DISPOSAL_NONE) && (prevDisposalMethod != DISPOSAL_LEAVE)) - { - // mtx->clear(); - } - - if (prevDisposalMethod == DISPOSAL_BACKGROUND) - { - fillImageDataRect(prevBackgroundIndex, rectX, rectY, rectWidth, rectHeight); - } - else if (prevDisposalMethod == DISPOSAL_RESTORE) - { - copyImageDataRect(imageDataBU, imageData, rectX, rectY, rectWidth, rectHeight); - } - prevDisposalMethod = disposalMethod; - if (disposalMethod != DISPOSAL_NONE) - { - rectX = tbiImageX; - rectY = tbiImageY; - rectWidth = tbiWidth; - rectHeight = tbiHeight; - if (disposalMethod == DISPOSAL_BACKGROUND) - { - if (transparentColorIndex != NO_TRANSPARENT_INDEX) - { - prevBackgroundIndex = transparentColorIndex; - } - else - { - prevBackgroundIndex = lsdBackgroundIndex; - } - } - else if (disposalMethod == DISPOSAL_RESTORE) - { - copyImageDataRect(imageData, imageDataBU, rectX, rectY, rectWidth, rectHeight); - } - } - lzwCodeSize = readByte(); - int offset = 0; - int dataBlockSize = readByte(); - while (dataBlockSize != 0) - { - backUpStream(1); - dataBlockSize++; - if (offset + dataBlockSize <= (int)sizeof(lzwImageData)) - { - readIntoBuffer(lzwImageData + offset, dataBlockSize); - } - else - { - int i; - for (i = 0; i < dataBlockSize; i++) - file.read(); - } - - offset += dataBlockSize; - dataBlockSize = readByte(); - } - lzw_decode_init(lzwCodeSize, lzwImageData); - decompressAndDisplayFrame(); - transparentColorIndex = NO_TRANSPARENT_INDEX; - disposalMethod = DISPOSAL_NONE; - if (frameDelay < 1) - { - frameDelay = 1; - } - newframeDelay = frameDelay * 10; - return frameDelay * 10; - } - -#define LZW_MAXBITS 10 -#define LZW_SIZTABLE (1 << LZW_MAXBITS) - unsigned int mask[17] = { - 0x0000, 0x0001, 0x0003, 0x0007, - 0x000F, 0x001F, 0x003F, 0x007F, - 0x00FF, 0x01FF, 0x03FF, 0x07FF, - 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, - 0xFFFF}; - - byte *pbuf; - int bbits; - int bbuf; - int cursize; // The current code size - int curmask; - int codesize; - int clear_code; - int end_code; - int newcodes; // First available code - int top_slot; // Highest code for current size - int extra_slot; - int slot; // Last read code - int fc, oc; - int bs; // Current buffer size for GIF - byte *sp; - byte stack[LZW_SIZTABLE]; - byte suffix[LZW_SIZTABLE]; - unsigned int prefix[LZW_SIZTABLE]; - - void lzw_decode_init(int csize, byte *buf) - { - pbuf = buf; - bbuf = 0; - bbits = 0; - bs = 0; - codesize = csize; - cursize = codesize + 1; - curmask = mask[cursize]; - top_slot = 1 << cursize; - clear_code = 1 << codesize; - end_code = clear_code + 1; - slot = newcodes = clear_code + 2; - oc = fc = -1; - sp = stack; - } - - int lzw_get_code() - { - while (bbits < cursize) - { - if (!bs) - { - bs = *pbuf++; - } - bbuf |= (*pbuf++) << bbits; - bbits += 8; - bs--; - } - int c = bbuf; - bbuf >>= cursize; - bbits -= cursize; - return c & curmask; - } - - int lzw_decode(byte *buf, int len) - { - int l, c, code; - - if (end_code < 0) - { - return 0; - } - l = len; - - for (;;) - { - while (sp > stack) - { - *buf++ = *(--sp); - if ((--l) == 0) - { - goto the_end; - } - } - c = lzw_get_code(); - if (c == end_code) - { - break; - } - else if (c == clear_code) - { - cursize = codesize + 1; - curmask = mask[cursize]; - slot = newcodes; - top_slot = 1 << cursize; - fc = oc = -1; - } - else - { - - code = c; - if ((code == slot) && (fc >= 0)) - { - *sp++ = fc; - code = oc; - } - else if (code >= slot) - { - break; - } - while (code >= newcodes) - { - *sp++ = suffix[code]; - code = prefix[code]; - } - *sp++ = code; - if ((slot < top_slot) && (oc >= 0)) - { - suffix[slot] = code; - prefix[slot++] = oc; - } - fc = code; - oc = c; - if (slot >= top_slot) - { - if (cursize < LZW_MAXBITS) - { - top_slot <<= 1; - curmask = mask[++cursize]; - } - else - { - } - } - } - } - end_code = -1; - the_end: - return len - l; - } - - void redrawLastFrame() - { - if (needNewFrame) - return; - CRGB color; - int yOffset, pixel; - for (int y = tbiImageY; y < tbiHeight + tbiImageY; y++) - { - yOffset = y * WIDTH; - for (int x = tbiImageX; x < tbiWidth + tbiImageX; x++) - { - pixel = lastFrame[yOffset + x]; - if (pixel == -99) - { - mtx->drawPixel(x + offsetX, y + offsetY, mtx->Color(0, 0, 0)); - continue; - } - color.red = gifPalette[pixel].Red; - color.green = gifPalette[pixel].Green; - color.blue = gifPalette[pixel].Blue; - mtx->drawPixel(x + offsetX, y + offsetY, color); - } - } - } - - void decompressAndDisplayFrame() - { - CRGB color; - if (tbiInterlaced) - { - for (int line = tbiImageY + 0; line < tbiHeight + tbiImageY; line += 8) - { - lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth); - } - for (int line = tbiImageY + 4; line < tbiHeight + tbiImageY; line += 8) - { - lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth); - } - for (int line = tbiImageY + 2; line < tbiHeight + tbiImageY; line += 4) - { - lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth); - } - for (int line = tbiImageY + 1; line < tbiHeight + tbiImageY; line += 2) - { - lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth); - } - } - else - { - for (int line = tbiImageY; line < tbiHeight + tbiImageY; line++) - { - lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth); - } - } - - // Ersetze alle transparenten Pixel durch schwarze Pixel - for (int i = 0; i < tbiWidth * tbiHeight; i++) - { - if (imageData[i] == transparentColorIndex) - { - imageData[i] = -99; // Indexwert für Schwarz - } - } - - int pixel, yOffset; - for (int y = tbiImageY; y < tbiHeight + tbiImageY; y++) - { - yOffset = y * WIDTH; - for (int x = tbiImageX; x < tbiWidth + tbiImageX; x++) - { - pixel = imageData[yOffset + x]; - if (pixel == transparentColorIndex) // Check if the pixel index is the transparent index - { - mtx->drawPixel(x + offsetX, y + offsetY, mtx->Color(0, 0, 0)); // Draw a black pixel - lastFrame[yOffset + x] = -99; // Save it as a special value - } - else - { - color.red = gifPalette[pixel].Red; - color.green = gifPalette[pixel].Green; - color.blue = gifPalette[pixel].Blue; - mtx->drawPixel(x + offsetX, y + offsetY, color); - lastFrame[yOffset + x] = pixel; - } - } - } - needNewFrame = false; - lastFrameTime = millis(); - } - -public: - void setMatrix(FastLED_NeoMatrix *matrix) - { - mtx = matrix; - } - void setFile(File imageFile) - { - if (imageFile.name() == file.name()) - return; - - needNewFrame = true; - file = imageFile; - memset(lastFrame, 0, sizeof(lastFrame)); - memset(gifPalette, 0, sizeof(gifPalette)); - memset(lzwImageData, 0, sizeof(lzwImageData)); - memset(imageData, 0, sizeof(imageData)); - memset(imageDataBU, 0, sizeof(imageDataBU)); - memset(stack, 0, sizeof(stack)); - memset(suffix, 0, sizeof(suffix)); - memset(prefix, 0, sizeof(prefix)); - parseGifHeader(); - parseLogicalScreenDescriptor(); - parseGlobalColorTable(); - drawFrame(offsetX, offsetY); - } - - boolean parseGifHeader() - { - char buffer[10]; - readIntoBuffer(buffer, GIFHDRSIZE); - if ((strncmp(buffer, GIFHDRTAGNORM, GIFHDRSIZE) != 0) && - (strncmp(buffer, GIFHDRTAGNORM1, GIFHDRSIZE) != 0)) - { - return false; - } - else - { - return true; - } - } - - void parseLogicalScreenDescriptor() - { - lsdWidth = readWord(); - lsdHeight = readWord(); - lsdPackedField = readByte(); - lsdBackgroundIndex = readByte(); - lsdAspectRatio = readByte(); - } - - void parseGlobalColorTable() - { - if (lsdPackedField & COLORTBLFLAG) - { - colorCount = 1 << ((lsdPackedField & 7) + 1); - int colorTableBytes = sizeof(_RGB) * colorCount; - readIntoBuffer(gifPalette, colorTableBytes); - } - } - - unsigned long drawFrame(int x, int y) - { - offsetX = x; - offsetY = y; - if (!file) - return 0; - - if (millis() - lastFrameTime < newframeDelay) - { - redrawLastFrame(); - return 0; - } - - lastFrameDrawn = false; - - boolean done = false; - while (!done) - { - byte b = readByte(); - if (b == 0x2c) - { - parseTableBasedImage(); - return 0; - } - else if (b == 0x21) - { - b = readByte(); - switch (b) - { - case 0x01: - parsePlainTextExtension(); - break; - case 0xf9: - parseGraphicControlExtension(); - break; - case 0xfe: - parseCommentExtension(); - break; - case 0xff: - parseApplicationExtension(); - break; - default: - return ERROR_UNKNOWNCONTROLEXT; - } - } - else - { - done = true; - file.seek(0); - parseGifHeader(); - parseLogicalScreenDescriptor(); - parseGlobalColorTable(); - drawFrame(offsetX, offsetY); - return ERROR_FINISHED; - } - } - return ERROR_NONE; - } -}; +#ifndef GifPlayer_H +#define GifPlayer_H +#include +class GifPlayer +{ +public: +#define ERROR_NONE 0 +#define ERROR_FILEOPEN 1 +#define ERROR_FILENOTGIF 2 +#define ERROR_BADGIFFORMAT 3 +#define ERROR_UNKNOWNCONTROLEXT 4 +#define ERROR_FINISHED 5 +#define WIDTH 32 +#define HEIGHT 8 +private: + bool needNewFrame; + long lastFrameTime; + bool firstFrameDone; + int newframeDelay; + int lastFrame[WIDTH * HEIGHT]; + bool lastFrameDrawn = false; + unsigned long nextFrameTime = 0; +#define GIFHDRTAGNORM "GIF87a" +#define GIFHDRTAGNORM1 "GIF89a" +#define GIFHDRSIZE 6 + FastLED_NeoMatrix *mtx; +#define COLORTBLFLAG 0x80 +#define INTERLACEFLAG 0x40 +#define TRANSPARENTFLAG 0x01 +#define NO_TRANSPARENT_INDEX -1 +#define DISPOSAL_NONE 0 +#define DISPOSAL_LEAVE 1 +#define DISPOSAL_BACKGROUND 2 +#define DISPOSAL_RESTORE 3 + + typedef struct + { + byte Red; + byte Green; + byte Blue; + } _RGB; + int lsdWidth; + int lsdHeight; + int lsdPackedField; + int lsdAspectRatio; + int lsdBackgroundIndex; + int offsetX; + int offsetY; + int tbiImageX; + int tbiImageY; + int tbiWidth; + int tbiHeight; + int tbiPackedBits; + boolean tbiInterlaced; + +public: + int frameDelay; + int transparentColorIndex; + int prevBackgroundIndex; + int prevDisposalMethod; + int disposalMethod; + int lzwCodeSize; + boolean keyFrame; + int rectX; + int rectY; + int rectWidth; + int rectHeight; + int colorCount; + _RGB gifPalette[256]; + byte lzwImageData[1280]; + char tempBuffer[260]; + File file; + byte imageData[WIDTH * HEIGHT]; + byte imageDataBU[WIDTH * HEIGHT]; + + void backUpStream(int n) + { + file.seek(file.position() - n, SeekSet); + } + + int readByte() + { + int b = file.read(); + return b; + } + + int readWord() + { + int b0 = readByte(); + int b1 = readByte(); + return (b1 << 8) | b0; + } + +int readIntoBuffer(void *buffer, int numberOfBytes) +{ + int result = file.read(static_cast(buffer), numberOfBytes); + return result; +} + + void fillImageDataRect(byte colorIndex, int x, int y, int width, int height) + { + int yOffset; + for (int yy = y; yy < height + y; yy++) + { + yOffset = yy * WIDTH; + for (int xx = x; xx < width + x; xx++) + { + imageData[yOffset + xx] = colorIndex; + } + } + } + + void fillImageData(byte colorIndex) + { + memset(imageData, colorIndex, sizeof(imageData)); + } + + void copyImageDataRect(byte *src, byte *dst, int x, int y, int width, int height) + { + + int yOffset, offset; + + for (int yy = y; yy < height + y; yy++) + { + yOffset = yy * WIDTH; + for (int xx = x; xx < width + x; xx++) + { + offset = yOffset + xx; + dst[offset] = src[offset]; + } + } + } + + void parsePlainTextExtension() + { + byte len = readByte(); + readIntoBuffer(tempBuffer, len); + len = readByte(); + while (len != 0) + { + readIntoBuffer(tempBuffer, len); + len = readByte(); + } + } + + void parseGraphicControlExtension() + { + readByte(); + int packedBits = readByte(); + frameDelay = readWord(); + transparentColorIndex = readByte(); + + if ((packedBits & TRANSPARENTFLAG) == 0) + { + // Indicate no transparent index + transparentColorIndex = NO_TRANSPARENT_INDEX; + } + disposalMethod = (packedBits >> 2) & 7; + if (disposalMethod > 3) + { + disposalMethod = 0; + } + + readByte(); // Toss block end + } + + void parseApplicationExtension() + { + memset(tempBuffer, 0, sizeof(tempBuffer)); + byte len = readByte(); + readIntoBuffer(tempBuffer, len); + len = readByte(); + while (len != 0) + { + readIntoBuffer(tempBuffer, len); + len = readByte(); + } + } + + void parseCommentExtension() + { + byte len = readByte(); + while (len != 0) + { + memset(tempBuffer, 0, sizeof(tempBuffer)); + readIntoBuffer(tempBuffer, len); + len = readByte(); + } + } + + int parseGIFFileTerminator() + { + byte b = readByte(); + if (b != 0x3B) + { + return ERROR_BADGIFFORMAT; + } + else + { + return ERROR_NONE; + } + } + + unsigned long parseTableBasedImage() + { + tbiImageX = readWord(); + tbiImageY = readWord(); + tbiWidth = readWord(); + tbiHeight = readWord(); + tbiPackedBits = readByte(); + tbiInterlaced = ((tbiPackedBits & INTERLACEFLAG) != 0); + boolean localColorTable = ((tbiPackedBits & COLORTBLFLAG) != 0); + if (localColorTable) + { + int colorBits = ((tbiPackedBits & 7) + 1); + colorCount = 1 << colorBits; + int colorTableBytes = sizeof(_RGB) * colorCount; + readIntoBuffer(gifPalette, colorTableBytes); + } + + if (keyFrame) + { + if (transparentColorIndex == NO_TRANSPARENT_INDEX) + { + fillImageData(lsdBackgroundIndex); + } + else + { + fillImageData(transparentColorIndex); + } + keyFrame = false; + + rectX = 0; + rectY = 0; + rectWidth = WIDTH; + rectHeight = HEIGHT; + } + + if ((prevDisposalMethod != DISPOSAL_NONE) && (prevDisposalMethod != DISPOSAL_LEAVE)) + { + // mtx->clear(); + } + + if (prevDisposalMethod == DISPOSAL_BACKGROUND) + { + fillImageDataRect(prevBackgroundIndex, rectX, rectY, rectWidth, rectHeight); + } + else if (prevDisposalMethod == DISPOSAL_RESTORE) + { + copyImageDataRect(imageDataBU, imageData, rectX, rectY, rectWidth, rectHeight); + } + prevDisposalMethod = disposalMethod; + if (disposalMethod != DISPOSAL_NONE) + { + rectX = tbiImageX; + rectY = tbiImageY; + rectWidth = tbiWidth; + rectHeight = tbiHeight; + if (disposalMethod == DISPOSAL_BACKGROUND) + { + if (transparentColorIndex != NO_TRANSPARENT_INDEX) + { + prevBackgroundIndex = transparentColorIndex; + } + else + { + prevBackgroundIndex = lsdBackgroundIndex; + } + } + else if (disposalMethod == DISPOSAL_RESTORE) + { + copyImageDataRect(imageData, imageDataBU, rectX, rectY, rectWidth, rectHeight); + } + } + lzwCodeSize = readByte(); + int offset = 0; + int dataBlockSize = readByte(); + while (dataBlockSize != 0) + { + backUpStream(1); + dataBlockSize++; + if (offset + dataBlockSize <= (int)sizeof(lzwImageData)) + { + readIntoBuffer(lzwImageData + offset, dataBlockSize); + } + else + { + int i; + for (i = 0; i < dataBlockSize; i++) + file.read(); + } + + offset += dataBlockSize; + dataBlockSize = readByte(); + } + lzw_decode_init(lzwCodeSize, lzwImageData); + decompressAndDisplayFrame(); + transparentColorIndex = NO_TRANSPARENT_INDEX; + disposalMethod = DISPOSAL_NONE; + if (frameDelay < 1) + { + frameDelay = 1; + } + newframeDelay = frameDelay * 10; + return frameDelay * 10; + } + +#define LZW_MAXBITS 10 +#define LZW_SIZTABLE (1 << LZW_MAXBITS) + unsigned int mask[17] = { + 0x0000, 0x0001, 0x0003, 0x0007, + 0x000F, 0x001F, 0x003F, 0x007F, + 0x00FF, 0x01FF, 0x03FF, 0x07FF, + 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, + 0xFFFF}; + + byte *pbuf; + int bbits; + int bbuf; + int cursize; // The current code size + int curmask; + int codesize; + int clear_code; + int end_code; + int newcodes; // First available code + int top_slot; // Highest code for current size + int extra_slot; + int slot; // Last read code + int fc, oc; + int bs; // Current buffer size for GIF + byte *sp; + byte stack[LZW_SIZTABLE]; + byte suffix[LZW_SIZTABLE]; + unsigned int prefix[LZW_SIZTABLE]; + + void lzw_decode_init(int csize, byte *buf) + { + pbuf = buf; + bbuf = 0; + bbits = 0; + bs = 0; + codesize = csize; + cursize = codesize + 1; + curmask = mask[cursize]; + top_slot = 1 << cursize; + clear_code = 1 << codesize; + end_code = clear_code + 1; + slot = newcodes = clear_code + 2; + oc = fc = -1; + sp = stack; + } + + int lzw_get_code() + { + while (bbits < cursize) + { + if (!bs) + { + bs = *pbuf++; + } + bbuf |= (*pbuf++) << bbits; + bbits += 8; + bs--; + } + int c = bbuf; + bbuf >>= cursize; + bbits -= cursize; + return c & curmask; + } + + int lzw_decode(byte *buf, int len) + { + int l, c, code; + + if (end_code < 0) + { + return 0; + } + l = len; + + for (;;) + { + while (sp > stack) + { + *buf++ = *(--sp); + if ((--l) == 0) + { + goto the_end; + } + } + c = lzw_get_code(); + if (c == end_code) + { + break; + } + else if (c == clear_code) + { + cursize = codesize + 1; + curmask = mask[cursize]; + slot = newcodes; + top_slot = 1 << cursize; + fc = oc = -1; + } + else + { + + code = c; + if ((code == slot) && (fc >= 0)) + { + *sp++ = fc; + code = oc; + } + else if (code >= slot) + { + break; + } + while (code >= newcodes) + { + *sp++ = suffix[code]; + code = prefix[code]; + } + *sp++ = code; + if ((slot < top_slot) && (oc >= 0)) + { + suffix[slot] = code; + prefix[slot++] = oc; + } + fc = code; + oc = c; + if (slot >= top_slot) + { + if (cursize < LZW_MAXBITS) + { + top_slot <<= 1; + curmask = mask[++cursize]; + } + else + { + } + } + } + } + end_code = -1; + the_end: + return len - l; + } + + void redrawLastFrame() + { + if (needNewFrame) + return; + CRGB color; + int yOffset, pixel; + for (int y = tbiImageY; y < tbiHeight + tbiImageY; y++) + { + yOffset = y * WIDTH; + for (int x = tbiImageX; x < tbiWidth + tbiImageX; x++) + { + pixel = lastFrame[yOffset + x]; + if (pixel == -99) + { + mtx->drawPixel(x + offsetX, y + offsetY, mtx->Color(0, 0, 0)); + continue; + } + color.red = gifPalette[pixel].Red; + color.green = gifPalette[pixel].Green; + color.blue = gifPalette[pixel].Blue; + mtx->drawPixel(x + offsetX, y + offsetY, color); + } + } + } + + void decompressAndDisplayFrame() + { + CRGB color; + if (tbiInterlaced) + { + for (int line = tbiImageY + 0; line < tbiHeight + tbiImageY; line += 8) + { + lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth); + } + for (int line = tbiImageY + 4; line < tbiHeight + tbiImageY; line += 8) + { + lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth); + } + for (int line = tbiImageY + 2; line < tbiHeight + tbiImageY; line += 4) + { + lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth); + } + for (int line = tbiImageY + 1; line < tbiHeight + tbiImageY; line += 2) + { + lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth); + } + } + else + { + for (int line = tbiImageY; line < tbiHeight + tbiImageY; line++) + { + lzw_decode(imageData + (line * WIDTH) + tbiImageX, tbiWidth); + } + } + + // Ersetze alle transparenten Pixel durch schwarze Pixel + for (int i = 0; i < tbiWidth * tbiHeight; i++) + { + if (imageData[i] == transparentColorIndex) + { + imageData[i] = -99; // Indexwert für Schwarz + } + } + + int pixel, yOffset; + for (int y = tbiImageY; y < tbiHeight + tbiImageY; y++) + { + yOffset = y * WIDTH; + for (int x = tbiImageX; x < tbiWidth + tbiImageX; x++) + { + pixel = imageData[yOffset + x]; + if (pixel == transparentColorIndex) // Check if the pixel index is the transparent index + { + mtx->drawPixel(x + offsetX, y + offsetY, mtx->Color(0, 0, 0)); // Draw a black pixel + lastFrame[yOffset + x] = -99; // Save it as a special value + } + else + { + color.red = gifPalette[pixel].Red; + color.green = gifPalette[pixel].Green; + color.blue = gifPalette[pixel].Blue; + mtx->drawPixel(x + offsetX, y + offsetY, color); + lastFrame[yOffset + x] = pixel; + } + } + } + needNewFrame = false; + lastFrameTime = millis(); + } + +public: + void setMatrix(FastLED_NeoMatrix *matrix) + { + mtx = matrix; + } + + void playGif(int x, int y, File *imageFile) + { + offsetX = x; + offsetY = y; + + if (imageFile->name() == file.name()) + { + drawFrame(); + return; + } + else + { + needNewFrame = true; + file = *imageFile; + memset(lastFrame, 0, sizeof(lastFrame)); + memset(gifPalette, 0, sizeof(gifPalette)); + memset(lzwImageData, 0, sizeof(lzwImageData)); + memset(imageData, 0, sizeof(imageData)); + memset(imageDataBU, 0, sizeof(imageDataBU)); + memset(stack, 0, sizeof(stack)); + memset(suffix, 0, sizeof(suffix)); + memset(prefix, 0, sizeof(prefix)); + parseGifHeader(); + parseLogicalScreenDescriptor(); + parseGlobalColorTable(); + drawFrame(); + } + } + + boolean parseGifHeader() + { + char buffer[10]; + readIntoBuffer(buffer, GIFHDRSIZE); + if ((strncmp(buffer, GIFHDRTAGNORM, GIFHDRSIZE) != 0) && + (strncmp(buffer, GIFHDRTAGNORM1, GIFHDRSIZE) != 0)) + { + return false; + } + else + { + return true; + } + } + + void parseLogicalScreenDescriptor() + { + lsdWidth = readWord(); + lsdHeight = readWord(); + lsdPackedField = readByte(); + lsdBackgroundIndex = readByte(); + lsdAspectRatio = readByte(); + } + + void parseGlobalColorTable() + { + if (lsdPackedField & COLORTBLFLAG) + { + colorCount = 1 << ((lsdPackedField & 7) + 1); + int colorTableBytes = sizeof(_RGB) * colorCount; + readIntoBuffer(gifPalette, colorTableBytes); + } + } + + unsigned long drawFrame() + { + if (millis() - lastFrameTime < newframeDelay) + { + redrawLastFrame(); + return 0; + } + + lastFrameDrawn = false; + + boolean done = false; + while (!done) + { + byte b = readByte(); + if (b == 0x2c) + { + parseTableBasedImage(); + return 0; + } + else if (b == 0x21) + { + b = readByte(); + switch (b) + { + case 0x01: + parsePlainTextExtension(); + break; + case 0xf9: + parseGraphicControlExtension(); + break; + case 0xfe: + parseCommentExtension(); + break; + case 0xff: + parseApplicationExtension(); + break; + default: + return ERROR_UNKNOWNCONTROLEXT; + } + } + else + { + done = true; + file.seek(0); + parseGifHeader(); + parseLogicalScreenDescriptor(); + parseGlobalColorTable(); + drawFrame(); + return ERROR_FINISHED; + } + } + return ERROR_NONE; + } +}; #endif \ No newline at end of file diff --git a/lib/MatrixUI/MatrixDisplayUi.cpp b/lib/MatrixUI/MatrixDisplayUi.cpp index 5911df7..ba6a9b0 100644 --- a/lib/MatrixUI/MatrixDisplayUi.cpp +++ b/lib/MatrixUI/MatrixDisplayUi.cpp @@ -30,6 +30,9 @@ #include "MatrixDisplayUi.h" #include "Fonts/AwtrixFont.h" + GifPlayer gif1; + GifPlayer gif2; + MatrixDisplayUi::MatrixDisplayUi(FastLED_NeoMatrix *matrix) { this->matrix = matrix; @@ -41,6 +44,8 @@ void MatrixDisplayUi::init() this->matrix->setTextWrap(false); this->matrix->setBrightness(70); this->matrix->setFont(&AwtrixFont); + gif1.setMatrix(this->matrix); + gif2.setMatrix(this->matrix); } void MatrixDisplayUi::setTargetFPS(uint8_t fps) @@ -256,12 +261,12 @@ void MatrixDisplayUi::drawApp() bool FirstApp = progress < 0.2; bool LastApp = progress > 0.8; this->matrix->drawRect(x, y, x1, y1, matrix->Color(0, 0, 0)); - (this->AppFunctions[this->state.currentApp])(this->matrix, &this->state, x, y, FirstApp, LastApp); - (this->AppFunctions[this->getnextAppNumber()])(this->matrix, &this->state, x1, y1, FirstApp, LastApp); + (this->AppFunctions[this->state.currentApp])(this->matrix, &this->state, x, y, FirstApp, LastApp, &gif1); + (this->AppFunctions[this->getnextAppNumber()])(this->matrix, &this->state, x1, y1, FirstApp, LastApp, &gif2); break; } case FIXED: - (this->AppFunctions[this->state.currentApp])(this->matrix, &this->state, 0, 0, false, false); + (this->AppFunctions[this->state.currentApp])(this->matrix, &this->state, 0, 0, false, false, &gif2); break; } } @@ -278,7 +283,7 @@ void MatrixDisplayUi::drawOverlays() { for (uint8_t i = 0; i < this->overlayCount; i++) { - (this->overlayFunctions[i])(this->matrix, &this->state); + (this->overlayFunctions[i])(this->matrix, &this->state, &gif2); } } diff --git a/lib/MatrixUI/MatrixDisplayUi.h b/lib/MatrixUI/MatrixDisplayUi.h index a1de439..5ef0e4c 100644 --- a/lib/MatrixUI/MatrixDisplayUi.h +++ b/lib/MatrixUI/MatrixDisplayUi.h @@ -31,7 +31,7 @@ #include #include "FastLED_NeoMatrix.h" #include - +#include "GifPlayer.h" // #define DEBUG_MatrixDisplayUi(...) Serial.printf( __VA_ARGS__ ) #ifndef DEBUG_MatrixDisplayUi @@ -53,6 +53,7 @@ enum AppState // Structure of the UiState struct MatrixDisplayUiState { + u_int64_t lastUpdate = 0; uint16_t ticksSinceLastStateSwitch = 0; @@ -68,17 +69,17 @@ struct MatrixDisplayUiState void *userData = NULL; }; -typedef void (*AppCallback)(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstApp, bool lastApp); -typedef void (*OverlayCallback)(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state); +typedef void (*AppCallback)(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer); +typedef void (*OverlayCallback)(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, GifPlayer *gifPlayer); class MatrixDisplayUi { private: FastLED_NeoMatrix *matrix; + // Values for the Apps AnimationDirection appAnimationDirection = SLIDE_DOWN; - int8_t lastTransitionDirection = 1; uint16_t ticksPerApp = 151; // ~ 5000ms at 30 FPS @@ -86,7 +87,7 @@ private: bool setAutoTransition = true; bool lastFrameShown; - AppCallback *AppFunctions; + AppCallback *AppFunctions = nullptr; // Internally used to transition to a specific app int8_t nextAppNumber = -1; @@ -114,7 +115,6 @@ public: * Initialise the display */ void init(); - void show(); /** * Configure the internal used target FPS diff --git a/platformio.ini b/platformio.ini index edd1640..3d2ed23 100644 --- a/platformio.ini +++ b/platformio.ini @@ -25,8 +25,6 @@ lib_deps = marcmerlin/FastLED NeoMatrix@^1.2 knolleary/PubSubClient@^2.8 plerup/EspSoftwareSerial@^8.0.1 - h2zero/NimBLE-Arduino@^1.4.1 - [env:awtrix_upgrade] platform = https://github.com/platformio/platform-espressif32.git @@ -45,4 +43,3 @@ lib_deps = marcmerlin/FastLED NeoMatrix@^1.2 knolleary/PubSubClient@^2.8 plerup/EspSoftwareSerial@^8.0.1 - h2zero/NimBLE-Arduino@^1.4.1 diff --git a/src/Apps.h b/src/Apps.h index 29f4435..b898a5b 100644 --- a/src/Apps.h +++ b/src/Apps.h @@ -107,7 +107,7 @@ int findAppIndexByName(const String &name) return -1; } -void TimeApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void TimeApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { if (notify.flag) return; @@ -155,7 +155,7 @@ void TimeApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, } } -void DateApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void DateApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { if (notify.flag) return; @@ -183,7 +183,7 @@ void DateApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, } } -void TempApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void TempApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { if (notify.flag) return; @@ -209,7 +209,7 @@ void TempApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, } } -void HumApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void HumApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { if (notify.flag) return; @@ -222,10 +222,8 @@ void HumApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, i matrix->print("%"); } - - #ifdef ULANZI -void BatApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void BatApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { if (notify.flag) return; @@ -238,7 +236,7 @@ void BatApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, i } #endif -void MenuApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) +void MenuApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, GifPlayer *gifPlayer) { if (!MenuManager.inMenu) return; @@ -246,7 +244,7 @@ void MenuApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) DisplayManager.printText(0, 6, utf8ascii(MenuManager.menutext()).c_str(), true, true); } -void AlarmApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) +void AlarmApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, GifPlayer *gifPlayer) { if (ALARM_ACTIVE) { @@ -271,7 +269,7 @@ void AlarmApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) } } -void TimerApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) +void TimerApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, GifPlayer *gifPlayer) { if (TIMER_ACTIVE) { @@ -297,7 +295,7 @@ void TimerApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) } } -void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { // Abort if notify.flag is set if (notify.flag) @@ -457,10 +455,8 @@ void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState // Display animated GIF if enabled and App is fixed, since we have only one gifplayer instance, it looks weird when 2 apps want to draw a different gif if (ca->isGif) { - if (state->appState == FIXED) - { - DisplayManager.drawGIF(x + ca->iconPosition, y, ca->icon); - } + + gifPlayer->playGif(x + ca->iconPosition, y, &ca->icon); } else { @@ -478,7 +474,7 @@ void ShowCustomApp(String name, FastLED_NeoMatrix *matrix, MatrixDisplayUiState DisplayManager.getInstance().resetTextColor(); } -void NotifyApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) +void NotifyApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, GifPlayer *gifPlayer) { // Check if notification flag is set if (!notify.flag) @@ -634,7 +630,8 @@ void NotifyApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) if (notify.isGif) { // Display GIF if present - DisplayManager.drawGIF(notify.iconPosition, 0, notify.icon); + + gifPlayer->playGif(notify.iconPosition, 0, ¬ify.icon); } else { @@ -655,124 +652,124 @@ void NotifyApp(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state) // Unattractive to have a function for every customapp wich does the same, but currently still no other option found TODO -void CApp1(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp1(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp1); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp2(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp2(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp2); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp3(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp3(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp3); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp4(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp4(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp4); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp5(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp5(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp5); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp6(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp6(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp6); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp7(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp7(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp7); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp8(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp8(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp8); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp9(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp9(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp9); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp10(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp10(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp10); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp11(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp11(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp11); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp12(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp12(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp12); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp13(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp13(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp13); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp14(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp14(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp14); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp15(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp15(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp15); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp16(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp16(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp16); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp17(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp17(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp17); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp18(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp18(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp18); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp19(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp19(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp19); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } -void CApp20(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame) +void CApp20(FastLED_NeoMatrix *matrix, MatrixDisplayUiState *state, int16_t x, int16_t y, bool firstFrame, bool lastFrame, GifPlayer *gifPlayer) { String name = getAppNameByFunction(CApp20); - ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame); + ShowCustomApp(name, matrix, state, x, y, firstFrame, lastFrame, gifPlayer); } const uint16_t *getWeatherIcon(int code) diff --git a/src/BeaconScanner.cpp b/src/BeaconScanner.cpp deleted file mode 100644 index be18710..0000000 --- a/src/BeaconScanner.cpp +++ /dev/null @@ -1,292 +0,0 @@ -#include -#include -#include -#include "MQTTManager.h" -#include -#include -#include - -float triggerDistance = 0.6; -String room = "Büro"; -std::vector allowedIds; - -Ticker scanTicker; -// The getter for the instantiated singleton instance -BeaconScanner_ &BeaconScanner_::getInstance() -{ - static BeaconScanner_ instance; - return instance; -} - -// Initialize the global shared instance -BeaconScanner_ &BeaconScanner = BeaconScanner.getInstance(); - -static const int scanTime = 5; // In Sekunden -NimBLEScan *pBLEScan; - -uint32_t fnv1a_32(const String &input) -{ - const uint32_t FNV_PRIME = 0x01000193; - const uint32_t FNV_OFFSET_BASIS = 0x811C9DC5; - - uint32_t hash = FNV_OFFSET_BASIS; - - for (unsigned int i = 0; i < input.length(); ++i) - { - hash ^= static_cast(input[i]); - hash *= FNV_PRIME; - } - - return hash; -} - -String getManufacturerName(uint16_t manufacturerID) -{ - switch (manufacturerID) - { - case 0x004C: // Apple, Inc. - return "Apple"; - case 0x0118: // Google, Inc. - return "Google"; - case 0x000F: // Microsoft Corporation - return "Microsoft"; - case 0x0030: // Ericsson Technology Licensing - return "Ericsson"; - case 0x0039: // Intel Corp. - return "Intel"; - case 0x0059: // Samsung Electronics Co., Ltd. - return "Samsung"; - case 0x000D: // Nokia Corporation - return "Nokia"; - case 0x0100: // Broadcom Corporation - return "Broadcom"; - case 0x0034: // Texas Instruments Inc. - return "Texas Instruments"; - case 0x0075: // Sony Ericsson Mobile Communications AB - return "Sony Ericsson"; - case 0x0089: // Panasonic Corporation - return "Panasonic"; - default: - return "Unknown"; - } -} - -bool isIdAllowed(const char *id) -{ - if (allowedIds.size() == 0) - return true; - for (const String &allowedId : allowedIds) - { - if (strcmp(id, allowedId.c_str()) == 0) - { - return true; - } - } - Serial.println("Not allowed"); - return false; -} - -double calculateDistance(int rssi, int8_t txPower) -{ - if (rssi == 0) - { - return -1.0; - } - - double pathLoss = txPower - rssi; - // Path Loss Exponent (n) ist eine Umgebungsspezifische Konstante und liegt normalerweise zwischen 2 und 4. - // 2 entspricht Freiraum, während 4 dichtere Umgebungen repräsentiert. - double pathLossExponent = 3.0; - - return pow(10, pathLoss / (10 * pathLossExponent)); -} - -String toHexString(const std::string &input) -{ - String hexString = ""; - for (unsigned char c : input) - { - char hexChar[3]; - snprintf(hexChar, sizeof(hexChar), "%02X", c); - hexString += hexChar; - } - return hexString; -} - -bool isIBeacon(NimBLEAdvertisedDevice *advertisedDevice, uint16_t *major, uint16_t *minor, int8_t *txPower, String *uuid, String *manufacturerName) -{ - std::string payload = advertisedDevice->getManufacturerData(); - - if (payload.size() == 25 && static_cast(payload[0]) == 0x4C && static_cast(payload[1]) == 0x00 && static_cast(payload[2]) == 0x02 && static_cast(payload[3]) == 0x15) - { - uint16_t manufacturerID = (static_cast(payload[0]) << 8) | static_cast(payload[1]); - *uuid = toHexString(payload.substr(4, 16)); - *major = (static_cast(payload[20]) << 8) | static_cast(payload[21]); - *minor = (static_cast(payload[22]) << 8) | static_cast(payload[23]); - *txPower = static_cast(payload[24]); - *manufacturerName = getManufacturerName(manufacturerID); - return true; - } - return false; -} - -bool isEddystoneUID(NimBLEAdvertisedDevice *advertisedDevice, String *namespaceID, String *instanceID) -{ - std::string serviceData = advertisedDevice->getServiceData(); - if (serviceData.size() >= 20 && static_cast(serviceData[0]) == 0xAA && static_cast(serviceData[1]) == 0xFE && static_cast(serviceData[2]) == 0x00) - { - *namespaceID = toHexString(serviceData.substr(4, 10)); - *instanceID = toHexString(serviceData.substr(14, 6)); - return true; - } - - return false; -} - -class MyAdvertisedDeviceCallbacks : public NimBLEAdvertisedDeviceCallbacks -{ - void onResult(NimBLEAdvertisedDevice *advertisedDevice) - { - StaticJsonDocument<512> doc; - uint16_t major; - uint16_t minor; - int8_t txPower; - String uuid; - String manufacturer; - - uint32_t hashedUUID = fnv1a_32(uuid); - char compressedUUID[9]; - snprintf(compressedUUID, sizeof(compressedUUID), "%08X", hashedUUID); - - int rssi = advertisedDevice->getRSSI(); - doc["id"] = compressedUUID; - doc["rssi"] = rssi; - String topic; - bool sendToMQTT = false; - topic = String("rooms/") + room; - if (isIBeacon(advertisedDevice, &major, &minor, &txPower, &uuid, &manufacturer)) - { - double BeaconDistance = calculateDistance(rssi, txPower); - - doc["txPower"] = txPower; - doc["distance"] = BeaconDistance; - - if (advertisedDevice->haveName()) - { - String nameBLE = String(advertisedDevice->getName().c_str()); - doc["name"] = nameBLE; - } - Serial.print("iBeacon UUID: "); - Serial.print(compressedUUID); - Serial.print(" Distance: "); - Serial.print(BeaconDistance); - Serial.print(" Manufacturer: "); - Serial.print(manufacturer); - Serial.print(" TxPower: "); - Serial.println(txPower); - - if (BeaconDistance < 0.2) - { - char jsonString[100]; - memset(jsonString, 0, sizeof(jsonString)); - std::snprintf(jsonString, sizeof(jsonString), "{\"text\":\"%s\",\"duration\":\"%d\",\"color\":%s\"}", compressedUUID, 4,"#00ff00"); - DisplayManager.generateNotification(jsonString); - return; - } - - if ((BeaconDistance <= triggerDistance) && isIdAllowed(compressedUUID)) - { - char JSONmessageBuffer[512]; - serializeJson(doc, JSONmessageBuffer); - MQTTManager.publish(topic.c_str(), JSONmessageBuffer); - } - } - } -}; - -void BeaconScanner_::startScan() -{ - Serial.println(F("---------------")); - Serial.println(F("Scan BLE")); - Serial.println(F("---------------")); - pBLEScan->start(scanTime, [](NimBLEScanResults results) - { scanTicker.once(scanTime, []() - { BeaconScanner.startScan(); }); }); -} - -void printAllowedIds() -{ - Serial.println("Allowed IDs:"); - for (const String &allowedId : allowedIds) - { - Serial.println(allowedId); - } -} - -bool loadBeaconSettings() -{ - Serial.println("loadSettings"); - if (LittleFS.exists("/beacons.json")) - { - File file = LittleFS.open("/beacons.json", "r"); - DynamicJsonDocument doc(128); - DeserializationError error = deserializeJson(doc, file); - if (error) - { - Serial.println(F("Failed to read beacon settings")); - return false; - } - - if (doc.containsKey("room")) - { - room = doc["room"].as(); - Serial.println(room); - } - else - { - return false; - } - - if (doc.containsKey("trigger_distance")) - { - triggerDistance = doc["trigger_distance"].as(); - Serial.println(triggerDistance); - } - else - { - return false; - } - - if (doc.containsKey("allowed_ids")) - { - JsonArray allowedIdsJsonArray = doc["allowed_ids"]; - for (const char *id : allowedIdsJsonArray) - { - allowedIds.push_back(String(id)); - } - printAllowedIds(); - } - - return true; - file.close(); - } - else - { - return false; - } - } - -void BeaconScanner_::setup() -{ - if (!loadBeaconSettings()) - return; - NimBLEDevice::init(""); - NimBLEDevice::setPower(ESP_PWR_LVL_P9); - pBLEScan = NimBLEDevice::getScan(); - pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); - pBLEScan->setInterval(100); - pBLEScan->setWindow(99); - pBLEScan->setActiveScan(true); - pBLEScan->setMaxResults(0); - startScan(); -} \ No newline at end of file diff --git a/src/BeaconScanner.h b/src/BeaconScanner.h deleted file mode 100644 index c7da94c..0000000 --- a/src/BeaconScanner.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef BeaconScanner_h -#define BeaconScanner_h - -#include - -class BeaconScanner_ -{ -private: - BeaconScanner_() = default; - -public: - static BeaconScanner_ &getInstance(); - void setup(); - void startScan(); -}; - -extern BeaconScanner_ &BeaconScanner; -#endif diff --git a/src/DisplayManager.cpp b/src/DisplayManager.cpp index af7c149..2ce1518 100644 --- a/src/DisplayManager.cpp +++ b/src/DisplayManager.cpp @@ -15,6 +15,7 @@ #include "Apps.h" #include "Dictionary.h" #include +#include "GifPlayer.h" Ticker AlarmTicker; Ticker TimerTicker; @@ -93,12 +94,6 @@ bool DisplayManager_::setAutoTransition(bool active) showGif = false; } -void DisplayManager_::drawGIF(uint16_t x, uint16_t y, fs::File gFile) -{ - gif.setFile(gFile); - gif.drawFrame(x, y); -} - void DisplayManager_::drawJPG(uint16_t x, uint16_t y, fs::File jpgFile) { TJpgDec.drawFsJpg(x, y, jpgFile); @@ -211,7 +206,7 @@ void pushCustomApp(String name, int position) if (customApps.count(name) == 0) { ++customPagesCount; - void (*customApps[20])(FastLED_NeoMatrix *, MatrixDisplayUiState *, int16_t, int16_t, bool, bool) = {CApp1, CApp2, CApp3, CApp4, CApp5, CApp6, CApp7, CApp8, CApp9, CApp10, CApp11, CApp12, CApp13, CApp14, CApp15, CApp16, CApp17, CApp18, CApp19, CApp20}; + void (*customApps[20])(FastLED_NeoMatrix *, MatrixDisplayUiState *, int16_t, int16_t, bool, bool, GifPlayer *) = {CApp1, CApp2, CApp3, CApp4, CApp5, CApp6, CApp7, CApp8, CApp9, CApp10, CApp11, CApp12, CApp13, CApp14, CApp15, CApp16, CApp17, CApp18, CApp19, CApp20}; if (position < 0) // Insert at the end of the vector { @@ -307,7 +302,7 @@ void DisplayManager_::generateCustomPage(const String &name, const char *json) customApp.pushIcon = doc.containsKey("pushIcon") ? doc["pushIcon"] : 0; customApp.textCase = doc.containsKey("textCase") ? doc["textCase"] : 0; customApp.name = name; - customApp.text = utf8ascii(doc["text"].as()); + customApp.text = doc.containsKey("text") ? utf8ascii(doc["text"].as()) : ""; if (doc.containsKey("color")) { @@ -386,7 +381,7 @@ void DisplayManager_::generateNotification(const char *json) deserializeJson(doc, json); notify.duration = doc.containsKey("duration") ? doc["duration"].as() * 1000 : TIME_PER_APP; - notify.text = utf8ascii(doc["text"].as()); + notify.text = doc.containsKey("text") ? utf8ascii(doc["text"].as()) : ""; notify.repeat = doc.containsKey("repeat") ? doc["repeat"].as() : -1; notify.rainbow = doc.containsKey("rainbow") ? doc["rainbow"].as() : false; notify.hold = doc.containsKey("hold") ? doc["hold"].as() : false; @@ -551,10 +546,6 @@ void DisplayManager_::setup() ui->init(); } -void ShowGif() -{ -} - void DisplayManager_::tick() { if (AP_MODE) @@ -577,9 +568,6 @@ void DisplayManager_::tick() MQTTManager.setCurrentApp(CURRENT_APP); setAppTime(TIME_PER_APP); } - // if (showGif && !MenuManager.inMenu) - // - // matrix->show(); } } diff --git a/src/main.cpp b/src/main.cpp index 91b2bf3..5c42490 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,7 +37,6 @@ #include "ServerManager.h" #include "Globals.h" #include "UpdateManager.h" -#include "BeaconScanner.h" TaskHandle_t taskHandle; volatile bool StopTask = false; @@ -85,7 +84,7 @@ void setup() DisplayManager.HSVtext(x, 6, ("AWTRIX " + ServerManager.myIP.toString()).c_str(), true, 0); x -= 0.18; } - BeaconScanner.setup(); + // BeaconScanner.setup(); } else { @@ -94,6 +93,7 @@ void setup() } delay(200); + DisplayManager.setBrightness(BRIGHTNESS); DisplayManager.clearMatrix(); }