Просмотр исходного кода

SD & Spiffs buffered drawing option

ZinggJM 8 лет назад
Родитель
Сommit
99284cc74d

+ 4 - 1
README.md

@@ -33,7 +33,10 @@ New Simplified Version of GxEPD for Arduino
 - Waveshare e-paper displays with SPI: http://forum.arduino.cc/index.php?topic=487007.0
 - Waveshare e-paper displays with SPI: http://forum.arduino.cc/index.php?topic=487007.0
 - Good Dispay ePaper for Arduino : https://forum.arduino.cc/index.php?topic=436411.0
 - Good Dispay ePaper for Arduino : https://forum.arduino.cc/index.php?topic=436411.0
 
 
-### Version 1.0.4
+### Version 1.0.5
+- add buffered drawing option to GxEPD2_SD_Example and GxEPD2_Spiffs_Example
+- allows use with 1.54" 3-color 200x200 display, may also be useful for small bitmaps
+#### Version 1.0.4
 - add GxEPD2_GFX base class support (optional, selectable, uses slightly more code)
 - add GxEPD2_GFX base class support (optional, selectable, uses slightly more code)
 - base class GxEPD2_GFX can be used to pass references or pointers to the display instance as parameter
 - base class GxEPD2_GFX can be used to pass references or pointers to the display instance as parameter
 - add GxEPD2_GFX_MultiDisplayExample, uses  GxEPD2_GFX reference parameter
 - add GxEPD2_GFX_MultiDisplayExample, uses  GxEPD2_GFX reference parameter

+ 279 - 12
examples/GxEPD2_SD_Example/GxEPD2_SD_Example.ino

@@ -74,7 +74,7 @@ SdFat SD;
 //GxEPD2_BW < GxEPD2_583, GxEPD2_583::HEIGHT / 2 > display(GxEPD2_583(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW < GxEPD2_583, GxEPD2_583::HEIGHT / 2 > display(GxEPD2_583(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW < GxEPD2_750, GxEPD2_750::HEIGHT / 2 > display(GxEPD2_750(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW < GxEPD2_750, GxEPD2_750::HEIGHT / 2 > display(GxEPD2_750(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 // 3-color e-papers
 // 3-color e-papers
-// !!! can't be used for this // GxEPD2_3C<GxEPD2_154c, GxEPD2_154c::HEIGHT> display(GxEPD2_154c(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
+//GxEPD2_3C<GxEPD2_154c, GxEPD2_154c::HEIGHT> display(GxEPD2_154c(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_3C<GxEPD2_213c, GxEPD2_213c::HEIGHT> display(GxEPD2_213c(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_3C<GxEPD2_213c, GxEPD2_213c::HEIGHT> display(GxEPD2_213c(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_3C<GxEPD2_290c, GxEPD2_290c::HEIGHT> display(GxEPD2_290c(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_3C<GxEPD2_290c, GxEPD2_290c::HEIGHT> display(GxEPD2_290c(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_3C<GxEPD2_270c, GxEPD2_270c::HEIGHT> display(GxEPD2_270c(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_3C<GxEPD2_270c, GxEPD2_270c::HEIGHT> display(GxEPD2_270c(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
@@ -95,7 +95,7 @@ SdFat SD;
 //GxEPD2_BW < GxEPD2_583, GxEPD2_583::HEIGHT / 2 > display(GxEPD2_583(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
 //GxEPD2_BW < GxEPD2_583, GxEPD2_583::HEIGHT / 2 > display(GxEPD2_583(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
 //GxEPD2_BW < GxEPD2_750, GxEPD2_750::HEIGHT / 2 > display(GxEPD2_750(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
 //GxEPD2_BW < GxEPD2_750, GxEPD2_750::HEIGHT / 2 > display(GxEPD2_750(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
 // 3-color e-papers
 // 3-color e-papers
-// !!! can't be used for this //GxEPD2_3C<GxEPD2_154c, GxEPD2_154c::HEIGHT> display(GxEPD2_154c(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
+//GxEPD2_3C<GxEPD2_154c, GxEPD2_154c::HEIGHT> display(GxEPD2_154c(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
 //GxEPD2_3C<GxEPD2_213c, GxEPD2_213c::HEIGHT> display(GxEPD2_213c(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
 //GxEPD2_3C<GxEPD2_213c, GxEPD2_213c::HEIGHT> display(GxEPD2_213c(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
 //GxEPD2_3C<GxEPD2_290c, GxEPD2_290c::HEIGHT> display(GxEPD2_290c(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
 //GxEPD2_3C<GxEPD2_290c, GxEPD2_290c::HEIGHT> display(GxEPD2_290c(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
 //GxEPD2_3C<GxEPD2_270c, GxEPD2_270c::HEIGHT> display(GxEPD2_270c(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
 //GxEPD2_3C<GxEPD2_270c, GxEPD2_270c::HEIGHT> display(GxEPD2_270c(/*CS=15*/ SS, /*DC=4*/ 4, /*RST=5*/ 5, /*BUSY=16*/ 16));
@@ -117,7 +117,7 @@ SdFat SD;
 //GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> display(GxEPD2_420(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
 //GxEPD2_BW<GxEPD2_420, GxEPD2_420::HEIGHT> display(GxEPD2_420(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
 //GxEPD2_BW<GxEPD2_750, GxEPD2_750::HEIGHT> display(GxEPD2_750(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
 //GxEPD2_BW<GxEPD2_750, GxEPD2_750::HEIGHT> display(GxEPD2_750(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
 // 3-color e-papers
 // 3-color e-papers
-// !!! can't be used for this //GxEPD2_3C<GxEPD2_154c, GxEPD2_154c::HEIGHT> display(GxEPD2_154c(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
+//GxEPD2_3C<GxEPD2_154c, GxEPD2_154c::HEIGHT> display(GxEPD2_154c(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
 //GxEPD2_3C<GxEPD2_213c, GxEPD2_213c::HEIGHT> display(GxEPD2_213c(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
 //GxEPD2_3C<GxEPD2_213c, GxEPD2_213c::HEIGHT> display(GxEPD2_213c(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
 //GxEPD2_3C<GxEPD2_290c, GxEPD2_290c::HEIGHT> display(GxEPD2_290c(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
 //GxEPD2_3C<GxEPD2_290c, GxEPD2_290c::HEIGHT> display(GxEPD2_290c(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
 //GxEPD2_3C<GxEPD2_270c, GxEPD2_270c::HEIGHT> display(GxEPD2_270c(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
 //GxEPD2_3C<GxEPD2_270c, GxEPD2_270c::HEIGHT> display(GxEPD2_270c(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
@@ -139,7 +139,7 @@ SdFat SD;
 //GxEPD2_BW<GxEPD2_750, MAX_HEIGHT(GxEPD2_750)> display(GxEPD2_750(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
 //GxEPD2_BW<GxEPD2_750, MAX_HEIGHT(GxEPD2_750)> display(GxEPD2_750(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
 // 3-color e-papers
 // 3-color e-papers
 #define MAX_HEIGHT_3C(EPD) (EPD::HEIGHT <= (MAX_DISPAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
 #define MAX_HEIGHT_3C(EPD) (EPD::HEIGHT <= (MAX_DISPAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8) ? EPD::HEIGHT : (MAX_DISPAY_BUFFER_SIZE / 2) / (EPD::WIDTH / 8))
-// !!! can't be used for this //GxEPD2_3C<GxEPD2_154c, MAX_HEIGHT_3C(GxEPD2_154c)> display(GxEPD2_154c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
+//GxEPD2_3C<GxEPD2_154c, MAX_HEIGHT_3C(GxEPD2_154c)> display(GxEPD2_154c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
 //GxEPD2_3C<GxEPD2_213c, MAX_HEIGHT_3C(GxEPD2_213c)> display(GxEPD2_213c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
 //GxEPD2_3C<GxEPD2_213c, MAX_HEIGHT_3C(GxEPD2_213c)> display(GxEPD2_213c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
 //GxEPD2_3C<GxEPD2_290c, MAX_HEIGHT_3C(GxEPD2_290c)> display(GxEPD2_290c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
 //GxEPD2_3C<GxEPD2_290c, MAX_HEIGHT_3C(GxEPD2_290c)> display(GxEPD2_290c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
 //GxEPD2_3C<GxEPD2_270c, MAX_HEIGHT_3C(GxEPD2_270c)> display(GxEPD2_270c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
 //GxEPD2_3C<GxEPD2_270c, MAX_HEIGHT_3C(GxEPD2_270c)> display(GxEPD2_270c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
@@ -158,11 +158,16 @@ SdFat SD;
 // function declaration with default parameter
 // function declaration with default parameter
 void drawBitmapFromSD(const char *filename, int16_t x, int16_t y, bool with_color = true);
 void drawBitmapFromSD(const char *filename, int16_t x, int16_t y, bool with_color = true);
 
 
+// bitmap drawing using buffered graphics, e.g. for small bitmaps or for GxEPD2_154c
+// partial_update selects refresh mode (not effective for GxEPD2_154c)
+// overwrite = true does not clear buffer before drawing, use only if buffer is full height
+void drawBitmapFromSD_Buffered(const char *filename, int16_t x, int16_t y, bool with_color = true, bool partial_update = false, bool overwrite = false);
+
 void setup()
 void setup()
 {
 {
   Serial.begin(115200);
   Serial.begin(115200);
   Serial.println();
   Serial.println();
-  Serial.println("setup");
+  Serial.println("GxEPD2_SD_Example");
 
 
   display.init(115200);
   display.init(115200);
 
 
@@ -174,10 +179,21 @@ void setup()
   }
   }
   Serial.println("SD OK!");
   Serial.println("SD OK!");
 
 
-  drawBitmaps_200x200();
-  drawBitmaps_other();
+  if ((display.epd2.panel == GxEPD2::GDEW0154Z04) || false)
+  {
+    drawBitmapsBuffered_200x200();
+    drawBitmapsBuffered_other();
+  }
+  else
+  {
+    drawBitmaps_200x200();
+    drawBitmaps_other();
+  }
 
 
   //drawBitmaps_test();
   //drawBitmaps_test();
+  //drawBitmapsBuffered_test();
+
+  Serial.println("GxEPD2_SD_Example done");
 }
 }
 
 
 void loop(void)
 void loop(void)
@@ -248,6 +264,76 @@ void drawBitmaps_test()
   int16_t h2 = display.height() / 2;
   int16_t h2 = display.height() / 2;
   drawBitmapFromSD("betty_4.bmp", w2 - 102, h2 - 126);
   drawBitmapFromSD("betty_4.bmp", w2 - 102, h2 - 126);
   delay(2000);
   delay(2000);
+  drawBitmapFromSD("bb4.bmp", 0, 0);
+  delay(2000);
+}
+
+void drawBitmapsBuffered_200x200()
+{
+  int16_t x = (display.width() - 200) / 2;
+  int16_t y = (display.height() - 200) / 2;
+  drawBitmapFromSD_Buffered("logo200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSD_Buffered("first200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSD_Buffered("second200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSD_Buffered("third200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSD_Buffered("fourth200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSD_Buffered("fifth200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSD_Buffered("sixth200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSD_Buffered("seventh200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSD_Buffered("eighth200x200.bmp", x, y);
+  delay(2000);
+}
+
+void drawBitmapsBuffered_other()
+{
+  int16_t w2 = display.width() / 2;
+  int16_t h2 = display.height() / 2;
+  drawBitmapFromSD_Buffered("parrot.bmp", w2 - 64, h2 - 80);
+  delay(2000);
+  drawBitmapFromSD_Buffered("betty_1.bmp", w2 - 100, h2 - 160);
+  delay(2000);
+  drawBitmapFromSD_Buffered("betty_4.bmp", w2 - 102, h2 - 126);
+  delay(2000);
+  drawBitmapFromSD_Buffered("marilyn_240x240x8.bmp", w2 - 120, h2 - 120);
+  delay(2000);
+  drawBitmapFromSD_Buffered("miniwoof.bmp", w2 - 60, h2 - 80);
+  delay(2000);
+  drawBitmapFromSD_Buffered("t200x200.bmp", w2 - 100, h2 - 100);
+  delay(2000);
+  drawBitmapFromSD_Buffered("test.bmp", w2 - 120, h2 - 160);
+  delay(2000);
+  drawBitmapFromSD_Buffered("tiger.bmp", w2 - 160, h2 - 120);
+  delay(2000);
+  drawBitmapFromSD_Buffered("tiger_178x160x4.bmp", w2 - 89, h2 - 80);
+  delay(2000);
+  drawBitmapFromSD_Buffered("tiger_240x317x4.bmp", w2 - 120, h2 - 160);
+  delay(2000);
+  drawBitmapFromSD_Buffered("tiger_320x200x24.bmp", w2 - 160, h2 - 100);
+  delay(2000);
+  drawBitmapFromSD_Buffered("tiger16T.bmp", w2 - 160, h2 - 120);
+  delay(2000);
+  drawBitmapFromSD_Buffered("woof.bmp", w2 - 120, h2 - 160);
+  delay(2000);
+  drawBitmapFromSD_Buffered("bitmap640x384_1.bmp", 0, 0);
+  delay(2000);
+}
+
+void drawBitmapsBuffered_test()
+{
+  int16_t w2 = display.width() / 2;
+  int16_t h2 = display.height() / 2;
+  drawBitmapFromSD_Buffered("betty_4.bmp", w2 - 102, h2 - 126);
+  delay(2000);
+  drawBitmapFromSD_Buffered("bb4.bmp", 0, 0, false, true, true);
+  delay(2000);
 }
 }
 
 
 static const uint16_t input_buffer_pixels = 20; // may affect performance
 static const uint16_t input_buffer_pixels = 20; // may affect performance
@@ -310,6 +396,7 @@ void drawBitmapFromSD(const char *filename, int16_t x, int16_t y, bool with_colo
       Serial.println(height);
       Serial.println(height);
       // BMP rows are padded (if needed) to 4-byte boundary
       // BMP rows are padded (if needed) to 4-byte boundary
       uint32_t rowSize = (width * depth / 8 + 3) & ~3;
       uint32_t rowSize = (width * depth / 8 + 3) & ~3;
+      if (depth < 8) rowSize = ((width * depth + 8 - depth) / 8 + 3) & ~3;
       if (height < 0)
       if (height < 0)
       {
       {
         height = -height;
         height = -height;
@@ -415,18 +502,14 @@ void drawBitmapFromSD(const char *filename, int16_t x, int16_t y, bool with_colo
             }
             }
             if (whitish)
             if (whitish)
             {
             {
-              //out_byte |= 0x80 >> col % 8; // not black
-              //out_color_byte |= 0x80 >> col % 8; // not colored
               // keep white
               // keep white
             }
             }
             else if (colored && with_color)
             else if (colored && with_color)
             {
             {
-              //out_byte |= 0x80 >> col % 8; // not black
               out_color_byte &= ~(0x80 >> col % 8); // colored
               out_color_byte &= ~(0x80 >> col % 8); // colored
             }
             }
             else
             else
             {
             {
-              //out_color_byte |= 0x80 >> col % 8; // not colored
               out_byte &= ~(0x80 >> col % 8); // black
               out_byte &= ~(0x80 >> col % 8); // black
             }
             }
             if ((7 == col % 8) || (col == w - 1)) // write that last byte! (for w%8!=0 boarder)
             if ((7 == col % 8) || (col == w - 1)) // write that last byte! (for w%8!=0 boarder)
@@ -445,7 +528,191 @@ void drawBitmapFromSD(const char *filename, int16_t x, int16_t y, bool with_colo
       }
       }
     }
     }
   }
   }
-  //Serial.print("end curPosition  "); Serial.println(file.curPosition());
+  file.close();
+  if (!valid)
+  {
+    Serial.println("bitmap format not handled.");
+  }
+}
+
+void drawBitmapFromSD_Buffered(const char *filename, int16_t x, int16_t y, bool with_color, bool partial_update, bool overwrite)
+{
+  SdFile file;
+  bool valid = false; // valid format to be handled
+  bool flip = true; // bitmap is stored bottom-to-top
+  uint32_t startTime = millis();
+  if ((x >= display.width()) || (y >= display.height())) return;
+  Serial.println();
+  Serial.print("Loading image '");
+  Serial.print(filename);
+  Serial.println('\'');
+#if defined(ESP32)
+  file = SD.open(String("/") + filename, FILE_READ);
+  if (!file)
+  {
+    Serial.print("File not found");
+    return;
+  }
+#else
+  if (!file.open(filename, FILE_READ))
+  {
+    Serial.print("File not found");
+    return;
+  }
+#endif
+  // Parse BMP header
+  if (read16(file) == 0x4D42) // BMP signature
+  {
+    uint32_t fileSize = read32(file);
+    uint32_t creatorBytes = read32(file);
+    uint32_t imageOffset = read32(file); // Start of image data
+    uint32_t headerSize = read32(file);
+    uint32_t width  = read32(file);
+    uint32_t height = read32(file);
+    uint16_t planes = read16(file);
+    uint16_t depth = read16(file); // bits per pixel
+    uint32_t format = read32(file);
+    if ((planes == 1) && ((format == 0) || (format == 3))) // uncompressed is handled, 565 also
+    {
+      Serial.print("File size: "); Serial.println(fileSize);
+      Serial.print("Image Offset: "); Serial.println(imageOffset);
+      Serial.print("Header size: "); Serial.println(headerSize);
+      Serial.print("Bit Depth: "); Serial.println(depth);
+      Serial.print("Image size: ");
+      Serial.print(width);
+      Serial.print('x');
+      Serial.println(height);
+      // BMP rows are padded (if needed) to 4-byte boundary
+      uint32_t rowSize = (width * depth / 8 + 3) & ~3;
+      if (depth < 8) rowSize = ((width * depth + 8 - depth) / 8 + 3) & ~3;
+      if (height < 0)
+      {
+        height = -height;
+        flip = false;
+      }
+      uint16_t w = width;
+      uint16_t h = height;
+      if ((x + w - 1) >= display.width())  w = display.width()  - x;
+      if ((y + h - 1) >= display.height()) h = display.height() - y;
+      //if (w <= max_row_width) // handle with direct drawing
+      {
+        valid = true;
+        uint8_t bitmask = 0xFF;
+        uint8_t bitshift = 8 - depth;
+        uint16_t red, green, blue;
+        bool whitish, colored;
+        if (depth == 1) with_color = false;
+        if (depth <= 8)
+        {
+          if (depth < 8) bitmask >>= depth;
+          file.seekSet(54); //palette is always @ 54
+          for (uint16_t pn = 0; pn < (1 << depth); pn++)
+          {
+            blue  = file.read();
+            green = file.read();
+            red   = file.read();
+            file.read();
+            whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
+            colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
+            if (0 == pn % 8) mono_palette_buffer[pn / 8] = 0;
+            mono_palette_buffer[pn / 8] |= whitish << pn % 8;
+            if (0 == pn % 8) color_palette_buffer[pn / 8] = 0;
+            color_palette_buffer[pn / 8] |= colored << pn % 8;
+          }
+        }
+        if (partial_update) display.setPartialWindow(x, y, w, h);
+        else display.setFullWindow();
+        display.firstPage();
+        do
+        {
+          if (!overwrite) display.fillScreen(GxEPD_WHITE);
+          uint32_t rowPosition = flip ? imageOffset + (height - h) * rowSize : imageOffset;
+          for (uint16_t row = 0; row < h; row++, rowPosition += rowSize) // for each line
+          {
+            uint32_t in_remain = rowSize;
+            uint32_t in_idx = 0;
+            uint32_t in_bytes = 0;
+            uint8_t in_byte = 0; // for depth <= 8
+            uint8_t in_bits = 0; // for depth <= 8
+            uint16_t color = GxEPD_WHITE;
+            file.seekSet(rowPosition);
+            for (uint16_t col = 0; col < w; col++) // for each pixel
+            {
+              // Time to read more pixel data?
+              if (in_idx >= in_bytes) // ok, exact match for 24bit also (size IS multiple of 3)
+              {
+                in_bytes = file.read(input_buffer, in_remain > sizeof(input_buffer) ? sizeof(input_buffer) : in_remain);
+                in_remain -= in_bytes;
+                in_idx = 0;
+              }
+              switch (depth)
+              {
+                case 24:
+                  blue = input_buffer[in_idx++];
+                  green = input_buffer[in_idx++];
+                  red = input_buffer[in_idx++];
+                  whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
+                  colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
+                  break;
+                case 16:
+                  {
+                    uint8_t lsb = input_buffer[in_idx++];
+                    uint8_t msb = input_buffer[in_idx++];
+                    if (format == 0) // 555
+                    {
+                      blue  = (lsb & 0x1F) << 3;
+                      green = ((msb & 0x03) << 6) | ((lsb & 0xE0) >> 2);
+                      red   = (msb & 0x7C) << 1;
+                    }
+                    else // 565
+                    {
+                      blue  = (lsb & 0x1F) << 3;
+                      green = ((msb & 0x07) << 5) | ((lsb & 0xE0) >> 3);
+                      red   = (msb & 0xF8);
+                    }
+                    whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
+                    colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
+                  }
+                  break;
+                case 1:
+                case 4:
+                case 8:
+                  {
+                    if (0 == in_bits)
+                    {
+                      in_byte = input_buffer[in_idx++];
+                      in_bits = 8;
+                    }
+                    uint16_t pn = (in_byte >> bitshift) & bitmask;
+                    whitish = mono_palette_buffer[pn / 8] & (0x1 << pn % 8);
+                    colored = color_palette_buffer[pn / 8] & (0x1 << pn % 8);
+                    in_byte <<= depth;
+                    in_bits -= depth;
+                  }
+                  break;
+              }
+              if (whitish)
+              {
+                color = GxEPD_WHITE;
+              }
+              else if (colored && with_color)
+              {
+                color = GxEPD_COLORED;
+              }
+              else
+              {
+                color = GxEPD_BLACK;
+              }
+              uint16_t yrow = y + (flip ? h - row - 1 : row);
+              display.drawPixel(x + col, yrow, color);
+            } // end pixel
+          } // end line
+        }
+        while (display.nextPage());
+        Serial.print("loaded in "); Serial.print(millis() - startTime); Serial.println(" ms");
+      }
+    }
+  }
   file.close();
   file.close();
   if (!valid)
   if (!valid)
   {
   {

BIN
examples/GxEPD2_SD_Example/bitmaps/bb4.bmp


+ 269 - 55
examples/GxEPD2_Spiffs_Example/GxEPD2_Spiffs_Example.ino

@@ -39,13 +39,17 @@
 #include <GxEPD2_3C.h>
 #include <GxEPD2_3C.h>
 #include <Fonts/FreeMonoBold9pt7b.h>
 #include <Fonts/FreeMonoBold9pt7b.h>
 
 
+#if defined(ESP32)
+#include "SPIFFS.h"
+#endif
+
 #include <FS.h>
 #include <FS.h>
 #define FileClass fs::File
 #define FileClass fs::File
 #define EPD_CS SS
 #define EPD_CS SS
 
 
 #if defined (ESP8266)
 #if defined (ESP8266)
 // select one and adapt to your mapping, can use full buffer size (full HEIGHT)
 // select one and adapt to your mapping, can use full buffer size (full HEIGHT)
-GxEPD2_BW<GxEPD2_154, GxEPD2_154::HEIGHT> display(GxEPD2_154(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
+//GxEPD2_BW<GxEPD2_154, GxEPD2_154::HEIGHT> display(GxEPD2_154(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW<GxEPD2_213, GxEPD2_213::HEIGHT> display(GxEPD2_213(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW<GxEPD2_213, GxEPD2_213::HEIGHT> display(GxEPD2_213(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW<GxEPD2_290, GxEPD2_290::HEIGHT> display(GxEPD2_290(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW<GxEPD2_290, GxEPD2_290::HEIGHT> display(GxEPD2_290(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW<GxEPD2_270, GxEPD2_270::HEIGHT> display(GxEPD2_270(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
 //GxEPD2_BW<GxEPD2_270, GxEPD2_270::HEIGHT> display(GxEPD2_270(/*CS=D8*/ EPD_CS, /*DC=D3*/ 0, /*RST=D4*/ 2, /*BUSY=D2*/ 4));
@@ -103,48 +107,14 @@ GxEPD2_BW<GxEPD2_154, GxEPD2_154::HEIGHT> display(GxEPD2_154(/*CS=D8*/ EPD_CS, /
 //GxEPD2_3C<GxEPD2_750c, GxEPD2_750c::HEIGHT> display(GxEPD2_750c(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
 //GxEPD2_3C<GxEPD2_750c, GxEPD2_750c::HEIGHT> display(GxEPD2_750c(/*CS=5*/ EPD_CS, /*DC=*/ 17, /*RST=*/ 16, /*BUSY=*/ 4));
 #endif
 #endif
 
 
-#if defined(_BOARD_GENERIC_STM32F103C_H_)
-#define MAX_DISPAY_BUFFER_SIZE (400 * 300) // ~5k is a good compromise
-#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPAY_BUFFER_SIZE / 8 / EPD::WIDTH ? EPD::HEIGHT : MAX_DISPAY_BUFFER_SIZE / 8 / EPD::WIDTH)
-// select one and adapt to your mapping
-//GxEPD2_BW<GxEPD2_154, MAX_HEIGHT(GxEPD2_154)> display(GxEPD2_154(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
-//GxEPD2_BW<GxEPD2_213, MAX_HEIGHT(GxEPD2_213)> display(GxEPD2_213(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
-//GxEPD2_BW<GxEPD2_290, MAX_HEIGHT(GxEPD2_290)> display(GxEPD2_290(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
-//GxEPD2_BW<GxEPD2_270, MAX_HEIGHT(GxEPD2_270)> display(GxEPD2_270(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
-//GxEPD2_BW<GxEPD2_420, MAX_HEIGHT(GxEPD2_420)> display(GxEPD2_420(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
-//GxEPD2_BW<GxEPD2_750, MAX_HEIGHT(GxEPD2_750)> display(GxEPD2_750(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
-// 3-color e-papers
-//GxEPD2_3C<GxEPD2_154c, MAX_HEIGHT(GxEPD2_154c)> display(GxEPD2_154c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
-//GxEPD2_3C<GxEPD2_213c, MAX_HEIGHT(GxEPD2_213c)> display(GxEPD2_213c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
-//GxEPD2_3C<GxEPD2_290c, MAX_HEIGHT(GxEPD2_290c)> display(GxEPD2_290c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
-//GxEPD2_3C<GxEPD2_270c, MAX_HEIGHT(GxEPD2_270c)> display(GxEPD2_270c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
-//GxEPD2_3C<GxEPD2_420c, MAX_HEIGHT(GxEPD2_420c)> display(GxEPD2_420c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
-//GxEPD2_3C<GxEPD2_750c, MAX_HEIGHT(GxEPD2_750c)> display(GxEPD2_750c(/*CS=4*/ EPD_CS, /*DC=*/ 3, /*RST=*/ 2, /*BUSY=*/ 1));
-#endif
-
-#if defined(__AVR)
-#define MAX_DISPAY_BUFFER_SIZE 1000 // half of available RAM as a compromise
-#define MAX_HEIGHT(EPD) (EPD::HEIGHT <= MAX_DISPAY_BUFFER_SIZE / 8 / EPD::WIDTH ? EPD::HEIGHT : MAX_DISPAY_BUFFER_SIZE / 8 / EPD::WIDTH)
-// select one and adapt to your mapping
-//GxEPD2_BW<GxEPD2_154, MAX_HEIGHT(GxEPD2_154)> display(GxEPD2_154(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
-//GxEPD2_BW<GxEPD2_213, MAX_HEIGHT(GxEPD2_213)> display(GxEPD2_213(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
-//GxEPD2_BW<GxEPD2_290, MAX_HEIGHT(GxEPD2_290)> display(GxEPD2_290(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
-//GxEPD2_BW<GxEPD2_270, MAX_HEIGHT(GxEPD2_270)> display(GxEPD2_270(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
-//GxEPD2_BW<GxEPD2_420, MAX_HEIGHT(GxEPD2_420)> display(GxEPD2_420(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
-//GxEPD2_BW<GxEPD2_750, MAX_HEIGHT(GxEPD2_750)> display(GxEPD2_750(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
-// 3-color e-papers
-//GxEPD2_3C<GxEPD2_154c, MAX_HEIGHT(GxEPD2_154c)> display(GxEPD2_154c(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
-//GxEPD2_3C<GxEPD2_213c, MAX_HEIGHT(GxEPD2_213c)> display(GxEPD2_213c(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
-//GxEPD2_3C<GxEPD2_290c, MAX_HEIGHT(GxEPD2_290c)> display(GxEPD2_290c(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
-//GxEPD2_3C<GxEPD2_270c, MAX_HEIGHT(GxEPD2_270c)> display(GxEPD2_270c(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
-//GxEPD2_3C<GxEPD2_420c, MAX_HEIGHT(GxEPD2_420c)> display(GxEPD2_420c(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
-//GxEPD2_3C<GxEPD2_750c, MAX_HEIGHT(GxEPD2_750c)> display(GxEPD2_750c(/*CS=10*/ EPD_CS, /*DC=*/ 8, /*RST=*/ 9, /*BUSY=*/ 7));
-#endif
-
-
 // function declaration with default parameter
 // function declaration with default parameter
 void drawBitmapFromSpiffs(const char *filename, int16_t x, int16_t y, bool with_color = true);
 void drawBitmapFromSpiffs(const char *filename, int16_t x, int16_t y, bool with_color = true);
 
 
+// bitmap drawing using buffered graphics, e.g. for small bitmaps or for GxEPD2_154c
+// partial_update selects refresh mode (not effective for GxEPD2_154c)
+// overwrite = true does not clear buffer before drawing, use only if buffer is full height
+void drawBitmapFromSpiffs_Buffered(const char *filename, int16_t x, int16_t y, bool with_color = true, bool partial_update = false, bool overwrite = false);
+
 void setup()
 void setup()
 {
 {
   Serial.begin(115200);
   Serial.begin(115200);
@@ -157,8 +127,18 @@ void setup()
   Serial.println("SPIFFS started");
   Serial.println("SPIFFS started");
   listFiles();
   listFiles();
 
 
-  drawBitmaps_200x200();
-  drawBitmaps_other();
+  if ((display.epd2.panel == GxEPD2::GDEW0154Z04) || false)
+  {
+    drawBitmapsBuffered_200x200();
+    drawBitmapsBuffered_other();
+  }
+  else
+  {
+    drawBitmaps_200x200();
+    drawBitmaps_other();
+  }
+
+  Serial.println("GxEPD2_Spiffs_Example done");
 }
 }
 
 
 void loop(void)
 void loop(void)
@@ -183,7 +163,7 @@ void drawBitmaps_200x200()
   delay(2000);
   delay(2000);
   drawBitmapFromSpiffs("sixth200x200.bmp", x, y);
   drawBitmapFromSpiffs("sixth200x200.bmp", x, y);
   delay(2000);
   delay(2000);
-  drawBitmapFromSpiffs("senventh200x200.bmp", x, y);
+  drawBitmapFromSpiffs("seventh200x200.bmp", x, y);
   delay(2000);
   delay(2000);
   drawBitmapFromSpiffs("eighth200x200.bmp", x, y);
   drawBitmapFromSpiffs("eighth200x200.bmp", x, y);
   delay(2000);
   delay(2000);
@@ -215,6 +195,56 @@ void drawBitmaps_other()
   delay(2000);
   delay(2000);
 }
 }
 
 
+void drawBitmapsBuffered_200x200()
+{
+  int16_t x = (display.width() - 200) / 2;
+  int16_t y = (display.height() - 200) / 2;
+  drawBitmapFromSpiffs_Buffered("logo200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("first200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("second200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("third200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("fourth200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("fifth200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("sixth200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("seventh200x200.bmp", x, y);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("eighth200x200.bmp", x, y);
+  delay(2000);
+}
+
+void drawBitmapsBuffered_other()
+{
+  int16_t w2 = display.width() / 2;
+  int16_t h2 = display.height() / 2;
+  drawBitmapFromSpiffs_Buffered("chanceflurries.bmp", w2 - 50, h2 - 50, false);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("betty_1.bmp", w2 - 100, h2 - 160);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("betty_4.bmp", w2 - 102, h2 - 126);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("marilyn_240x240x8.bmp", w2 - 120, h2 - 120);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("miniwoof.bmp", w2 - 60, h2 - 80);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("tiger.bmp", w2 - 160, h2 - 120);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("tiger_178x160x4.bmp", w2 - 89, h2 - 80);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("tiger_240x317x4.bmp", w2 - 120, h2 - 160);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("tiger_320x200x24.bmp", w2 - 160, h2 - 100);
+  delay(2000);
+  drawBitmapFromSpiffs_Buffered("woof.bmp", w2 - 120, h2 - 160);
+  delay(2000);
+}
+
 static const uint16_t input_buffer_pixels = 640; // may affect performance
 static const uint16_t input_buffer_pixels = 640; // may affect performance
 
 
 static const uint16_t max_row_width = 640; // for up to 7.5" display
 static const uint16_t max_row_width = 640; // for up to 7.5" display
@@ -237,7 +267,11 @@ void drawBitmapFromSpiffs(const char *filename, int16_t x, int16_t y, bool with_
   Serial.print("Loading image '");
   Serial.print("Loading image '");
   Serial.print(filename);
   Serial.print(filename);
   Serial.println('\'');
   Serial.println('\'');
+#if defined(ESP32)
+  file = SPIFFS.open(String("/") + filename, "r");
+#else
   file = SPIFFS.open(filename, "r");
   file = SPIFFS.open(filename, "r");
+#endif
   if (!file)
   if (!file)
   {
   {
     Serial.print("File not found");
     Serial.print("File not found");
@@ -267,6 +301,7 @@ void drawBitmapFromSpiffs(const char *filename, int16_t x, int16_t y, bool with_
       Serial.println(height);
       Serial.println(height);
       // BMP rows are padded (if needed) to 4-byte boundary
       // BMP rows are padded (if needed) to 4-byte boundary
       uint32_t rowSize = (width * depth / 8 + 3) & ~3;
       uint32_t rowSize = (width * depth / 8 + 3) & ~3;
+      if (depth < 8) rowSize = ((width * depth + 8 - depth) / 8 + 3) & ~3;
       if (height < 0)
       if (height < 0)
       {
       {
         height = -height;
         height = -height;
@@ -276,7 +311,7 @@ void drawBitmapFromSpiffs(const char *filename, int16_t x, int16_t y, bool with_
       uint16_t h = height;
       uint16_t h = height;
       if ((x + w - 1) >= display.width())  w = display.width()  - x;
       if ((x + w - 1) >= display.width())  w = display.width()  - x;
       if ((y + h - 1) >= display.height()) h = display.height() - y;
       if ((y + h - 1) >= display.height()) h = display.height() - y;
-      if (w < max_row_width) // handle with direct drawing
+      if (w <= max_row_width) // handle with direct drawing
       {
       {
         valid = true;
         valid = true;
         uint8_t bitmask = 0xFF;
         uint8_t bitmask = 0xFF;
@@ -302,7 +337,7 @@ void drawBitmapFromSpiffs(const char *filename, int16_t x, int16_t y, bool with_
             color_palette_buffer[pn / 8] |= colored << pn % 8;
             color_palette_buffer[pn / 8] |= colored << pn % 8;
           }
           }
         }
         }
-        display.writeScreenBuffer();
+        display.clearScreen();
         uint32_t rowPosition = flip ? imageOffset + (height - h) * rowSize : imageOffset;
         uint32_t rowPosition = flip ? imageOffset + (height - h) * rowSize : imageOffset;
         for (uint16_t row = 0; row < h; row++, rowPosition += rowSize) // for each line
         for (uint16_t row = 0; row < h; row++, rowPosition += rowSize) // for each line
         {
         {
@@ -311,8 +346,8 @@ void drawBitmapFromSpiffs(const char *filename, int16_t x, int16_t y, bool with_
           uint32_t in_bytes = 0;
           uint32_t in_bytes = 0;
           uint8_t in_byte = 0; // for depth <= 8
           uint8_t in_byte = 0; // for depth <= 8
           uint8_t in_bits = 0; // for depth <= 8
           uint8_t in_bits = 0; // for depth <= 8
-          uint8_t out_byte = 0;
-          uint8_t out_color_byte = 0;
+          uint8_t out_byte = 0xFF; // white (for w%8!=0 boarder)
+          uint8_t out_color_byte = 0xFF; // white (for w%8!=0 boarder)
           uint32_t out_idx = 0;
           uint32_t out_idx = 0;
           file.seek(rowPosition);
           file.seek(rowPosition);
           for (uint16_t col = 0; col < w; col++) // for each pixel
           for (uint16_t col = 0; col < w; col++) // for each pixel
@@ -372,23 +407,22 @@ void drawBitmapFromSpiffs(const char *filename, int16_t x, int16_t y, bool with_
             }
             }
             if (whitish)
             if (whitish)
             {
             {
-              out_byte |= 0x80 >> col % 8; // not black
-              out_color_byte |= 0x80 >> col % 8; // not colored
+              // keep white
             }
             }
             else if (colored && with_color)
             else if (colored && with_color)
             {
             {
-              out_byte |= 0x80 >> col % 8; // not black
+              out_color_byte &= ~(0x80 >> col % 8); // colored
             }
             }
             else
             else
             {
             {
-              out_color_byte |= 0x80 >> col % 8; // not colored
+              out_byte &= ~(0x80 >> col % 8); // black
             }
             }
-            if (7 == col % 8)
+            if ((7 == col % 8) || (col == w - 1)) // write that last byte! (for w%8!=0 boarder)
             {
             {
               output_row_color_buffer[out_idx] = out_color_byte;
               output_row_color_buffer[out_idx] = out_color_byte;
               output_row_mono_buffer[out_idx++] = out_byte;
               output_row_mono_buffer[out_idx++] = out_byte;
-              out_byte = 0;
-              out_color_byte = 0;
+              out_byte = 0xFF; // white (for w%8!=0 boarder)
+              out_color_byte = 0xFF; // white (for w%8!=0 boarder)
             }
             }
           } // end pixel
           } // end pixel
           uint16_t yrow = y + (flip ? h - row - 1 : row);
           uint16_t yrow = y + (flip ? h - row - 1 : row);
@@ -399,7 +433,187 @@ void drawBitmapFromSpiffs(const char *filename, int16_t x, int16_t y, bool with_
       }
       }
     }
     }
   }
   }
-  //Serial.print("end curPosition  "); Serial.println(file.curPosition());
+  file.close();
+  if (!valid)
+  {
+    Serial.println("bitmap format not handled.");
+  }
+}
+
+void drawBitmapFromSpiffs_Buffered(const char *filename, int16_t x, int16_t y, bool with_color, bool partial_update, bool overwrite)
+{
+  fs::File file;
+  bool valid = false; // valid format to be handled
+  bool flip = true; // bitmap is stored bottom-to-top
+  uint32_t startTime = millis();
+  if ((x >= display.width()) || (y >= display.height())) return;
+  Serial.println();
+  Serial.print("Loading image '");
+  Serial.print(filename);
+  Serial.println('\'');
+#if defined(ESP32)
+  file = SPIFFS.open(String("/") + filename, "r");
+#else
+  file = SPIFFS.open(filename, "r");
+#endif
+  if (!file)
+  {
+    Serial.print("File not found");
+    return;
+  }
+  // Parse BMP header
+  if (read16(file) == 0x4D42) // BMP signature
+  {
+    uint32_t fileSize = read32(file);
+    uint32_t creatorBytes = read32(file);
+    uint32_t imageOffset = read32(file); // Start of image data
+    uint32_t headerSize = read32(file);
+    uint32_t width  = read32(file);
+    uint32_t height = read32(file);
+    uint16_t planes = read16(file);
+    uint16_t depth = read16(file); // bits per pixel
+    uint32_t format = read32(file);
+    if ((planes == 1) && ((format == 0) || (format == 3))) // uncompressed is handled, 565 also
+    {
+      Serial.print("File size: "); Serial.println(fileSize);
+      Serial.print("Image Offset: "); Serial.println(imageOffset);
+      Serial.print("Header size: "); Serial.println(headerSize);
+      Serial.print("Bit Depth: "); Serial.println(depth);
+      Serial.print("Image size: ");
+      Serial.print(width);
+      Serial.print('x');
+      Serial.println(height);
+      // BMP rows are padded (if needed) to 4-byte boundary
+      uint32_t rowSize = (width * depth / 8 + 3) & ~3;
+      if (depth < 8) rowSize = ((width * depth + 8 - depth) / 8 + 3) & ~3;
+      if (height < 0)
+      {
+        height = -height;
+        flip = false;
+      }
+      uint16_t w = width;
+      uint16_t h = height;
+      if ((x + w - 1) >= display.width())  w = display.width()  - x;
+      if ((y + h - 1) >= display.height()) h = display.height() - y;
+      //if (w <= max_row_width) // handle with direct drawing
+      {
+        valid = true;
+        uint8_t bitmask = 0xFF;
+        uint8_t bitshift = 8 - depth;
+        uint16_t red, green, blue;
+        bool whitish, colored;
+        if (depth == 1) with_color = false;
+        if (depth <= 8)
+        {
+          if (depth < 8) bitmask >>= depth;
+          file.seek(54); //palette is always @ 54
+          for (uint16_t pn = 0; pn < (1 << depth); pn++)
+          {
+            blue  = file.read();
+            green = file.read();
+            red   = file.read();
+            file.read();
+            whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
+            colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
+            if (0 == pn % 8) mono_palette_buffer[pn / 8] = 0;
+            mono_palette_buffer[pn / 8] |= whitish << pn % 8;
+            if (0 == pn % 8) color_palette_buffer[pn / 8] = 0;
+            color_palette_buffer[pn / 8] |= colored << pn % 8;
+          }
+        }
+        if (partial_update) display.setPartialWindow(x, y, w, h);
+        else display.setFullWindow();
+        display.firstPage();
+        do
+        {
+          if (!overwrite) display.fillScreen(GxEPD_WHITE);
+          uint32_t rowPosition = flip ? imageOffset + (height - h) * rowSize : imageOffset;
+          for (uint16_t row = 0; row < h; row++, rowPosition += rowSize) // for each line
+          {
+            uint32_t in_remain = rowSize;
+            uint32_t in_idx = 0;
+            uint32_t in_bytes = 0;
+            uint8_t in_byte = 0; // for depth <= 8
+            uint8_t in_bits = 0; // for depth <= 8
+            uint16_t color = GxEPD_WHITE;
+            file.seek(rowPosition);
+            for (uint16_t col = 0; col < w; col++) // for each pixel
+            {
+              // Time to read more pixel data?
+              if (in_idx >= in_bytes) // ok, exact match for 24bit also (size IS multiple of 3)
+              {
+                in_bytes = file.read(input_buffer, in_remain > sizeof(input_buffer) ? sizeof(input_buffer) : in_remain);
+                in_remain -= in_bytes;
+                in_idx = 0;
+              }
+              switch (depth)
+              {
+                case 24:
+                  blue = input_buffer[in_idx++];
+                  green = input_buffer[in_idx++];
+                  red = input_buffer[in_idx++];
+                  whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
+                  colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
+                  break;
+                case 16:
+                  {
+                    uint8_t lsb = input_buffer[in_idx++];
+                    uint8_t msb = input_buffer[in_idx++];
+                    if (format == 0) // 555
+                    {
+                      blue  = (lsb & 0x1F) << 3;
+                      green = ((msb & 0x03) << 6) | ((lsb & 0xE0) >> 2);
+                      red   = (msb & 0x7C) << 1;
+                    }
+                    else // 565
+                    {
+                      blue  = (lsb & 0x1F) << 3;
+                      green = ((msb & 0x07) << 5) | ((lsb & 0xE0) >> 3);
+                      red   = (msb & 0xF8);
+                    }
+                    whitish = with_color ? ((red > 0x80) && (green > 0x80) && (blue > 0x80)) : ((red + green + blue) > 3 * 0x80); // whitish
+                    colored = (red > 0xF0) || ((green > 0xF0) && (blue > 0xF0)); // reddish or yellowish?
+                  }
+                  break;
+                case 1:
+                case 4:
+                case 8:
+                  {
+                    if (0 == in_bits)
+                    {
+                      in_byte = input_buffer[in_idx++];
+                      in_bits = 8;
+                    }
+                    uint16_t pn = (in_byte >> bitshift) & bitmask;
+                    whitish = mono_palette_buffer[pn / 8] & (0x1 << pn % 8);
+                    colored = color_palette_buffer[pn / 8] & (0x1 << pn % 8);
+                    in_byte <<= depth;
+                    in_bits -= depth;
+                  }
+                  break;
+              }
+              if (whitish)
+              {
+                color = GxEPD_WHITE;
+              }
+              else if (colored && with_color)
+              {
+                color = GxEPD_COLORED;
+              }
+              else
+              {
+                color = GxEPD_BLACK;
+              }
+              uint16_t yrow = y + (flip ? h - row - 1 : row);
+              display.drawPixel(x + col, yrow, color);
+            } // end pixel
+          } // end line
+        }
+        while (display.nextPage());
+        Serial.print("loaded in "); Serial.print(millis() - startTime); Serial.println(" ms");
+      }
+    }
+  }
   file.close();
   file.close();
   if (!valid)
   if (!valid)
   {
   {

+ 53 - 3
examples/GxEPD2_Spiffs_Example/SPIFFS_Support.ino

@@ -1,9 +1,52 @@
+#if defined(ESP32)
+
+#include "FS.h"
+#include "SPIFFS.h"
+
+void listDir(fs::FS &fs, const char * dirname, uint8_t levels) {
+  Serial.printf("Listing directory: %s\r\n", dirname);
+
+  File root = fs.open(dirname);
+  if (!root) {
+    Serial.println("- failed to open directory");
+    return;
+  }
+  if (!root.isDirectory()) {
+    Serial.println(" - not a directory");
+    return;
+  }
+
+  File file = root.openNextFile();
+  while (file) {
+    if (file.isDirectory()) {
+      Serial.print("  DIR : ");
+      Serial.println(file.name());
+      if (levels) {
+        listDir(fs, file.name(), levels - 1);
+      }
+    } else {
+      Serial.print("  FILE: ");
+      Serial.print(file.name());
+      Serial.print("\tSIZE: ");
+      Serial.println(file.size());
+    }
+    file = root.openNextFile();
+  }
+}
+
+void listFiles()
+{
+  listDir(SPIFFS, "/", 0);
+}
+
+#elif defined(ESP8266)
+
 /*====================================================================================
 /*====================================================================================
   This sketch contains support functions for the ESP6266 SPIFFS filing system
   This sketch contains support functions for the ESP6266 SPIFFS filing system
 
 
   Created by Bodmer 15th Jan 2017
   Created by Bodmer 15th Jan 2017
   ==================================================================================*/
   ==================================================================================*/
- 
+
 //====================================================================================
 //====================================================================================
 //                 Print a SPIFFS directory list (root directory)
 //                 Print a SPIFFS directory list (root directory)
 //====================================================================================
 //====================================================================================
@@ -15,7 +58,7 @@ void listFiles(void) {
   fs::Dir dir = SPIFFS.openDir(""); // Root directory
   fs::Dir dir = SPIFFS.openDir(""); // Root directory
   String  line = "=====================================";
   String  line = "=====================================";
   uint32_t totalBytes = 0;
   uint32_t totalBytes = 0;
-  
+
   Serial.println(line);
   Serial.println(line);
   Serial.println("  File name               Size");
   Serial.println("  File name               Size");
   Serial.println(line);
   Serial.println(line);
@@ -33,10 +76,17 @@ void listFiles(void) {
   int spaces = 25 - 8; // Tabulate nicely
   int spaces = 25 - 8; // Tabulate nicely
   while (spaces--) Serial.print(" ");
   while (spaces--) Serial.print(" ");
   Serial.print(totalBytes); Serial.println(" bytes");
   Serial.print(totalBytes); Serial.println(" bytes");
-  
+
   Serial.println(line);
   Serial.println(line);
   Serial.println();
   Serial.println();
   delay(1000);
   delay(1000);
 }
 }
 //====================================================================================
 //====================================================================================
 
 
+#else
+
+void listFiles()
+{
+  Serial.println("SPIFFS listFiles() not implemented");
+}
+#endif

+ 27 - 0
examples/GxEPD2_Spiffs_Loader/GxEPD2_Spiffs_Loader.ino

@@ -13,8 +13,17 @@
 // Waveshare e-paper displays with SPI: http://forum.arduino.cc/index.php?topic=487007.0
 // Waveshare e-paper displays with SPI: http://forum.arduino.cc/index.php?topic=487007.0
 // Good Dispay ePaper for Arduino : https://forum.arduino.cc/index.php?topic=436411.0
 // Good Dispay ePaper for Arduino : https://forum.arduino.cc/index.php?topic=436411.0
 
 
+#if defined(ESP32)
+#include "SPIFFS.h"
+// set formatOnFail = true for initial format of ESP32 SPIFFS (e.g. if error occured)
+const bool formatOnFail = false;
+#endif
+
 #include <FS.h>
 #include <FS.h>
+
+#if defined (ESP8266)
 #include <ESP8266WiFi.h>
 #include <ESP8266WiFi.h>
+#endif
 #include <WiFiClient.h>
 #include <WiFiClient.h>
 #include <WiFiClientSecure.h>
 #include <WiFiClientSecure.h>
 
 
@@ -74,7 +83,11 @@ void setup()
   // Print the IP address
   // Print the IP address
   Serial.println(WiFi.localIP());
   Serial.println(WiFi.localIP());
 
 
+#if defined(ESP32)
+  SPIFFS.begin(formatOnFail);
+#else
   SPIFFS.begin();
   SPIFFS.begin();
+#endif
   Serial.println("SPIFFS started");
   Serial.println("SPIFFS started");
   listFiles();
   listFiles();
   //deleteFiles();
   //deleteFiles();
@@ -178,10 +191,16 @@ void downloadFile_HTTP(const char* host, const char* path, const char* filename,
   }
   }
   if (!ok) return;
   if (!ok) return;
   uint8_t buffer[512];
   uint8_t buffer[512];
+#if defined(ESP8266)
   client.peekBytes(buffer, 2);
   client.peekBytes(buffer, 2);
   Serial.write(buffer[0]); Serial.write(buffer[1]); Serial.println();
   Serial.write(buffer[0]); Serial.write(buffer[1]); Serial.println();
+#endif
   size_t total = 0;
   size_t total = 0;
+#if defined(ESP32)
+  fs::File file = SPIFFS.open(String("/") + target, "w+");
+#else
   fs::File file = SPIFFS.open(target, "w+");
   fs::File file = SPIFFS.open(target, "w+");
+#endif
   if (!file)
   if (!file)
   {
   {
     Serial.print(target); Serial.println(" open failed");
     Serial.print(target); Serial.println(" open failed");
@@ -214,6 +233,7 @@ void downloadFile_HTTPS(const char* host, const char* path, const char* filename
     Serial.println("connection failed");
     Serial.println("connection failed");
     return;
     return;
   }
   }
+#if defined (ESP8266)
   if (fingerprint)
   if (fingerprint)
   {
   {
     if (client.verify(fingerprint, host))
     if (client.verify(fingerprint, host))
@@ -226,6 +246,7 @@ void downloadFile_HTTPS(const char* host, const char* path, const char* filename
       return;
       return;
     }
     }
   }
   }
+#endif
   Serial.print("requesting URL: ");
   Serial.print("requesting URL: ");
   Serial.println(String("https://") + host + path + filename);
   Serial.println(String("https://") + host + path + filename);
   client.print(String("GET ") + path + filename + " HTTP/1.1\r\n" +
   client.print(String("GET ") + path + filename + " HTTP/1.1\r\n" +
@@ -253,10 +274,16 @@ void downloadFile_HTTPS(const char* host, const char* path, const char* filename
   }
   }
   if (!ok) return;
   if (!ok) return;
   uint8_t buffer[512];
   uint8_t buffer[512];
+#if defined(ESP8266)
   client.peekBytes(buffer, 2);
   client.peekBytes(buffer, 2);
   Serial.write(buffer[0]); Serial.write(buffer[1]); Serial.println();
   Serial.write(buffer[0]); Serial.write(buffer[1]); Serial.println();
+#endif
   size_t total = 0;
   size_t total = 0;
+#if defined(ESP32)
+  fs::File file = SPIFFS.open(String("/") + target, "w+");
+#else
   fs::File file = SPIFFS.open(target, "w+");
   fs::File file = SPIFFS.open(target, "w+");
+#endif
   if (!file)
   if (!file)
   {
   {
     Serial.print(target); Serial.println(" open failed");
     Serial.print(target); Serial.println(" open failed");

+ 53 - 3
examples/GxEPD2_Spiffs_Loader/SPIFFS_Support.ino

@@ -1,9 +1,52 @@
+#if defined(ESP32)
+
+#include "FS.h"
+#include "SPIFFS.h"
+
+void listDir(fs::FS &fs, const char * dirname, uint8_t levels) {
+  Serial.printf("Listing directory: %s\r\n", dirname);
+
+  File root = fs.open(dirname);
+  if (!root) {
+    Serial.println("- failed to open directory");
+    return;
+  }
+  if (!root.isDirectory()) {
+    Serial.println(" - not a directory");
+    return;
+  }
+
+  File file = root.openNextFile();
+  while (file) {
+    if (file.isDirectory()) {
+      Serial.print("  DIR : ");
+      Serial.println(file.name());
+      if (levels) {
+        listDir(fs, file.name(), levels - 1);
+      }
+    } else {
+      Serial.print("  FILE: ");
+      Serial.print(file.name());
+      Serial.print("\tSIZE: ");
+      Serial.println(file.size());
+    }
+    file = root.openNextFile();
+  }
+}
+
+void listFiles()
+{
+  listDir(SPIFFS, "/", 0);
+}
+
+#elif defined(ESP8266)
+
 /*====================================================================================
 /*====================================================================================
   This sketch contains support functions for the ESP6266 SPIFFS filing system
   This sketch contains support functions for the ESP6266 SPIFFS filing system
 
 
   Created by Bodmer 15th Jan 2017
   Created by Bodmer 15th Jan 2017
   ==================================================================================*/
   ==================================================================================*/
- 
+
 //====================================================================================
 //====================================================================================
 //                 Print a SPIFFS directory list (root directory)
 //                 Print a SPIFFS directory list (root directory)
 //====================================================================================
 //====================================================================================
@@ -15,7 +58,7 @@ void listFiles(void) {
   fs::Dir dir = SPIFFS.openDir(""); // Root directory
   fs::Dir dir = SPIFFS.openDir(""); // Root directory
   String  line = "=====================================";
   String  line = "=====================================";
   uint32_t totalBytes = 0;
   uint32_t totalBytes = 0;
-  
+
   Serial.println(line);
   Serial.println(line);
   Serial.println("  File name               Size");
   Serial.println("  File name               Size");
   Serial.println(line);
   Serial.println(line);
@@ -33,10 +76,17 @@ void listFiles(void) {
   int spaces = 25 - 8; // Tabulate nicely
   int spaces = 25 - 8; // Tabulate nicely
   while (spaces--) Serial.print(" ");
   while (spaces--) Serial.print(" ");
   Serial.print(totalBytes); Serial.println(" bytes");
   Serial.print(totalBytes); Serial.println(" bytes");
-  
+
   Serial.println(line);
   Serial.println(line);
   Serial.println();
   Serial.println();
   delay(1000);
   delay(1000);
 }
 }
 //====================================================================================
 //====================================================================================
 
 
+#else
+
+void listFiles()
+{
+  Serial.println("SPIFFS listFiles() not implemented");
+}
+#endif

BIN
extras/bitmaps/bb4.bmp


+ 1 - 1
library.properties

@@ -1,5 +1,5 @@
 name=GxEPD2
 name=GxEPD2
-version=1.0.4
+version=1.0.5
 author=Jean-Marc Zingg
 author=Jean-Marc Zingg
 maintainer=Jean-Marc Zingg
 maintainer=Jean-Marc Zingg
 sentence=Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.
 sentence=Display Library for SPI e-paper panels from Dalian Good Display and boards from Waveshare.

+ 1 - 1
src/GxEPD2_3C.h

@@ -202,7 +202,7 @@ class GxEPD2_3C : public Adafruit_GFX
         if (_current_page == _pages)
         if (_current_page == _pages)
         {
         {
           _current_page = 0;
           _current_page = 0;
-          if (epd2.panel == GxEPD2::GDEW0154Z04)
+          if ((epd2.panel == GxEPD2::GDEW0154Z04) && (_pages > 1))
           {
           {
             if (!_second_phase)
             if (!_second_phase)
             {
             {