diff --git a/src/M5Display.cpp b/src/M5Display.cpp index 895d3ef..1ba1f8e 100644 --- a/src/M5Display.cpp +++ b/src/M5Display.cpp @@ -431,4 +431,181 @@ void M5Display::qrcode(const String &string, uint16_t x, uint16_t y, uint8_t wid char buffer[len]; string.toCharArray(buffer, len); qrcode(buffer, x, y, width, version); -} \ No newline at end of file +} + + +#include "rom/tjpgd.h" + +#define jpgColor(c) \ + (((uint16_t)(((uint8_t *)(c))[0] & 0xF8) << 8) | \ + ((uint16_t)(((uint8_t *)(c))[1] & 0xFC) << 3) | \ + ((((uint8_t *)(c))[2] & 0xF8) >> 3)) + + +typedef struct { + uint16_t x; + uint16_t y; + uint16_t maxWidth; + uint16_t maxHeight; + uint16_t offX; + uint16_t offY; + jpeg_div_t scale; + const void *src; + size_t len; + size_t index; + M5Display *tft; + uint16_t outWidth; + uint16_t outHeight; +} jpg_file_decoder_t; + +static uint32_t jpgRead(JDEC *decoder, uint8_t *buf, uint32_t len) { + jpg_file_decoder_t *jpeg = (jpg_file_decoder_t *)decoder->device; + if (buf) { + memcpy(buf, (const uint8_t *)jpeg->src + jpeg->index, len); + } + jpeg->index += len; + return len; +} + +static uint32_t jpgWrite(JDEC *decoder, void *bitmap, JRECT *rect) { + jpg_file_decoder_t *jpeg = (jpg_file_decoder_t *)decoder->device; + uint16_t x = rect->left; + uint16_t y = rect->top; + uint16_t w = rect->right + 1 - x; + uint16_t h = rect->bottom + 1 - y; + uint16_t oL = 0, oR = 0; + uint8_t *data = (uint8_t *)bitmap; + + if (rect->right < jpeg->offX) { + return 1; + } + if (rect->left >= (jpeg->offX + jpeg->outWidth)) { + return 1; + } + if (rect->bottom < jpeg->offY) { + return 1; + } + if (rect->top >= (jpeg->offY + jpeg->outHeight)) { + return 1; + } + if (rect->top < jpeg->offY) { + uint16_t linesToSkip = jpeg->offY - rect->top; + data += linesToSkip * w * 3; + h -= linesToSkip; + y += linesToSkip; + } + if (rect->bottom >= (jpeg->offY + jpeg->outHeight)) { + uint16_t linesToSkip = (rect->bottom + 1) - (jpeg->offY + jpeg->outHeight); + h -= linesToSkip; + } + if (rect->left < jpeg->offX) { + oL = jpeg->offX - rect->left; + } + if (rect->right >= (jpeg->offX + jpeg->outWidth)) { + oR = (rect->right + 1) - (jpeg->offX + jpeg->outWidth); + } + + uint16_t pixBuf[32]; + uint8_t pixIndex = 0; + uint16_t line; + + jpeg->tft->startWrite(); + // jpeg->tft->setAddrWindow(x - jpeg->offX + jpeg->x + oL, y - jpeg->offY + + // jpeg->y, w - (oL + oR), h); + jpeg->tft->setWindow(x - jpeg->offX + jpeg->x + oL, + y - jpeg->offY + jpeg->y, + x - jpeg->offX + jpeg->x + oL + w - (oL + oR) - 1, + y - jpeg->offY + jpeg->y + h - 1); + + while (h--) { + data += 3 * oL; + line = w - (oL + oR); + while (line--) { + pixBuf[pixIndex++] = jpgColor(data); + data += 3; + if (pixIndex == 32) { + jpeg->tft->writePixels(pixBuf, 32); + // SPI.writePixels((uint8_t *)pixBuf, 64); + pixIndex = 0; + } + } + data += 3 * oR; + } + if (pixIndex) { + jpeg->tft->writePixels(pixBuf, pixIndex); + // SPI.writePixels((uint8_t *)pixBuf, pixIndex * 2); + } + jpeg->tft->endWrite(); + return 1; +} + + +static bool jpgDecode(jpg_file_decoder_t *jpeg, + uint32_t (*reader)(JDEC *, uint8_t *, uint32_t)) { + static uint8_t work[3100]; + JDEC decoder; + + JRESULT jres = jd_prepare(&decoder, reader, work, 3100, jpeg); + if (jres != JDR_OK) { + log_e("jd_prepare failed! %s", jd_errors[jres]); + return false; + } + + uint16_t jpgWidth = decoder.width / (1 << (uint8_t)(jpeg->scale)); + uint16_t jpgHeight = decoder.height / (1 << (uint8_t)(jpeg->scale)); + + if (jpeg->offX >= jpgWidth || jpeg->offY >= jpgHeight) { + log_e("Offset Outside of JPEG size"); + return false; + } + + size_t jpgMaxWidth = jpgWidth - jpeg->offX; + size_t jpgMaxHeight = jpgHeight - jpeg->offY; + + jpeg->outWidth = + (jpgMaxWidth > jpeg->maxWidth) ? jpeg->maxWidth : jpgMaxWidth; + jpeg->outHeight = + (jpgMaxHeight > jpeg->maxHeight) ? jpeg->maxHeight : jpgMaxHeight; + + jres = jd_decomp(&decoder, jpgWrite, (uint8_t)jpeg->scale); + if (jres != JDR_OK) { + log_e("jd_decomp failed! %s", jd_errors[jres]); + return false; + } + + return true; +} + + +void M5Display::drawJpg(const uint8_t *jpg_data, size_t jpg_len, uint16_t x, + uint16_t y, uint16_t maxWidth, uint16_t maxHeight, + uint16_t offX, uint16_t offY, jpeg_div_t scale) { + if ((x + maxWidth) > width() || (y + maxHeight) > height()) { + log_e("Bad dimensions given"); + return; + } + + jpg_file_decoder_t jpeg; + + if (!maxWidth) { + maxWidth = width() - x; + } + if (!maxHeight) { + maxHeight = height() - y; + } + + jpeg.src = jpg_data; + jpeg.len = jpg_len; + jpeg.index = 0; + jpeg.x = x; + jpeg.y = y; + jpeg.maxWidth = maxWidth; + jpeg.maxHeight = maxHeight; + jpeg.offX = offX; + jpeg.offY = offY; + jpeg.scale = scale; + jpeg.tft = this; + + jpgDecode(&jpeg, jpgRead); +} + diff --git a/src/M5Display.h b/src/M5Display.h index 153b84d..25b96a3 100644 --- a/src/M5Display.h +++ b/src/M5Display.h @@ -49,10 +49,6 @@ class M5Display : public TFT_eSPI { inline void setHighlightColor(uint16_t color) { highlightcolor = color; istransparent = false; } void qrcode(const char *string, uint16_t x = 5, uint16_t y = 45, uint8_t width = 70, uint8_t version = 7); void qrcode(const String &string, uint16_t x = 5, uint16_t y = 45, uint8_t width = 70, uint8_t version = 7); - /* - void clearDisplay(uint32_t color=ILI9341_BLACK) { fillScreen(color); } - void clear(uint32_t color=ILI9341_BLACK) { fillScreen(color); } - void display() {} inline void startWrite(void){ #if defined (SPI_HAS_TRANSACTION) && defined (SUPPORT_TRANSACTIONS) && !defined(ESP32_PARALLEL) @@ -70,6 +66,18 @@ class M5Display : public TFT_eSPI { inline void writePixels(uint16_t * colors, uint32_t len){ SPI.writePixels((uint8_t*)colors , len * 2); } + + void drawJpg(const uint8_t *jpg_data, size_t jpg_len, uint16_t x = 0, + uint16_t y = 0, uint16_t maxWidth = 0, uint16_t maxHeight = 0, + uint16_t offX = 0, uint16_t offY = 0, + jpeg_div_t scale = JPEG_DIV_NONE); + + /* + void clearDisplay(uint32_t color=ILI9341_BLACK) { fillScreen(color); } + void clear(uint32_t color=ILI9341_BLACK) { fillScreen(color); } + void display() {} + + void progressBar(int x, int y, int w, int h, uint8_t val); #define setFont setFreeFont @@ -86,10 +94,7 @@ class M5Display : public TFT_eSPI { void drawBitmap(int16_t x0, int16_t y0, int16_t w, int16_t h, uint8_t *data); void drawBitmap(int16_t x0, int16_t y0, int16_t w, int16_t h, const uint16_t *data, uint16_t transparent); - void drawJpg(const uint8_t *jpg_data, size_t jpg_len, uint16_t x = 0, - uint16_t y = 0, uint16_t maxWidth = 0, uint16_t maxHeight = 0, - uint16_t offX = 0, uint16_t offY = 0, - jpeg_div_t scale = JPEG_DIV_NONE); + void drawJpg(fs::FS &fs, const char *path, uint16_t x = 0, uint16_t y = 0, uint16_t maxWidth = 0, uint16_t maxHeight = 0,