diff --git a/examples/http_stream/stream_huge_jpg b/examples/http_stream/stream_huge_jpg new file mode 100644 index 0000000..7bfcfb4 --- /dev/null +++ b/examples/http_stream/stream_huge_jpg @@ -0,0 +1,102 @@ +// Example for library: +// https://github.com/Bodmer/TJpg_Decoder + +// This example if for ESP32, it renders a Jpeg file +// that is streamed from a http url. The test file +// is way to huge (~18mb) and mainly for showing the limits of the library +// if you scale it down with value 8, it's gonna take around 60s +// shown in it's original size, the stream will take around 10s +// for implementation in real life projects, it's recommended +// to catch images in size of the display + +// Include the jpeg decoder library +#include + +// Include SD +#include + + +// Include the TFT library https://github.com/Bodmer/TFT_eSPI +#include "SPI.h" +#include // Hardware-specific library +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library +const char* ssid ="ssid";// +const char* password = "password";// +char* url = "https://upload.wikimedia.org/wikipedia/commons/0/00/Center_of_the_Milky_Way_Galaxy_IV_%E2%80%93_Composite.jpg"; + + +// This next function will be called during decoding of the jpeg file to +// render each block to the TFT. If you use a different TFT library +// you will need to adapt this function to suit. +bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap) +{ + // Stop further decoding as image is running off bottom of screen + if ( y >= tft.height() ) return 0; + + // This function will clip the image block rendering automatically at the TFT boundaries + tft.pushImage(x, y, w, h, bitmap); + + // This might work instead if you adapt the sketch to use the Adafruit_GFX library + // tft.drawRGBBitmap(x, y, bitmap, w, h); + + // Return 1 to decode next block + return 1; +} + + +void setup() +{ + Serial.begin(115200); + Serial.println("\n\n Testing TJpg_Decoder library"); + + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + // Initialise the TFT + tft.begin(); + tft.setTextColor(0xFFFF, 0x0000); + tft.fillScreen(TFT_BLACK); + tft.setSwapBytes(true); // We need to swap the colour bytes (endianess) + + // The jpeg image can be scaled by a factor of 1, 2, 4, or 8 + TJpgDec.setJpgScale(1); + + // The decoder must be given the exact name of the rendering function above + TJpgDec.setCallback(tft_output); +} + +void loop() +{ + tft.fillScreen(TFT_RED); + + // Time recorded for test purposes + uint32_t t = millis(); + + // Get the width and height in pixels of the jpeg if you wish + uint16_t w = 0, h = 0; + TJpgDec.getJpgSizeFromStream(&w, &h, url); + Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h); + + // Draw the image, top left at 0,0 + TJpgDec.drawJpgFromStream(0 ,0, url); + //TJpgDec.drawSdJpg(0, 0, "/panda.jpg"); + + // How much time did rendering take + t = millis() - t; + Serial.print(t); Serial.println(" ms"); + + // Wait before drawing again + delay(2000); +} diff --git a/examples/http_stream/stream_jpg b/examples/http_stream/stream_jpg new file mode 100644 index 0000000..3d57755 --- /dev/null +++ b/examples/http_stream/stream_jpg @@ -0,0 +1,97 @@ +// Example for library: +// https://github.com/Bodmer/TJpg_Decoder + +// This example if for ESP32, it renders a Jpeg file +// that is streamed from a http url. + +// Include the jpeg decoder library +#include + +// Include SD +#include + + +// Include the TFT library https://github.com/Bodmer/TFT_eSPI +#include "SPI.h" +#include // Hardware-specific library +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library +const char* ssid ="ssid"; +const char* password = "password"; +char* url = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/00/Center_of_the_Milky_Way_Galaxy_IV_%E2%80%93_Composite.jpg/320px-Center_of_the_Milky_Way_Galaxy_IV_%E2%80%93_Composite.jpg"; + + +// This next function will be called during decoding of the jpeg file to +// render each block to the TFT. If you use a different TFT library +// you will need to adapt this function to suit. +bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap) +{ + // Stop further decoding as image is running off bottom of screen + if ( y >= tft.height() ) return 0; + + // This function will clip the image block rendering automatically at the TFT boundaries + tft.pushImage(x, y, w, h, bitmap); + + // This might work instead if you adapt the sketch to use the Adafruit_GFX library + // tft.drawRGBBitmap(x, y, bitmap, w, h); + + // Return 1 to decode next block + return 1; +} + + +void setup() +{ + Serial.begin(115200); + Serial.println("\n\n Testing TJpg_Decoder library"); + + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + // Initialise the TFT + tft.begin(); + tft.setTextColor(0xFFFF, 0x0000); + tft.fillScreen(TFT_BLACK); + tft.setSwapBytes(true); // We need to swap the colour bytes (endianess) + + // The jpeg image can be scaled by a factor of 1, 2, 4, or 8 + TJpgDec.setJpgScale(1); + + // The decoder must be given the exact name of the rendering function above + TJpgDec.setCallback(tft_output); +} + +void loop() +{ + tft.fillScreen(TFT_RED); + + // Time recorded for test purposes + uint32_t t = millis(); + + // Get the width and height in pixels of the jpeg if you wish + uint16_t w = 0, h = 0; + TJpgDec.getJpgSizeFromStream(&w, &h, url); + Serial.print("Width = "); Serial.print(w); Serial.print(", height = "); Serial.println(h); + + // Draw the image, top left at 0,0 + TJpgDec.drawJpgFromStream(0 ,0, url); + //TJpgDec.drawSdJpg(0, 0, "/panda.jpg"); + + // How much time did rendering take + t = millis() - t; + Serial.print(t); Serial.println(" ms"); + + // Wait before drawing again + delay(2000); +} diff --git a/src/TJpg_Decoder.cpp b/src/TJpg_Decoder.cpp index 1c6269a..114e661 100644 --- a/src/TJpg_Decoder.cpp +++ b/src/TJpg_Decoder.cpp @@ -129,6 +129,29 @@ unsigned int TJpg_Decoder::jd_input(JDEC* jdec, uint8_t* buf, unsigned int len) } } #endif + +#ifdef TJPGD_LOAD_HTTP_LIBRARY + else if (thisPtr->jpg_source == TJPG_STREAM_FILE) + { + if (thisPtr->array_index + len > thisPtr->array_size) + { + len = thisPtr->array_size - thisPtr->array_index; + } + + if(thisPtr->jpg_http->connected()) + { + uint8_t _buff[len]; + WiFiClient* const stream = thisPtr->jpg_http->getStreamPtr(); + if (stream->available()) stream->readBytes(_buff, len); + yield(); + if (buf) + { + memcpy_P(buf,_buff,len); + } + thisPtr->array_index = thisPtr->array_index + len; + } + } +#endif return len; } @@ -156,6 +179,101 @@ int TJpg_Decoder::jd_output(JDEC* jdec, void* bitmap, JRECT* jrect) } +#if defined (TJPGD_LOAD_HTTP_LIBRARY) + +/*************************************************************************************** +** Function name: drawJpg +** Description: Draw a named jpg file at x,y (name in char array) +***************************************************************************************/ + +JRESULT TJpg_Decoder::drawJpgFromStream(int32_t x, int32_t y, char* _url) { + JDEC jdec; + JRESULT jresult = JDR_OK; + + jpg_source = TJPG_STREAM_FILE; + + jpeg_x = x; + jpeg_y = y; + array_size = 0; + array_index = 0; + + jdec.swap = _swap; + jpg_http = new HTTPClient; + jpg_http->begin(_url); + long httpCode = jpg_http->GET(); + if (httpCode == HTTP_CODE_OK) + { + array_size = jpg_http->getSize(); + Serial.println(array_size); + array_index = 0; + } + + // Analyse input data + jresult = jd_prepare(&jdec, jd_input, workspace, TJPGD_WORKSPACE_SIZE, 0); + + // Extract image and render + if (jresult == JDR_OK)jresult = jd_decomp(&jdec, jd_output, jpgScale); + + if(jpg_http->connected()) + { + WiFiClient* const stream = jpg_http->getStreamPtr(); + stream->stop(); + stream->flush(); + } + jpg_http->end(); + delete jpg_http; + jpg_http = NULL; + + return jresult; +} + +/*************************************************************************************** +** Function name: getJpgSizeFromStream +** Description: Get width and height of a jpg file (name in char array) +***************************************************************************************/ +// Generic file call for SD or SPIFFS, uses leading / to distinguish SPIFFS files +JRESULT TJpg_Decoder::getJpgSizeFromStream(uint16_t *w, uint16_t *h, char *_url){ + + JDEC jdec; + JRESULT jresult = JDR_OK; + + *w = 0; + *h = 0; + array_index = 0; + + jpg_source = TJPG_STREAM_FILE; + + if(!jpg_http||!jpg_http->connected()){ +Serial.println("get size"); + jpg_http = new HTTPClient; + jpg_http->begin(_url); + long httpCode = jpg_http->GET(); + if (httpCode == HTTP_CODE_OK) + { + array_size = jpg_http->getSize(); + } + jresult = jd_prepare(&jdec, jd_input, workspace, TJPGD_WORKSPACE_SIZE, 0); + + if (jresult == JDR_OK) { + *w = jdec.width; + *h = jdec.height; + } + + if(jpg_http->connected()) + { + WiFiClient* const stream = jpg_http->getStreamPtr(); + stream->stop(); + stream->flush(); + } + jpg_http->end(); + delete jpg_http; + jpg_http = NULL; + } + return jresult; +} + +#endif + #if defined (TJPGD_LOAD_SD_LIBRARY) || defined (TJPGD_LOAD_FFS) /*************************************************************************************** diff --git a/src/TJpg_Decoder.h b/src/TJpg_Decoder.h index f72cf35..ef52490 100644 --- a/src/TJpg_Decoder.h +++ b/src/TJpg_Decoder.h @@ -17,7 +17,12 @@ Latest version here: #include "Arduino.h" #include "tjpgd.h" - #if defined (ESP8266) || defined (ESP32) + #if defined (ARDUINO_ARCH_ESP8266) || defined (ESP32) + #if defined (TJPGD_LOAD_HTTP_LIBRARY) + #include + #include + #endif + #include #include #include @@ -39,7 +44,8 @@ Latest version here: enum { TJPG_ARRAY = 0, TJPG_FS_FILE, - TJPG_SD_FILE + TJPG_SD_FILE, + TJPG_STREAM_FILE }; //------------------------------------------------------------------------------ @@ -56,6 +62,10 @@ class TJpg_Decoder { #ifdef TJPGD_LOAD_FFS fs::File jpgFile; #endif + +#if defined (TJPGD_LOAD_HTTP_LIBRARY) + HTTPClient* jpg_http = NULL; +#endif public: @@ -76,13 +86,18 @@ class TJpg_Decoder { JRESULT getJpgSize(uint16_t *w, uint16_t *h, const char *pFilename); JRESULT getJpgSize(uint16_t *w, uint16_t *h, const String& pFilename); #endif + +#if defined (TJPGD_LOAD_HTTP_LIBRARY) + JRESULT drawJpgFromStream(int32_t x, int32_t y, char* url) ; + JRESULT getJpgSizeFromStream(uint16_t *w, uint16_t *h, char* url); +#endif #if defined (TJPGD_LOAD_SD_LIBRARY) JRESULT drawSdJpg (int32_t x, int32_t y, const char *pFilename); JRESULT drawSdJpg (int32_t x, int32_t y, const String& pFilename); JRESULT drawSdJpg (int32_t x, int32_t y, File inFile); - JRESULT getSdJpgSize(uint16_t *w, uint16_t *h, const char *pFilename); + JRESULT getSdJpgSize(uint16_t *w, uint16_t *h, const char *pFilename); JRESULT getSdJpgSize(uint16_t *w, uint16_t *h, const String& pFilename); JRESULT getSdJpgSize(uint16_t *w, uint16_t *h, File inFile); #endif diff --git a/src/User_Config.h b/src/User_Config.h index 0920fc8..faa2ecb 100644 --- a/src/User_Config.h +++ b/src/User_Config.h @@ -3,3 +3,4 @@ #endif #define TJPGD_LOAD_SD_LIBRARY +#define TJPGD_LOAD_HTTP_LIBRARY