acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

ESP32_SDcard_jpeg.ino (9558B)

      1 // This sketch if for an ESP32, it draws Jpeg images pulled from an SD Card
      2 // onto the TFT.
      3 
      4 // As well as the TFT_eSPI library you will need the JPEG Decoder library.
      5 // A copy can be downloaded here, it is based on the library by Makoto Kurauchi.
      6 // https://github.com/Bodmer/JPEGDecoder
      7 
      8 // Images on SD Card must be put in the root folder (top level) to be found
      9 // Use the SD library examples to verify your SD Card interface works!
     10 
     11 // The example images used to test this sketch can be found in the library
     12 // JPEGDecoder/extras folder
     13 //----------------------------------------------------------------------------------------------------
     14 
     15 #include <SPI.h>
     16 
     17 #include <FS.h>
     18 #include <SD.h>
     19 
     20 #include <TFT_eSPI.h>
     21 TFT_eSPI tft = TFT_eSPI();
     22 
     23 // JPEG decoder library
     24 #include <JPEGDecoder.h>
     25 
     26 //####################################################################################################
     27 // Setup
     28 //####################################################################################################
     29 void setup() {
     30   Serial.begin(115200);
     31 
     32   // Set all chip selects high to avoid bus contention during initialisation of each peripheral
     33   digitalWrite(22, HIGH); // Touch controller chip select (if used)
     34   digitalWrite(15, HIGH); // TFT screen chip select
     35   digitalWrite( 5, HIGH); // SD card chips select, must use GPIO 5 (ESP32 SS)
     36 
     37   tft.begin();
     38 
     39   if (!SD.begin()) {
     40     Serial.println("Card Mount Failed");
     41     return;
     42   }
     43   uint8_t cardType = SD.cardType();
     44 
     45   if (cardType == CARD_NONE) {
     46     Serial.println("No SD card attached");
     47     return;
     48   }
     49 
     50   Serial.print("SD Card Type: ");
     51   if (cardType == CARD_MMC) {
     52     Serial.println("MMC");
     53   } else if (cardType == CARD_SD) {
     54     Serial.println("SDSC");
     55   } else if (cardType == CARD_SDHC) {
     56     Serial.println("SDHC");
     57   } else {
     58     Serial.println("UNKNOWN");
     59   }
     60 
     61   uint64_t cardSize = SD.cardSize() / (1024 * 1024);
     62   Serial.printf("SD Card Size: %lluMB\n", cardSize);
     63 
     64   Serial.println("initialisation done.");
     65 }
     66 
     67 //####################################################################################################
     68 // Main loop
     69 //####################################################################################################
     70 void loop() {
     71 
     72   tft.setRotation(2);  // portrait
     73   tft.fillScreen(random(0xFFFF));
     74 
     75   // The image is 300 x 300 pixels so we do some sums to position image in the middle of the screen!
     76   // Doing this by reading the image width and height from the jpeg info is left as an exercise!
     77   int x = (tft.width()  - 300) / 2 - 1;
     78   int y = (tft.height() - 300) / 2 - 1;
     79 
     80   drawSdJpeg("/EagleEye.jpg", x, y);     // This draws a jpeg pulled off the SD Card
     81   delay(2000);
     82 
     83   tft.setRotation(2);  // portrait
     84   tft.fillScreen(random(0xFFFF));
     85   drawSdJpeg("/Baboon40.jpg", 0, 0);     // This draws a jpeg pulled off the SD Card
     86   delay(2000);
     87 
     88   tft.setRotation(2);  // portrait
     89   tft.fillScreen(random(0xFFFF));
     90   drawSdJpeg("/lena20k.jpg", 0, 0);     // This draws a jpeg pulled off the SD Card
     91   delay(2000);
     92 
     93   tft.setRotation(1);  // landscape
     94   tft.fillScreen(random(0xFFFF));
     95   drawSdJpeg("/Mouse480.jpg", 0, 0);     // This draws a jpeg pulled off the SD Card
     96 
     97   delay(2000);
     98 
     99   while(1); // Wait here
    100 }
    101 
    102 //####################################################################################################
    103 // Draw a JPEG on the TFT pulled from SD Card
    104 //####################################################################################################
    105 // xpos, ypos is top left corner of plotted image
    106 void drawSdJpeg(const char *filename, int xpos, int ypos) {
    107 
    108   // Open the named file (the Jpeg decoder library will close it)
    109   File jpegFile = SD.open( filename, FILE_READ);  // or, file handle reference for SD library
    110  
    111   if ( !jpegFile ) {
    112     Serial.print("ERROR: File \""); Serial.print(filename); Serial.println ("\" not found!");
    113     return;
    114   }
    115 
    116   Serial.println("===========================");
    117   Serial.print("Drawing file: "); Serial.println(filename);
    118   Serial.println("===========================");
    119 
    120   // Use one of the following methods to initialise the decoder:
    121   bool decoded = JpegDec.decodeSdFile(jpegFile);  // Pass the SD file handle to the decoder,
    122   //bool decoded = JpegDec.decodeSdFile(filename);  // or pass the filename (String or character array)
    123 
    124   if (decoded) {
    125     // print information about the image to the serial port
    126     jpegInfo();
    127     // render the image onto the screen at given coordinates
    128     jpegRender(xpos, ypos);
    129   }
    130   else {
    131     Serial.println("Jpeg file format not supported!");
    132   }
    133 }
    134 
    135 //####################################################################################################
    136 // Draw a JPEG on the TFT, images will be cropped on the right/bottom sides if they do not fit
    137 //####################################################################################################
    138 // This function assumes xpos,ypos is a valid screen coordinate. For convenience images that do not
    139 // fit totally on the screen are cropped to the nearest MCU size and may leave right/bottom borders.
    140 void jpegRender(int xpos, int ypos) {
    141 
    142   //jpegInfo(); // Print information from the JPEG file (could comment this line out)
    143 
    144   uint16_t *pImg;
    145   uint16_t mcu_w = JpegDec.MCUWidth;
    146   uint16_t mcu_h = JpegDec.MCUHeight;
    147   uint32_t max_x = JpegDec.width;
    148   uint32_t max_y = JpegDec.height;
    149 
    150   bool swapBytes = tft.getSwapBytes();
    151   tft.setSwapBytes(true);
    152   
    153   // Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs)
    154   // Typically these MCUs are 16x16 pixel blocks
    155   // Determine the width and height of the right and bottom edge image blocks
    156   uint32_t min_w = jpg_min(mcu_w, max_x % mcu_w);
    157   uint32_t min_h = jpg_min(mcu_h, max_y % mcu_h);
    158 
    159   // save the current image block size
    160   uint32_t win_w = mcu_w;
    161   uint32_t win_h = mcu_h;
    162 
    163   // record the current time so we can measure how long it takes to draw an image
    164   uint32_t drawTime = millis();
    165 
    166   // save the coordinate of the right and bottom edges to assist image cropping
    167   // to the screen size
    168   max_x += xpos;
    169   max_y += ypos;
    170 
    171   // Fetch data from the file, decode and display
    172   while (JpegDec.read()) {    // While there is more data in the file
    173     pImg = JpegDec.pImage ;   // Decode a MCU (Minimum Coding Unit, typically a 8x8 or 16x16 pixel block)
    174 
    175     // Calculate coordinates of top left corner of current MCU
    176     int mcu_x = JpegDec.MCUx * mcu_w + xpos;
    177     int mcu_y = JpegDec.MCUy * mcu_h + ypos;
    178 
    179     // check if the image block size needs to be changed for the right edge
    180     if (mcu_x + mcu_w <= max_x) win_w = mcu_w;
    181     else win_w = min_w;
    182 
    183     // check if the image block size needs to be changed for the bottom edge
    184     if (mcu_y + mcu_h <= max_y) win_h = mcu_h;
    185     else win_h = min_h;
    186 
    187     // copy pixels into a contiguous block
    188     if (win_w != mcu_w)
    189     {
    190       uint16_t *cImg;
    191       int p = 0;
    192       cImg = pImg + win_w;
    193       for (int h = 1; h < win_h; h++)
    194       {
    195         p += mcu_w;
    196         for (int w = 0; w < win_w; w++)
    197         {
    198           *cImg = *(pImg + w + p);
    199           cImg++;
    200         }
    201       }
    202     }
    203 
    204     // calculate how many pixels must be drawn
    205     uint32_t mcu_pixels = win_w * win_h;
    206 
    207     // draw image MCU block only if it will fit on the screen
    208     if (( mcu_x + win_w ) <= tft.width() && ( mcu_y + win_h ) <= tft.height())
    209       tft.pushImage(mcu_x, mcu_y, win_w, win_h, pImg);
    210     else if ( (mcu_y + win_h) >= tft.height())
    211       JpegDec.abort(); // Image has run off bottom of screen so abort decoding
    212   }
    213 
    214   tft.setSwapBytes(swapBytes);
    215 
    216   showTime(millis() - drawTime); // These lines are for sketch testing only
    217 }
    218 
    219 //####################################################################################################
    220 // Print image information to the serial port (optional)
    221 //####################################################################################################
    222 // JpegDec.decodeFile(...) or JpegDec.decodeArray(...) must be called before this info is available!
    223 void jpegInfo() {
    224 
    225   // Print information extracted from the JPEG file
    226   Serial.println("JPEG image info");
    227   Serial.println("===============");
    228   Serial.print("Width      :");
    229   Serial.println(JpegDec.width);
    230   Serial.print("Height     :");
    231   Serial.println(JpegDec.height);
    232   Serial.print("Components :");
    233   Serial.println(JpegDec.comps);
    234   Serial.print("MCU / row  :");
    235   Serial.println(JpegDec.MCUSPerRow);
    236   Serial.print("MCU / col  :");
    237   Serial.println(JpegDec.MCUSPerCol);
    238   Serial.print("Scan type  :");
    239   Serial.println(JpegDec.scanType);
    240   Serial.print("MCU width  :");
    241   Serial.println(JpegDec.MCUWidth);
    242   Serial.print("MCU height :");
    243   Serial.println(JpegDec.MCUHeight);
    244   Serial.println("===============");
    245   Serial.println("");
    246 }
    247 
    248 //####################################################################################################
    249 // Show the execution time (optional)
    250 //####################################################################################################
    251 // WARNING: for UNO/AVR legacy reasons printing text to the screen with the Mega might not work for
    252 // sketch sizes greater than ~70KBytes because 16 bit address pointers are used in some libraries.
    253 
    254 // The Due will work fine with the HX8357_Due library.
    255 
    256 void showTime(uint32_t msTime) {
    257   //tft.setCursor(0, 0);
    258   //tft.setTextFont(1);
    259   //tft.setTextSize(2);
    260   //tft.setTextColor(TFT_WHITE, TFT_BLACK);
    261   //tft.print(F(" JPEG drawn in "));
    262   //tft.print(msTime);
    263   //tft.println(F(" ms "));
    264   Serial.print(F(" JPEG drawn in "));
    265   Serial.print(msTime);
    266   Serial.println(F(" ms "));
    267 }
    268