diff --git a/lib/Xlatb_RA8876-gemu-1.0/RA8876.cpp b/lib/Xlatb_RA8876-gemu-1.0/RA8876.cpp index 850b2bb8a..1d9a893e4 100644 --- a/lib/Xlatb_RA8876-gemu-1.0/RA8876.cpp +++ b/lib/Xlatb_RA8876-gemu-1.0/RA8876.cpp @@ -66,13 +66,14 @@ RA8876::RA8876(int8_t cs,int8_t mosi,int8_t miso,int8_t sclk,int8_t bp) : Render //#define RA8876_CS_LOW digitalWrite(m_csPin, LOW) //#define RA8876_CS_HIGH digitalWrite(m_csPin, HIGH) -#ifdef ESP8266 +#ifndef ESP32 #define RA8876_CS_LOW GPOC=(1<= 0x21) || (c <= 0x7F))) diff --git a/lib/Xlatb_RA8876-gemu-1.0/RA8876.h b/lib/Xlatb_RA8876-gemu-1.0/RA8876.h index b27698027..5bf8b404f 100644 --- a/lib/Xlatb_RA8876-gemu-1.0/RA8876.h +++ b/lib/Xlatb_RA8876-gemu-1.0/RA8876.h @@ -23,6 +23,7 @@ #include #include +#undef SPRINT #define SPRINT(A) {char str[32];sprintf(str,"val: %d ",A);Serial.println((char*)str);} diff --git a/tasmota/support_jpeg.ino b/tasmota/support_jpeg.ino new file mode 100644 index 000000000..7e811cd76 --- /dev/null +++ b/tasmota/support_jpeg.ino @@ -0,0 +1,157 @@ +/* + jpeg_utils.c - Version header file for Tasmota + + Copyright (C) 2020 Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifdef ESP32 +#ifdef JPEG_PICTS + +#include "img_converters.h" +#include "esp_jpg_decode.h" + +void rgb888_to_565(uint8_t *in, uint16_t *out, uint32_t len) { +uint8_t red, grn, blu; +uint16_t b , g, r; + + for (uint32_t cnt=0; cnt> 3) & 0x1f; + g = ((grn >> 2) & 0x3f) << 5; + r = ((red >> 3) & 0x1f) << 11; + *out++ = (r | g | b); + } +} + +typedef struct { + uint16_t width; + uint16_t height; + uint16_t data_offset; + const uint8_t *input; + uint8_t *output; +} rgb_jpg_decoder; + +//input buffer +static uint32_t _jpg_read(void * arg, size_t index, uint8_t *buf, size_t len) +{ + rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg; + if(buf) { + memcpy(buf, jpeg->input + index, len); + } + return len; +} + +//output buffer and image width +static bool _rgb_write(void * arg, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *data) +{ + rgb_jpg_decoder * jpeg = (rgb_jpg_decoder *)arg; + if(!data){ + if(x == 0 && y == 0){ + //write start + jpeg->width = w; + jpeg->height = h; + //if output is null, this is BMP + if(!jpeg->output){ + jpeg->output = (uint8_t *)malloc((w*h*3)+jpeg->data_offset); + if(!jpeg->output){ + return false; + } + } + } else { + //write end + } + return true; + } + + size_t jw = jpeg->width*3; + size_t t = y * jw; + size_t b = t + (h * jw); + size_t l = x * 3; + uint8_t *out = jpeg->output+jpeg->data_offset; + uint8_t *o = out; + size_t iy, ix; + + w = w * 3; + + for(iy=t; iy= data_size) return false; //Check to protect against segmentation faults + if(data[i] != 0xFF) return false; //Check that we are truly at the start of another block + if(data[i+1] == 0xC0) { //0xFFC0 is the "Start of frame" marker which contains the file size + //The structure of the 0xFFC0 block is quite simple [0xFFC0][ushort length][uchar precision][ushort x][ushort y] + *height = data[i+5]*256 + data[i+6]; + *width = data[i+7]*256 + data[i+8]; + return true; + } + else + { + i+=2; //Skip the block marker + block_length = data[i] * 256 + data[i+1]; //Go to the next block + } + } + return false; //If this point is reached then no size was found + }else{ return false; } //Not a valid JFIF string + + }else{ return false; } //Not a valid SOI header +} + +#endif // JPEG_PICTS +#endif //ESP32 diff --git a/tasmota/xdrv_13_display.ino b/tasmota/xdrv_13_display.ino index e490d0bd1..d2466d051 100644 --- a/tasmota/xdrv_13_display.ino +++ b/tasmota/xdrv_13_display.ino @@ -1500,47 +1500,109 @@ void CmndDisplayRows(void) /*********************************************************************************************\ * optional drivers \*********************************************************************************************/ +#ifdef ESP32 +#ifdef JPEG_PICTS +#include "img_converters.h" +#include "esp_jpg_decode.h" +bool jpg2rgb888(const uint8_t *src, size_t src_len, uint8_t * out, jpg_scale_t scale); +char get_jpeg_size(unsigned char* data, unsigned int data_size, unsigned short *width, unsigned short *height); +void rgb888_to_565(uint8_t *in, uint16_t *out, uint32_t len); +#endif +#endif + #if defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT) +#define XBUFF_LEN 128 void Draw_RGB_Bitmap(char *file,uint16_t xp, uint16_t yp) { if (!renderer) return; - - //if (!strstr(file,".RGB")) return; File fp; - fp=SD.open(file,FILE_READ); - if (!fp) return; - uint16_t xsize; - fp.read((uint8_t*)&xsize,2); - uint16_t ysize; - fp.read((uint8_t*)&ysize,2); + char *ending = strrchr(file,'.'); + if (!ending) return; + ending++; + char estr[8]; + memset(estr,0,sizeof(estr)); + for (uint32_t cnt=0; cntsetAddrWindow(xp,yp,xp+xsize,yp+ysize); - for(int16_t j=0; j=2) renderer->pushColors(rgb,len/2,true); + uint16_t xdiv=xsize/XBUFF_LEN; + renderer->setAddrWindow(xp,yp,xp+xsize,yp+ysize); + for(int16_t j=0; j=2) renderer->pushColors(rgb,len/2,true); + } + OsWatchLoop(); } - OsWatchLoop(); - } - renderer->setAddrWindow(0,0,0,0); + renderer->setAddrWindow(0,0,0,0); #else - for(int16_t j=0; jwritePixel(xp+i,yp,rgb); + for(int16_t j=0; jwritePixel(xp+i,yp,rgb); + } + delay(0); + OsWatchLoop(); + yp++; } - delay(0); - OsWatchLoop(); - yp++; - } #endif - fp.close(); + fp.close(); + } else if (!strcmp(estr,"jpg")) { + // jpeg files on ESP32 with more memory +#ifdef ESP32 +#ifdef JPEG_PICTS + if (psramFound()) { + fp=SD.open(file,FILE_READ); + if (!fp) return; + uint32_t size = fp.size(); + uint8_t *mem = (uint8_t *)heap_caps_malloc(size+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (mem) { + uint8_t res=fp.read(mem, size); + if (res) { + uint16_t xsize; + uint16_t ysize; + if (mem[0]==0xff && mem[1]==0xd8) { + get_jpeg_size(mem, size, &xsize, &ysize); + //Serial.printf(" x,y %d - %d\n",xsize, ysize ); + if (xsize && ysize) { + uint8_t *out_buf = (uint8_t *)heap_caps_malloc((xsize*ysize*3)+4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (out_buf) { + uint8_t *ob=out_buf; + jpg2rgb888(mem, size, out_buf, (jpg_scale_t)JPG_SCALE_NONE); + uint16_t pixels=xsize*ysize/XBUFF_LEN; + renderer->setAddrWindow(xp,yp,xp+xsize,yp+ysize); + for(int32_t j=0; jpushColors(rbuff,XBUFF_LEN,true); + OsWatchLoop(); + } + renderer->setAddrWindow(0,0,0,0); + free(out_buf); + } + } + } + } + free(mem); + } + fp.close(); + } +#endif // JPEG_PICTS +#endif // ESP32 + } } #endif diff --git a/tasmota/xdsp_10_RA8876.ino b/tasmota/xdsp_10_RA8876.ino index 9c86ff2d7..6a44708cb 100644 --- a/tasmota/xdsp_10_RA8876.ino +++ b/tasmota/xdsp_10_RA8876.ino @@ -71,11 +71,24 @@ void RA8876_InitDriver() fg_color = RA8876_WHITE; bg_color = RA8876_BLACK; +#ifdef ESP32 +#define HW_SPI_MOSI 23 +#define HW_SPI_MISO 19 +#define HW_SPI_CLK 18 +#else +#undef HW_SPI_MOSI +#define HW_SPI_MOSI 13 +#undef HW_SPI_MISO +#define HW_SPI_MISO 12 +#undef HW_SPI_CLK +#define HW_SPI_CLK 14 +#endif + // init renderer, must use hardware spi - if (PinUsed(GPIO_SSPI_CS) && (Pin(GPIO_SSPI_MOSI)==13) && (Pin(GPIO_SSPI_MISO)==12) && (Pin(GPIO_SSPI_SCLK)==14)) { + if (PinUsed(GPIO_SSPI_CS) && (Pin(GPIO_SSPI_MOSI)==HW_SPI_MOSI) && (Pin(GPIO_SSPI_MISO)==HW_SPI_MISO) && (Pin(GPIO_SSPI_SCLK)==HW_SPI_CLK)) { ra8876 = new RA8876(Pin(GPIO_SSPI_CS),Pin(GPIO_SSPI_MOSI),Pin(GPIO_SSPI_MISO),Pin(GPIO_SSPI_SCLK),Pin(GPIO_BACKLIGHT)); } else { - if (PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_MOSI)==13) && (Pin(GPIO_SPI_MISO)==12) && (Pin(GPIO_SPI_CLK)==14)) { + if (PinUsed(GPIO_SPI_CS) && (Pin(GPIO_SPI_MOSI)==HW_SPI_MOSI) && (Pin(GPIO_SPI_MISO)==HW_SPI_MISO) && (Pin(GPIO_SPI_CLK)==HW_SPI_CLK)) { ra8876 = new RA8876(Pin(GPIO_SPI_CS),Pin(GPIO_SPI_MOSI),Pin(GPIO_SPI_MISO),Pin(GPIO_SPI_CLK),Pin(GPIO_BACKLIGHT)); } else { return;