diff --git a/examples/2. Advanced Inkplate Features/01-Inkplate_WiFi_HTTP_request/01-Inkplate_WiFi_HTTP_request.ino b/examples/2. Advanced Inkplate Features/01-Inkplate_WiFi_HTTP_request/01-Inkplate_WiFi_HTTP_request.ino index 51c8c97..8c31495 100644 --- a/examples/2. Advanced Inkplate Features/01-Inkplate_WiFi_HTTP_request/01-Inkplate_WiFi_HTTP_request.ino +++ b/examples/2. Advanced Inkplate Features/01-Inkplate_WiFi_HTTP_request/01-Inkplate_WiFi_HTTP_request.ino @@ -2,11 +2,11 @@ 1_Inkplate_WiFi_HTTP example for e-radionica.com Inkplate 6 For this example you will need USB cable, Inkplate 6 and stable WiFi Internet connection Select "Inkplate 6(ESP32)" from Tools -> Board menu. - Don't have "Inkplate 6(ESP32)" option? Follow our tutorial and add it: + Don't have "Inkplate 6(ESP32)" option? Follow our tutorial and add it: https://e-radionica.com/en/blog/add-inkplate-6-to-arduino-ide/ This example will show you how to connect to WiFi network, get data from Internet and display that data on epaper. - This example is NOT on to how to parse HTML data from Internet - it will just print HTML on the screen. + This example is NOT on to how to parse HTML data from Internet - it will just print HTML on the screen. In quotation marks you will need write your WiFi SSID and WiFi password in order to connect to your WiFi network. @@ -15,68 +15,82 @@ 15 July 2020 by e-radionica.com */ -#include "Inkplate.h" //Include Inkplate library to the sketch -#include //Include ESP32 WiFi library to our sketch -#include //Include HTTP library to this sketch +#include "Inkplate.h" //Include Inkplate library to the sketch +#include //Include HTTP library to this sketch +#include //Include ESP32 WiFi library to our sketch -#define ssid "" //Name of the WiFi network (SSID) that you want to connect Inkplate to -#define pass "" //Password of that WiFi network +#define ssid "" // Name of the WiFi network (SSID) that you want to connect Inkplate to +#define pass "" // Password of that WiFi network -Inkplate display(INKPLATE_1BIT); //Create an object on Inkplate library and also set library into 1 Bit mode (Monochrome) +Inkplate display( + INKPLATE_1BIT); // Create an object on Inkplate library and also set library into 1 Bit mode (Monochrome) -void setup() { - display.begin(); //Init Inkplate library (you should call this function ONLY ONCE) - display.clearDisplay(); //Clear frame buffer of display - display.display(); //Put clear image on display - display.setTextSize(2); //Set text scaling to two (text will be two times bigger) - display.setCursor(0, 0); //Set print position - display.setTextColor(BLACK, WHITE); //Set text color to black and background color to white - display.println("Scanning for WiFi networks..."); //Write text - display.display(); //Send everything to display (refresh display) +void setup() +{ + display.begin(); // Init Inkplate library (you should call this function ONLY ONCE) + display.clearDisplay(); // Clear frame buffer of display + display.display(); // Put clear image on display + display.setTextSize(2); // Set text scaling to two (text will be two times bigger) + display.setCursor(0, 0); // Set print position + display.setTextColor(BLACK, WHITE); // Set text color to black and background color to white + display.println("Scanning for WiFi networks..."); // Write text + display.display(); // Send everything to display (refresh display) - int n = WiFi.scanNetworks(); //Start searching WiFi networks and put the nubmer of found WiFi networks in variable n - if (n == 0) { //If you did not find any network, show the message and stop the program. - display.print("No WiFi networks found!"); - display.partialUpdate(); - while (true); - } else { - if (n > 10) n = 0; //If you did find, print name (SSID), encryption and signal strength of first 10 networks - for (int i = 0; i < n; i++) { - display.print(WiFi.SSID(i)); - display.print((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? 'O' : '*'); - display.print('\n'); - display.print(WiFi.RSSI(i), DEC); + int n = WiFi.scanNetworks(); // Start searching WiFi networks and put the nubmer of found WiFi networks in variable + // n + if (n == 0) + { // If you did not find any network, show the message and stop the program. + display.print("No WiFi networks found!"); + display.partialUpdate(); + while (true) + ; } - display.partialUpdate(); //(Partial) refresh thescreen - } - - display.clearDisplay(); //Clear everything in frame buffer - display.setCursor(0,0); //Set print cursor to new position - display.print("Connecting to "); //Print the name of WiFi network - display.print(ssid); - WiFi.begin(ssid, pass); //Try to connect to WiFi network - while (WiFi.status() != WL_CONNECTED) { - delay(1000); //While it is connecting to network, display dot every second, just to know that Inkplate is alive. - display.print('.'); - display.partialUpdate(); - } - display.print("connected"); //If it's connected, notify user - display.partialUpdate(); - - HTTPClient http; - if(http.begin("http://example.com/index.html")) { //Now try to connect to some web page (in this example www.example.com. And yes, this is a valid Web page :)) - if(http.GET()>0) { //If connection was successful, try to read content of the Web page and display it on screen - String htmlText; - htmlText = http.getString(); - display.setTextSize(1); //Set smaller text size, so everything can fit on screen - display.clearDisplay(); - display.setCursor(0, 0); - display.print(htmlText); - display.display(); + else + { + if (n > 10) + n = 0; // If you did find, print name (SSID), encryption and signal strength of first 10 networks + for (int i = 0; i < n; i++) + { + display.print(WiFi.SSID(i)); + display.print((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? 'O' : '*'); + display.print('\n'); + display.print(WiFi.RSSI(i), DEC); + } + display.partialUpdate(); //(Partial) refresh thescreen + } + + display.clearDisplay(); // Clear everything in frame buffer + display.setCursor(0, 0); // Set print cursor to new position + display.print("Connecting to "); // Print the name of WiFi network + display.print(ssid); + WiFi.begin(ssid, pass); // Try to connect to WiFi network + while (WiFi.status() != WL_CONNECTED) + { + delay(1000); // While it is connecting to network, display dot every second, just to know that Inkplate is + // alive. + display.print('.'); + display.partialUpdate(); + } + display.print("connected"); // If it's connected, notify user + display.partialUpdate(); + + HTTPClient http; + if (http.begin("http://example.com/index.html")) + { // Now try to connect to some web page (in this example www.example.com. And yes, this is a valid Web page :)) + if (http.GET() > 0) + { // If connection was successful, try to read content of the Web page and display it on screen + String htmlText; + htmlText = http.getString(); + display.setTextSize(1); // Set smaller text size, so everything can fit on screen + display.clearDisplay(); + display.setCursor(0, 0); + display.print(htmlText); + display.display(); + } } - } } -void loop() { - //Nothing +void loop() +{ + // Nothing } diff --git a/examples/2. Advanced Inkplate Features/05-Inkplate_SD_BMP_pictures/05-Inkplate_SD_BMP_pictures.ino b/examples/2. Advanced Inkplate Features/05-Inkplate_SD_BMP_pictures/05-Inkplate_SD_BMP_pictures.ino index 0ffb3e7..72c22a9 100644 --- a/examples/2. Advanced Inkplate Features/05-Inkplate_SD_BMP_pictures/05-Inkplate_SD_BMP_pictures.ino +++ b/examples/2. Advanced Inkplate Features/05-Inkplate_SD_BMP_pictures/05-Inkplate_SD_BMP_pictures.ino @@ -21,52 +21,60 @@ 15 July 2020 by e-radionica.com */ -#include "Inkplate.h" //Include Inkplate library to the sketch -#include "SdFat.h" //Include library for SD card -Inkplate display(INKPLATE_1BIT); //Create an object on Inkplate library and also set library into 1 Bit mode (Monochrome) -SdFile file; //Create SdFile object used for accessing files on SD card +#include "Inkplate.h" //Include Inkplate library to the sketch +#include "SdFat.h" //Include library for SD card +Inkplate display( + INKPLATE_1BIT); // Create an object on Inkplate library and also set library into 1 Bit mode (Monochrome) +SdFile file; // Create SdFile object used for accessing files on SD card -void setup() { +void setup() +{ Serial.begin(115200); - display.begin(); //Init Inkplate library (you should call this function ONLY ONCE) - display.clearDisplay(); //Clear frame buffer of display - display.display(); //Put clear image on display + display.begin(); // Init Inkplate library (you should call this function ONLY ONCE) + display.clearDisplay(); // Clear frame buffer of display + display.display(); // Put clear image on display - //Init SD card. Display if SD card is init propery or not. - if (display.sdCardInit()) { + // Init SD card. Display if SD card is init propery or not. + if (display.sdCardInit()) + { display.println("SD Card OK! Reading image..."); display.partialUpdate(); - //If card is properly init, try to load image and display it on e-paper at position X=0, Y=0 - //NOTE: Both drawBitmapFromSD methods allow for an optional fourth "invert" parameter. Setting this parameter to true - //will flip all colors on the image, making black white and white black. This may be necessary when exporting bitmaps from - //certain softwares. - if (!display.drawBitmapFromSD("image1.bmp", 0, 0, 1)) { - //If is something failed (wrong filename or wrong bitmap format), write error message on the screen. - //REMEMBER! You can only use Windows Bitmap file with color depth of 1, 4, 8 or 24 bits with no compression! - //You can turn of dithering for somewhat faster image load by changing the last 1 to 0, or removing the 1 argument completely + // If card is properly init, try to load image and display it on e-paper at position X=0, Y=0 + // NOTE: Both drawBitmapFromSd methods allow for an optional fourth "invert" parameter. Setting this parameter + // to true will flip all colors on the image, making black white and white black. This may be necessary when + // exporting bitmaps from certain softwares. + if (!display.drawBitmapFromSd("image1.bmp", 0, 0, 1)) + { + // If is something failed (wrong filename or wrong bitmap format), write error message on the screen. + // REMEMBER! You can only use Windows Bitmap file with color depth of 1, 4, 8 or 24 bits with no + // compression! You can turn of dithering for somewhat faster image load by changing the last 1 to 0, or + // removing the 1 argument completely display.println("Image open error"); display.display(); } display.display(); } - else { - //If SD card init not success, display error on screen and stop the program (using infinite loop) + else + { + // If SD card init not success, display error on screen and stop the program (using infinite loop) display.println("SD Card error!"); display.partialUpdate(); - while (true); + while (true) + ; } delay(5000); - //Now try to load image using SdFat library class (for more advanced users) and display image on epaper. - if (file.open("image2.bmp", O_RDONLY)) { - display.drawBitmapFromSD(&file, 0, 0); + // Now try to load image using SdFat library class (for more advanced users) and display image on epaper. + if (file.open("image2.bmp", O_RDONLY)) + { + display.drawBitmapFromSd(&file, 0, 0); display.display(); } - } -void loop() { - //Nothing... +void loop() +{ + // Nothing... } diff --git a/examples/4. Others/1-Inkplate_Slave_Mode/1-Inkplate_Slave_Mode.ino b/examples/4. Others/1-Inkplate_Slave_Mode/1-Inkplate_Slave_Mode.ino index 2631ad6..cf871b0 100644 --- a/examples/4. Others/1-Inkplate_Slave_Mode/1-Inkplate_Slave_Mode.ino +++ b/examples/4. Others/1-Inkplate_Slave_Mode/1-Inkplate_Slave_Mode.ino @@ -1,24 +1,25 @@ /* Inkplate_Slave_Mode sketch for e-radionica.com Inkplate 6 Select "Inkplate 6(ESP32)" from Tools -> Board menu. - Don't have "Inkplate 6(ESP32)" option? Follow our tutorial and add it: + Don't have "Inkplate 6(ESP32)" option? Follow our tutorial and add it: https://e-radionica.com/en/blog/add-inkplate-6-to-arduino-ide/ - - Using this sketch, you don't have to program and control e-paper using Arduino code. + + Using this sketch, you don't have to program and control e-paper using Arduino code. Instead, you can send UART command (explained in documentation that can be found inside folder of this sketch). This give you flexibility that you can use this Inkplate 6 on any platform! - Because it uses UART, it's little bit slower and it's not recommended to send bunch of + Because it uses UART, it's little bit slower and it's not recommended to send bunch of drawPixel command to draw some image. Instead, load bitmaps and pictures on SD card and load image from SD. If we missed some function, you can modify this and make yor own. Also, every Inkplate comes with this slave mode right from the factory. - Learn more about Slave Mode in this update: https://www.crowdsupply.com/e-radionica/inkplate-6/updates/successfully-funded-also-third-party-master-controllers-and-partial-updates + Learn more about Slave Mode in this update: + https://www.crowdsupply.com/e-radionica/inkplate-6/updates/successfully-funded-also-third-party-master-controllers-and-partial-updates UART settings are: 115200 baud, standard parity, ending with "\n\r" (both) You can send commands via USB port or by directly connecting to ESP32 TX and RX pins. - Don't forget you need to send #L(1)* after each command to show it on the display - (equal to display.display()). + Don't forget you need to send #L(1)* after each command to show it on the display + (equal to display.display()). Want to learn more about Inkplate? Visit www.inkplate.io Looking to get support? Write on our forums: http://forum.e-radionica.com/en/ @@ -31,308 +32,342 @@ Inkplate display(INKPLATE_1BIT); #define BUFFER_SIZE 1000 char commandBuffer[BUFFER_SIZE + 1]; char strTemp[2001]; -void setup() { - display.begin(); - Serial.begin(115200); - memset(commandBuffer, 0, BUFFER_SIZE); +void setup() +{ + display.begin(); + Serial.begin(115200); + memset(commandBuffer, 0, BUFFER_SIZE); } -void loop() { - // put your main code here, to run repeatedly: - if (Serial.available()) { - while (Serial.available()) { - for (int i = 0; i < (BUFFER_SIZE - 1); i++) { - commandBuffer[i] = commandBuffer[i + 1]; - } - commandBuffer[BUFFER_SIZE - 1] = Serial.read(); - } - } - char *s = NULL; - char *e = NULL; - for (int i = 0; i < BUFFER_SIZE; i++) { - if (commandBuffer[i] == '#' && s == NULL) s = &commandBuffer[i]; - if (commandBuffer[i] == '*' && e == NULL) e = &commandBuffer[i]; - } - if (s != NULL && e != NULL) { - if ((e - s) > 0) { - int x, x1, x2, y, y1, y2, x3, y3, l, c, w, h, r, n; - char b; - char temp[150]; - switch (*(s + 1)) { - case '?': - Serial.print("OK"); - break; - - case '0': - sscanf(s + 3, "%d,%d,%d", &x, &y, &c); - //sprintf(temp, "display.drawPixel(%d, %d, %d)\n\r", x, y, c); - //Serial.print(temp); - display.drawPixel(x, y, c); - break; - - case '1': - sscanf(s + 3, "%d,%d,%d,%d,%d", &x1, &y1, &x2, &y2, &c); - //sprintf(temp, "display.drawLine(%d, %d, %d, %d, %d)\n\r", x1, y1, x2, y2, c); - //Serial.print(temp); - display.drawLine(x1, y1, x2, y2, c); - break; - - case '2': - sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &l, &c); - //sprintf(temp, "display.drawFastVLine(%d, %d, %d, %d)\n\r", x, y, l, c); - //Serial.print(temp); - display.drawFastVLine(x, y, l, c); - break; - - case '3': - sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &l, &c); - //sprintf(temp, "display.drawFastHLine(%d, %d, %d, %d)\n\r", x, y, l, c); - //Serial.print(temp); - display.drawFastHLine(x, y, l, c); - break; - - case '4': - sscanf(s + 3, "%d,%d,%d,%d,%d", &x, &y, &w, &h, &c); - //sprintf(temp, "display.drawRect(%d, %d, %d, %d, %d)\n\r", x, y, w, h, c); - //Serial.print(temp); - display.drawRect(x, y, w, h, c); - break; - - case '5': - sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &r, &c); - //sprintf(temp, "display.drawCircle(%d, %d, %d, %d)\n\r", x, y, r, c); - //Serial.print(temp); - display.drawCircle(x, y, r, c); - break; - - case '6': - sscanf(s + 3, "%d,%d,%d,%d,%d,%d,%d", &x1, &y1, &x2, &y2, &x3, &y3, &c); - //sprintf(temp, "display.drawTriangle(%d, %d, %d, %d, %d, %d, %d)\n\r", x1, y1, x2, y2, x3, y3, c); - //Serial.print(temp); - display.drawTriangle(x1, y1, x2, y2, x3, y3, c); - break; - - case '7': - sscanf(s + 3, "%d,%d,%d,%d,%d,%d", &x, &y, &w, &h, &r, &c); - //sprintf(temp, "display.drawRoundRect(%d, %d, %d, %d, %d, %d)\n\r", x, y, w, h, r, c); - //Serial.print(temp); - display.drawRoundRect(x, y, w, h, r, c); - break; - - case '8': - sscanf(s + 3, "%d,%d,%d,%d,%d", &x, &y, &w, &h, &c); - //sprintf(temp, "display.fillRect(%d, %d, %d, %d, %d)\n\r", x, y, w, h, c); - //Serial.print(temp); - display.fillRect(x, y, w, h, c); - break; - - case '9': - sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &r, &c); - //sprintf(temp, "display.fillCircle(%d, %d, %d, %d)\n\r", x, y, r, c); - //Serial.print(temp); - display.fillCircle(x, y, r, c); - break; - - case 'A': - sscanf(s + 3, "%d,%d,%d,%d,%d,%d,%d", &x1, &y1, &x2, &y2, &x3, &y3, &c); - //sprintf(temp, "display.fillTriangle(%d, %d, %d, %d, %d, %d, %d)\n\r", x1, y1, x2, y2, x3, y3, c); - //Serial.print(temp); - display.fillTriangle(x1, y1, x2, y2, x3, y3, c); - break; - - case 'B': - sscanf(s + 3, "%d,%d,%d,%d,%d,%d", &x, &y, &w, &h, &r, &c); - //sprintf(temp, "display.fillRoundRect(%d, %d, %d, %d, %d, %d)\n\r", x, y, w, h, r, c); - //Serial.print(temp); - display.fillRoundRect(x, y, w, h, r, c); - break; - - case 'C': - sscanf(s + 3, "\"%2000[^\"]\"", strTemp); - n = strlen(strTemp); - for (int i = 0; i < n; i++) { - strTemp[i] = toupper(strTemp[i]); - } - for (int i = 0; i < n; i += 2) { - strTemp[i / 2] = (hexToChar(strTemp[i]) << 4) | (hexToChar(strTemp[i + 1]) & 0x0F); - } - strTemp[n / 2] = 0; - //Serial.print("display.print(\""); - //Serial.print(strTemp); - //Serial.println("\");"); - display.print(strTemp); - break; - - case 'D': - sscanf(s + 3, "%d", &c); - //sprintf(temp, "display.setTextSize(%d)\n", c); - //Serial.print(temp); - display.setTextSize(c); - break; - - case 'E': - sscanf(s + 3, "%d,%d", &x, &y); - //sprintf(temp, "display.setCursor(%d, %d)\n", x, y); - //Serial.print(temp); - display.setCursor(x, y); - break; - - case 'F': - sscanf(s + 3, "%c", &b); - //sprintf(temp, "display.setTextWrap(%s)\n", b == 'T' ? "True" : "False"); - //Serial.print(temp); - if (b == 'T') display.setTextWrap(true); - if (b == 'F') display.setTextWrap(false); - break; - - case 'G': - sscanf(s + 3, "%d", &c); - c &= 3; - //sprintf(temp, "display.setRotation(%d)\n", c); - //Serial.print(temp); - display.setRotation(c); - break; - - case 'H': - sscanf(s + 3, "%d,%d,\"%149[^\"]\"", &x, &y, strTemp); - n = strlen(strTemp); - for (int i = 0; i < n; i++) { - strTemp[i] = toupper(strTemp[i]); - } - for (int i = 0; i < n; i += 2) { - strTemp[i / 2] = (hexToChar(strTemp[i]) << 4) | (hexToChar(strTemp[i + 1]) & 0x0F); - } - strTemp[n / 2] = 0; - r = display.sdCardInit(); - if (r) { - r = display.drawBitmapFromSD(strTemp, x, y); - Serial.print("#H("); - Serial.print(r, DEC); - Serial.println(")*"); - Serial.flush(); - //sprintf(temp, "display.drawBitmap(%d, %d, %s)\n", x, y, strTemp); - //Serial.print(temp); - } else { - Serial.println("#H(-1)*"); - Serial.flush(); - } - break; - - case 'I': - sscanf(s + 3, "%d", &c); - //sprintf(temp, "display.setDisplayMode(%s)\n", c == 0 ? "INKPLATE_1BIT" : "INKPLATE_3BIT"); - //Serial.print(temp); - if (c == INKPLATE_1BIT) display.selectDisplayMode(INKPLATE_1BIT); - if (c == INKPLATE_3BIT) display.selectDisplayMode(INKPLATE_3BIT); - break; - - case 'J': - sscanf(s + 3, "%c", &b); - if (b == '?') { - //if (0 == 0) { - // Serial.println("#J(0)*"); - //} else { - // Serial.println("#J(1)*"); - //} - if (display.getDisplayMode() == INKPLATE_1BIT) { - Serial.println("#J(0)*"); - Serial.flush(); +void loop() +{ + // put your main code here, to run repeatedly: + if (Serial.available()) + { + while (Serial.available()) + { + for (int i = 0; i < (BUFFER_SIZE - 1); i++) + { + commandBuffer[i] = commandBuffer[i + 1]; } - if (display.getDisplayMode() == INKPLATE_3BIT) { - Serial.println("#J(1)*"); - Serial.flush(); - } - } - break; - - case 'K': - sscanf(s + 3, "%c", &b); - if (b == '1') { - //Serial.print("display.clearDisplay();\n"); - display.clearDisplay(); - } - break; - - case 'L': - sscanf(s + 3, "%c", &b); - if (b == '1') { - //Serial.print("display.display();\n"); - display.display(); - } - break; - - case 'M': - sscanf(s + 3, "%d,%d,%d", &y1, &x2, &y2); - //sprintf(temp, "display.partialUpdate(%d, %d, %d);\n", y1, x2, y2); - //Serial.print(temp); - display.partialUpdate(); - break; - - case 'N': - sscanf(s + 3, "%c", &b); - if (b == '?') { - Serial.print("#N("); - Serial.print(display.readTemperature(), DEC); - //Serial.print(23, DEC); - Serial.println(")*"); - Serial.flush(); - } - break; - - case 'O': - sscanf(s + 3, "%d", &c); - if (c >= 0 && c <= 2) { - Serial.print("#O("); - Serial.print(display.readTouchpad(c), DEC); - //Serial.print(0, DEC); - Serial.println(")*"); - Serial.flush(); - } - break; - - case 'P': - sscanf(s + 3, "%c", &b); - if (b == '?') { - Serial.print("#P("); - Serial.print(display.readBattery(), 2); - //Serial.print(3.54, 2); - Serial.println(")*"); - Serial.flush(); - } - break; - - case 'Q': - sscanf(s + 3, "%d", &c); - c &= 1; - //if (c == 0) Serial.print("display.einkOff();\n"); - //if (c == 1) Serial.print("display.einkOn();\n"); - if (c == 0) display.einkOff(); - if (c == 1) display.einkOn(); - break; - - case 'R': - sscanf(s + 3, "%c", &b); - if (b == '?') { - Serial.print("#R("); - Serial.print(display.getPanelState(), DEC); - //Serial.print(1, DEC); - Serial.println(")*"); - Serial.flush(); - } - break; - } - *s = 0; - *e = 0; + commandBuffer[BUFFER_SIZE - 1] = Serial.read(); + } + } + char *s = NULL; + char *e = NULL; + for (int i = 0; i < BUFFER_SIZE; i++) + { + if (commandBuffer[i] == '#' && s == NULL) + s = &commandBuffer[i]; + if (commandBuffer[i] == '*' && e == NULL) + e = &commandBuffer[i]; + } + if (s != NULL && e != NULL) + { + if ((e - s) > 0) + { + int x, x1, x2, y, y1, y2, x3, y3, l, c, w, h, r, n; + char b; + char temp[150]; + switch (*(s + 1)) + { + case '?': + Serial.print("OK"); + break; + + case '0': + sscanf(s + 3, "%d,%d,%d", &x, &y, &c); + // sprintf(temp, "display.drawPixel(%d, %d, %d)\n\r", x, y, c); + // Serial.print(temp); + display.drawPixel(x, y, c); + break; + + case '1': + sscanf(s + 3, "%d,%d,%d,%d,%d", &x1, &y1, &x2, &y2, &c); + // sprintf(temp, "display.drawLine(%d, %d, %d, %d, %d)\n\r", x1, y1, x2, y2, c); + // Serial.print(temp); + display.drawLine(x1, y1, x2, y2, c); + break; + + case '2': + sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &l, &c); + // sprintf(temp, "display.drawFastVLine(%d, %d, %d, %d)\n\r", x, y, l, c); + // Serial.print(temp); + display.drawFastVLine(x, y, l, c); + break; + + case '3': + sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &l, &c); + // sprintf(temp, "display.drawFastHLine(%d, %d, %d, %d)\n\r", x, y, l, c); + // Serial.print(temp); + display.drawFastHLine(x, y, l, c); + break; + + case '4': + sscanf(s + 3, "%d,%d,%d,%d,%d", &x, &y, &w, &h, &c); + // sprintf(temp, "display.drawRect(%d, %d, %d, %d, %d)\n\r", x, y, w, h, c); + // Serial.print(temp); + display.drawRect(x, y, w, h, c); + break; + + case '5': + sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &r, &c); + // sprintf(temp, "display.drawCircle(%d, %d, %d, %d)\n\r", x, y, r, c); + // Serial.print(temp); + display.drawCircle(x, y, r, c); + break; + + case '6': + sscanf(s + 3, "%d,%d,%d,%d,%d,%d,%d", &x1, &y1, &x2, &y2, &x3, &y3, &c); + // sprintf(temp, "display.drawTriangle(%d, %d, %d, %d, %d, %d, %d)\n\r", x1, y1, x2, y2, x3, y3, c); + // Serial.print(temp); + display.drawTriangle(x1, y1, x2, y2, x3, y3, c); + break; + + case '7': + sscanf(s + 3, "%d,%d,%d,%d,%d,%d", &x, &y, &w, &h, &r, &c); + // sprintf(temp, "display.drawRoundRect(%d, %d, %d, %d, %d, %d)\n\r", x, y, w, h, r, c); + // Serial.print(temp); + display.drawRoundRect(x, y, w, h, r, c); + break; + + case '8': + sscanf(s + 3, "%d,%d,%d,%d,%d", &x, &y, &w, &h, &c); + // sprintf(temp, "display.fillRect(%d, %d, %d, %d, %d)\n\r", x, y, w, h, c); + // Serial.print(temp); + display.fillRect(x, y, w, h, c); + break; + + case '9': + sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &r, &c); + // sprintf(temp, "display.fillCircle(%d, %d, %d, %d)\n\r", x, y, r, c); + // Serial.print(temp); + display.fillCircle(x, y, r, c); + break; + + case 'A': + sscanf(s + 3, "%d,%d,%d,%d,%d,%d,%d", &x1, &y1, &x2, &y2, &x3, &y3, &c); + // sprintf(temp, "display.fillTriangle(%d, %d, %d, %d, %d, %d, %d)\n\r", x1, y1, x2, y2, x3, y3, c); + // Serial.print(temp); + display.fillTriangle(x1, y1, x2, y2, x3, y3, c); + break; + + case 'B': + sscanf(s + 3, "%d,%d,%d,%d,%d,%d", &x, &y, &w, &h, &r, &c); + // sprintf(temp, "display.fillRoundRect(%d, %d, %d, %d, %d, %d)\n\r", x, y, w, h, r, c); + // Serial.print(temp); + display.fillRoundRect(x, y, w, h, r, c); + break; + + case 'C': + sscanf(s + 3, "\"%2000[^\"]\"", strTemp); + n = strlen(strTemp); + for (int i = 0; i < n; i++) + { + strTemp[i] = toupper(strTemp[i]); + } + for (int i = 0; i < n; i += 2) + { + strTemp[i / 2] = (hexToChar(strTemp[i]) << 4) | (hexToChar(strTemp[i + 1]) & 0x0F); + } + strTemp[n / 2] = 0; + // Serial.print("display.print(\""); + // Serial.print(strTemp); + // Serial.println("\");"); + display.print(strTemp); + break; + + case 'D': + sscanf(s + 3, "%d", &c); + // sprintf(temp, "display.setTextSize(%d)\n", c); + // Serial.print(temp); + display.setTextSize(c); + break; + + case 'E': + sscanf(s + 3, "%d,%d", &x, &y); + // sprintf(temp, "display.setCursor(%d, %d)\n", x, y); + // Serial.print(temp); + display.setCursor(x, y); + break; + + case 'F': + sscanf(s + 3, "%c", &b); + // sprintf(temp, "display.setTextWrap(%s)\n", b == 'T' ? "True" : "False"); + // Serial.print(temp); + if (b == 'T') + display.setTextWrap(true); + if (b == 'F') + display.setTextWrap(false); + break; + + case 'G': + sscanf(s + 3, "%d", &c); + c &= 3; + // sprintf(temp, "display.setRotation(%d)\n", c); + // Serial.print(temp); + display.setRotation(c); + break; + + case 'H': + sscanf(s + 3, "%d,%d,\"%149[^\"]\"", &x, &y, strTemp); + n = strlen(strTemp); + for (int i = 0; i < n; i++) + { + strTemp[i] = toupper(strTemp[i]); + } + for (int i = 0; i < n; i += 2) + { + strTemp[i / 2] = (hexToChar(strTemp[i]) << 4) | (hexToChar(strTemp[i + 1]) & 0x0F); + } + strTemp[n / 2] = 0; + r = display.sdCardInit(); + if (r) + { + r = display.drawBitmapFromSd(strTemp, x, y); + Serial.print("#H("); + Serial.print(r, DEC); + Serial.println(")*"); + Serial.flush(); + // sprintf(temp, "display.drawBitmap(%d, %d, %s)\n", x, y, strTemp); + // Serial.print(temp); + } + else + { + Serial.println("#H(-1)*"); + Serial.flush(); + } + break; + + case 'I': + sscanf(s + 3, "%d", &c); + // sprintf(temp, "display.setDisplayMode(%s)\n", c == 0 ? "INKPLATE_1BIT" : "INKPLATE_3BIT"); + // Serial.print(temp); + if (c == INKPLATE_1BIT) + display.selectDisplayMode(INKPLATE_1BIT); + if (c == INKPLATE_3BIT) + display.selectDisplayMode(INKPLATE_3BIT); + break; + + case 'J': + sscanf(s + 3, "%c", &b); + if (b == '?') + { + // if (0 == 0) { + // Serial.println("#J(0)*"); + //} else { + // Serial.println("#J(1)*"); + //} + if (display.getDisplayMode() == INKPLATE_1BIT) + { + Serial.println("#J(0)*"); + Serial.flush(); + } + if (display.getDisplayMode() == INKPLATE_3BIT) + { + Serial.println("#J(1)*"); + Serial.flush(); + } + } + break; + + case 'K': + sscanf(s + 3, "%c", &b); + if (b == '1') + { + // Serial.print("display.clearDisplay();\n"); + display.clearDisplay(); + } + break; + + case 'L': + sscanf(s + 3, "%c", &b); + if (b == '1') + { + // Serial.print("display.display();\n"); + display.display(); + } + break; + + case 'M': + sscanf(s + 3, "%d,%d,%d", &y1, &x2, &y2); + // sprintf(temp, "display.partialUpdate(%d, %d, %d);\n", y1, x2, y2); + // Serial.print(temp); + display.partialUpdate(); + break; + + case 'N': + sscanf(s + 3, "%c", &b); + if (b == '?') + { + Serial.print("#N("); + Serial.print(display.readTemperature(), DEC); + // Serial.print(23, DEC); + Serial.println(")*"); + Serial.flush(); + } + break; + + case 'O': + sscanf(s + 3, "%d", &c); + if (c >= 0 && c <= 2) + { + Serial.print("#O("); + Serial.print(display.readTouchpad(c), DEC); + // Serial.print(0, DEC); + Serial.println(")*"); + Serial.flush(); + } + break; + + case 'P': + sscanf(s + 3, "%c", &b); + if (b == '?') + { + Serial.print("#P("); + Serial.print(display.readBattery(), 2); + // Serial.print(3.54, 2); + Serial.println(")*"); + Serial.flush(); + } + break; + + case 'Q': + sscanf(s + 3, "%d", &c); + c &= 1; + // if (c == 0) Serial.print("display.einkOff();\n"); + // if (c == 1) Serial.print("display.einkOn();\n"); + if (c == 0) + display.einkOff(); + if (c == 1) + display.einkOn(); + break; + + case 'R': + sscanf(s + 3, "%c", &b); + if (b == '?') + { + Serial.print("#R("); + Serial.print(display.getPanelState(), DEC); + // Serial.print(1, DEC); + Serial.println(")*"); + Serial.flush(); + } + break; + } + *s = 0; + *e = 0; + } } - } } -int hexToChar(char c) { - if (c >= '0' && c <= '9') - return c - '0'; - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - return -1; +int hexToChar(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + return -1; } diff --git a/src/include/Image.cpp b/src/include/Image.cpp index 1f24f05..df959c5 100644 --- a/src/include/Image.cpp +++ b/src/include/Image.cpp @@ -29,7 +29,7 @@ bool Image::drawImage(const char *path, int x, int y, bool dither, bool invert) else { if (strstr(path, ".bmp") != NULL) - return drawBitmapFromSD(path, x, y, dither, invert); + return drawBitmapFromSd(path, x, y, dither, invert); if (strstr(path, ".jpg") != NULL || strstr(path, ".jpeg") != NULL) return drawJpegFromSD(path, x, y, dither, invert); } @@ -43,143 +43,6 @@ bool Image::drawImage(const WiFiClient *s, int x, int y, int len, bool dither, b }; -// Loads first line in current dither buffer -void Image::ditherStart(uint8_t *pixelBuffer, uint8_t *bufferPtr, int w, bool invert, uint8_t bits) -{ - for (int i = 0; i < w; ++i) - if (bits == 24) - { - if (invert) - ditherBuffer[0][i] = ((255 - *(bufferPtr++)) * 2126 / 10000) + ((255 - *(bufferPtr++)) * 7152 / 10000) + - ((255 - *(bufferPtr++)) * 722 / 10000); - else - ditherBuffer[0][i] = - (*(bufferPtr++) * 2126 / 10000) + (*(bufferPtr++) * 7152 / 10000) + (*(bufferPtr++) * 722 / 10000); - } - else if (bits == 8) - { - if (invert) - ditherBuffer[0][i] = 255 - *(bufferPtr++); - else - ditherBuffer[0][i] = *(bufferPtr++); - } - if (bits == 4) - { - int _w = w / 8; - int paddingBits = w % 8; - - for (int i = 0; i < _w; ++i) - { - for (int n = 0; n < 4; n++) - { - uint8_t temp = *(bufferPtr++); - ditherBuffer[0][i * 8 + n * 2] = temp & 0xF0; - ditherBuffer[0][i * 8 + n * 2 + 1] = (temp & 0x0F) << 4; - - if (invert) - { - ditherBuffer[0][i * 8 + n * 2] = ~ditherBuffer[0][i * 8 + n * 2]; - ditherBuffer[0][i * 8 + n * 2 + 1] = ~ditherBuffer[0][i * 8 + n * 2 + 1]; - } - } - } - if (paddingBits) - { - uint32_t pixelRow = *(bufferPtr++) << 24 | *(bufferPtr++) << 16 | *(bufferPtr++) << 8 | *(bufferPtr++); - if (invert) - pixelRow = ~pixelRow; - for (int n = 0; n < paddingBits; n++) - { - ditherBuffer[0][_w * 8 + n] = (pixelRow & (0xFULL << ((7 - n) * 4))) >> ((7 - n) * 4 - 4); - } - } - } -} - -// Loads next line, after this ditherGetPixel can be called and alters values in next line -void Image::ditherLoadNextLine(uint8_t *pixelBuffer, uint8_t *bufferPtr, int w, bool invert, uint8_t bits) -{ - for (int i = 0; i < w; ++i) - { - if (bits == 24) - { - if (invert) - ditherBuffer[1][i] = ((255 - *(bufferPtr++)) * 2126 / 10000) + ((255 - *(bufferPtr++)) * 7152 / 10000) + - ((255 - *(bufferPtr++)) * 722 / 10000); - else - ditherBuffer[1][i] = - (*(bufferPtr++) * 2126 / 10000) + (*(bufferPtr++) * 7152 / 10000) + (*(bufferPtr++) * 722 / 10000); - } - else if (bits == 8) - { - if (invert) - ditherBuffer[1][i] = 255 - *(bufferPtr++); - else - ditherBuffer[1][i] = *(bufferPtr++); - } - } - if (bits == 4) - { - int _w = w / 8; - int paddingBits = w % 8; - - for (int i = 0; i < _w; ++i) - { - for (int n = 0; n < 4; n++) - { - uint8_t temp = *(bufferPtr++); - ditherBuffer[0][i * 8 + n * 2] = temp & 0xF0; - ditherBuffer[0][i * 8 + n * 2 + 1] = (temp & 0x0F) << 4; - - if (invert) - { - ditherBuffer[0][i * 8 + n * 2] = ~ditherBuffer[0][i * 8 + n * 2]; - ditherBuffer[0][i * 8 + n * 2 + 1] = ~ditherBuffer[0][i * 8 + n * 2 + 1]; - } - } - } - if (paddingBits) - { - uint32_t pixelRow = *(bufferPtr++) << 24 | *(bufferPtr++) << 16 | *(bufferPtr++) << 8 | *(bufferPtr++); - if (invert) - pixelRow = ~pixelRow; - for (int n = 0; n < paddingBits; n++) - { - ditherBuffer[1][_w * 8 + n] = (pixelRow & (0xFULL << (28 - n * 4))) >> (28 - n * 4 - 4); - } - } - } -} - -// Gets specific pixel, mainly at i, j is just used for bound checking when changing next line values -uint8_t Image::ditherGetPixel(int i, int j, int w, int h) -{ - uint8_t oldpixel = ditherBuffer[0][i]; - uint8_t newpixel = (oldpixel & B11100000); - - ditherBuffer[0][i] = newpixel; - - uint8_t quant_error = oldpixel - newpixel; - - if (i + 1 < w) - ditherBuffer[0][i + 1] = min(255, (int)ditherBuffer[0][i + 1] + (((int)quant_error * 7) >> 4)); - if (j + 1 < h && 0 <= i - 1) - ditherBuffer[1][i - 1] = min(255, (int)ditherBuffer[1][i - 1] + (((int)quant_error * 3) >> 4)); - if (j + 1 < h) - ditherBuffer[1][i + 0] = min(255, (int)ditherBuffer[1][i + 0] + (((int)quant_error * 5) >> 4)); - if (j + 1 < h && i + 1 < w) - ditherBuffer[1][i + 1] = min(255, (int)ditherBuffer[1][i + 1] + (((int)quant_error * 1) >> 4)); - - return newpixel; -} - -// Swaps current and next line, for next one to be overwritten -uint8_t Image::ditherSwap(int w) -{ - for (int i = 0; i < w; ++i) - ditherBuffer[0][i] = ditherBuffer[1][i]; -} - void Image::drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg) { int16_t byteWidth = (w + 7) >> 3; // Bitmap scanline pad = whole byte diff --git a/src/include/Image.h b/src/include/Image.h index 309d14e..a0a3f6b 100644 --- a/src/include/Image.h +++ b/src/include/Image.h @@ -28,8 +28,8 @@ class Image : virtual public Network uint16_t bg = 0xFFFF); void drawBitmap3Bit(int16_t _x, int16_t _y, const unsigned char *_p, int16_t _w, int16_t _h); - bool drawBitmapFromSD(SdFile *p, int x, int y, bool dither = 0, bool invert = 0); - bool drawBitmapFromSD(const char *fileName, int x, int y, bool dither = 0, bool invert = 0); + bool drawBitmapFromSd(SdFile *p, int x, int y, bool dither = 0, bool invert = 0); + bool drawBitmapFromSd(const char *fileName, int x, int y, bool dither = 0, bool invert = 0); bool drawBitmapFromWeb(WiFiClient *s, int x, int y, int len, bool dither = 0, bool invert = 0); bool drawBitmapFromWeb(const char *url, int x, int y, bool dither = 0, bool invert = 0); @@ -54,15 +54,16 @@ class Image : virtual public Network bool _invert); uint8_t pixelBuffer[800 * 4 + 5]; - uint8_t ditherBuffer[800 * 3 + 5][2]; - uint8_t pallete[128]; // 2 colors per byte, _###_### + uint8_t ditherBuffer[2][800 + 5]; + uint8_t ditherPalette[256]; // 8 bit colors + uint8_t palette[128]; // 2 3 bit colors per byte, _###_### bool legalBmp(bitmapHeader *bmpHeader); void ditherStart(uint8_t *pixelBuffer, uint8_t *bufferPtr, int w, bool invert, uint8_t bits); void ditherLoadNextLine(uint8_t *pixelBuffer, uint8_t *bufferPtr, int w, bool invert, uint8_t bits); - uint8_t ditherGetPixel(int i, int j, int w, int h); - uint8_t ditherSwap(int w); + uint8_t ditherGetPixel(uint8_t px, int i, int w, bool paletted); + void ditherSwap(int w); void readBmpHeader(uint8_t *buf, bitmapHeader *_h); void readBmpHeaderSd(SdFile *_f, bitmapHeader *_h); diff --git a/src/include/ImageBMP.cpp b/src/include/ImageBMP.cpp index 34dfcc0..5657de3 100644 --- a/src/include/ImageBMP.cpp +++ b/src/include/ImageBMP.cpp @@ -1,5 +1,12 @@ #include "Image.h" +bool Image::legalBmp(bitmapHeader *bmpHeader) +{ + return bmpHeader->signature == 0x4D42 && bmpHeader->compression == 0 && + (bmpHeader->color == 1 || bmpHeader->color == 4 || bmpHeader->color == 8 || bmpHeader->color == 16 || + bmpHeader->color == 24 || bmpHeader->color == 32); +} + void Image::readBmpHeaderSd(SdFile *_f, bitmapHeader *_h) { uint8_t header[55]; @@ -7,8 +14,8 @@ void Image::readBmpHeaderSd(SdFile *_f, bitmapHeader *_h) _f->rewind(); _f->read(header, 55); - uint16_t color = read16(header + 28); - uint32_t totalColors = read32(header + 46); + uint16_t color = READ16(header + 28); + uint32_t totalColors = READ32(header + 46); if (color <= 8) { @@ -31,16 +38,16 @@ void Image::readBmpHeaderSd(SdFile *_f, bitmapHeader *_h) void Image::readBmpHeader(uint8_t *buf, bitmapHeader *_h) { - _h->signature = read16(buf + 0); - _h->fileSize = read32(buf + 2); - _h->startRAW = read32(buf + 10); - _h->dibHeaderSize = read32(buf + 14); - _h->width = read32(buf + 18); - _h->height = read32(buf + 22); - _h->color = read16(buf + 28); - _h->compression = read32(buf + 30); + _h->signature = READ16(buf + 0); + _h->fileSize = READ32(buf + 2); + _h->startRAW = READ32(buf + 10); + _h->dibHeaderSize = READ32(buf + 14); + _h->width = READ32(buf + 18); + _h->height = READ32(buf + 22); + _h->color = READ16(buf + 28); + _h->compression = READ32(buf + 30); - uint32_t totalColors = read32(buf + 46); + uint32_t totalColors = READ32(buf + 46); uint8_t paletteRGB[1024]; @@ -50,110 +57,33 @@ void Image::readBmpHeader(uint8_t *buf, bitmapHeader *_h) totalColors = (1ULL << _h->color); memcpy(paletteRGB, buf + 53, totalColors * 4); - memset(pallete, 0, sizeof pallete); + memset(palette, 0, sizeof palette); for (int i = 0; i < totalColors; ++i) { - uint32_t c = read32(paletteRGB + (i << 2)); + uint32_t c = READ32(paletteRGB + (i << 2)); uint8_t r = (c & 0xFF000000) >> 24; uint8_t g = (c & 0x00FF0000) >> 16; uint8_t b = (c & 0x0000FF00) >> 8; - pallete[i >> 1] |= RGB3BIT(r, g, b) << (i & 1 ? 0 : 4); + palette[i >> 1] |= RGB3BIT(r, g, b) << (i & 1 ? 0 : 4); + ditherPalette[i] = RGB8BIT(r, g, b); } } }; -bool Image::legalBmp(bitmapHeader *bmpHeader) -{ - return bmpHeader->signature == 0x4D42 && bmpHeader->compression == 0 && - (bmpHeader->color == 1 || bmpHeader->color == 4 || bmpHeader->color == 8 || bmpHeader->color == 16 || - bmpHeader->color == 24 || bmpHeader->color == 32); -} -void Image::displayBmpLine(int16_t x, int16_t y, bitmapHeader *bmpHeader, bool dither, bool invert) -{ - int16_t w = bmpHeader->width, h = bmpHeader->height; - int8_t c = bmpHeader->color; - - startWrite(); - - for (int j = 0; j < w; ++j) - { - switch (c) - { - case 1: - // Should we ignore palette on 1 bit? - writePixel(x + j, y, (invert ^ (pallete[0] < pallete[1])) ^ !!(pixelBuffer[j >> 3] & (1 << (7 - j & 7)))); - break; - // as for 2 bit, literally cannot find an example online or in PS, so skipped - case 4: { - uint8_t px = pixelBuffer[j >> 1] & (j & 1 ? 0x0F : 0xF0) >> (j & 1 ? 0 : 4); - if (invert) - writePixel(x + j, y, 7 - (pallete[px >> 1] & (px & 1 ? 0x0F : 0xF0) >> (px & 1 ? 0 : 4))); - else - writePixel(x + j, y, pallete[px >> 1] & (px & 1 ? 0x0F : 0xF0) >> (px & 1 ? 0 : 4)); - break; - } - case 8: { - uint8_t px = pixelBuffer[j]; - if (invert) - writePixel(x + j, y, 7 - (pallete[px >> 1] & (px & 1 ? 0x0F : 0xF0) >> (px & 1 ? 0 : 4))); - else - writePixel(x + j, y, pallete[px >> 1] & (px & 1 ? 0x0F : 0xF0) >> (px & 1 ? 0 : 4)); - break; - } - case 16: { - uint16_t px = ((uint16_t)pixelBuffer[(j << 1) | 1] << 8) | pixelBuffer[(j << 1)]; - - uint8_t r = (px & 0x7C00) >> 7; - uint8_t g = (px & 0x3E0) >> 2; - uint8_t b = (px & 0x1F) << 3; - - if (invert) - writePixel(x + j, y, 7 - RGB3BIT(r, g, b)); - else - writePixel(x + j, y, RGB3BIT(r, g, b)); - break; - } - case 24: { - uint8_t r = pixelBuffer[j * 3]; - uint8_t g = pixelBuffer[j * 3 + 1]; - uint8_t b = pixelBuffer[j * 3 + 2]; - - if (invert) - writePixel(x + j, y, 7 - RGB3BIT(r, g, b)); - else - writePixel(x + j, y, RGB3BIT(r, g, b)); - break; - } - case 32: - uint8_t r = pixelBuffer[j * 4]; - uint8_t g = pixelBuffer[j * 4 + 1]; - uint8_t b = pixelBuffer[j * 4 + 2]; - - if (invert) - writePixel(x + j, y, 7 - RGB3BIT(r, g, b)); - else - writePixel(x + j, y, RGB3BIT(r, g, b)); - break; - } - } - - endWrite(); -} - -bool Image::drawBitmapFromSD(const char *fileName, int x, int y, bool dither, bool invert) +bool Image::drawBitmapFromSd(const char *fileName, int x, int y, bool dither, bool invert) { SdFile dat; if (dat.open(fileName, O_RDONLY)) - return drawBitmapFromSD(&dat, x, y, dither, invert); + return drawBitmapFromSd(&dat, x, y, dither, invert); else return 0; } -bool Image::drawBitmapFromSD(SdFile *p, int x, int y, bool dither, bool invert) +bool Image::drawBitmapFromSd(SdFile *p, int x, int y, bool dither, bool invert) { bitmapHeader bmpHeader; @@ -174,11 +104,11 @@ bool Image::drawBitmapFromSD(SdFile *p, int x, int y, bool dither, bool invert) int8_t c = bmpHeader.color; p->seekSet(bmpHeader.startRAW); - + if (dither) + memset(ditherBuffer, 0, sizeof ditherBuffer); for (int i = 0; i < h; ++i) { - int16_t n = rowSize(w, c); - + int16_t n = ROWSIZE(w, c); p->read(pixelBuffer, n); displayBmpLine(x, y + bmpHeader.height - i, &bmpHeader, dither, invert); } @@ -203,15 +133,125 @@ bool Image::drawBitmapFromWeb(const char *url, int x, int y, bool dither, bool i getDisplayMode() != INKPLATE_3BIT) selectDisplayMode(INKPLATE_3BIT); + if (dither) + memset(ditherBuffer, 0, sizeof ditherBuffer); uint8_t *bufferPtr = buf + bmpHeader.startRAW; for (int i = 0; i < bmpHeader.height; ++i) { - memcpy(pixelBuffer, bufferPtr, rowSize(bmpHeader.width, bmpHeader.color)); + memcpy(pixelBuffer, bufferPtr, ROWSIZE(bmpHeader.width, bmpHeader.color)); displayBmpLine(x, y + bmpHeader.height - i, &bmpHeader, dither, invert); - bufferPtr += rowSize(bmpHeader.width, bmpHeader.color); + bufferPtr += ROWSIZE(bmpHeader.width, bmpHeader.color); } free(buf); return 1; } + +void Image::displayBmpLine(int16_t x, int16_t y, bitmapHeader *bmpHeader, bool dither, bool invert) +{ + int16_t w = bmpHeader->width, h = bmpHeader->height; + int8_t c = bmpHeader->color; + + startWrite(); + for (int j = 0; j < w; ++j) + { + switch (c) + { + case 1: + writePixel(x + j, y, (invert ^ (palette[0] < palette[1])) ^ !!(pixelBuffer[j >> 3] & (1 << (7 - j & 7)))); + break; + // as for 2 bit, literally cannot find an example online or in PS, so skipped + case 4: { + uint8_t px = pixelBuffer[j >> 1] & (j & 1 ? 0x0F : 0xF0) >> (j & 1 ? 0 : 4); + uint8_t val; + + if (dither) + val = ditherGetPixel(px, j, w, 1); + else + val = palette[px >> 1] & (px & 1 ? 0x0F : 0xF0) >> (px & 1 ? 0 : 4); + + if (invert) + writePixel(x + j, y, 7 - val); + else + writePixel(x + j, y, val); + break; + } + case 8: { + uint8_t px = pixelBuffer[j]; + uint8_t val; + + if (dither) + val = ditherGetPixel(px, j, w, 1); + else + { + val = palette[px >> 1] & (px & 1 ? 0x0F : 0xF0) >> (px & 1 ? 0 : 4); + } + + if (invert) + writePixel(x + j, y, 7 - val); + else + writePixel(x + j, y, val); + break; + } + case 16: { + uint16_t px = ((uint16_t)pixelBuffer[(j << 1) | 1] << 8) | pixelBuffer[(j << 1)]; + + uint8_t r = (px & 0x7C00) >> 7; + uint8_t g = (px & 0x3E0) >> 2; + uint8_t b = (px & 0x1F) << 3; + + uint8_t val; + + if (dither) + val = ditherGetPixel(RGB8BIT(r, g, b), j, w, 0); + else + val = RGB3BIT(r, g, b); + + if (invert) + writePixel(x + j, y, 7 - val); + else + writePixel(x + j, y, val); + break; + } + case 24: { + uint8_t r = pixelBuffer[j * 3]; + uint8_t g = pixelBuffer[j * 3 + 1]; + uint8_t b = pixelBuffer[j * 3 + 2]; + + uint8_t val; + + if (dither) + val = ditherGetPixel(RGB8BIT(r, g, b), j, w, 0); + else + val = RGB3BIT(r, g, b); + + if (invert) + writePixel(x + j, y, 7 - val); + else + writePixel(x + j, y, val); + break; + } + case 32: + uint8_t r = pixelBuffer[j * 4]; + uint8_t g = pixelBuffer[j * 4 + 1]; + uint8_t b = pixelBuffer[j * 4 + 2]; + + uint8_t val; + + if (dither) + val = ditherGetPixel(RGB8BIT(r, g, b), j, w, 0); + else + val = RGB3BIT(r, g, b); + + if (invert) + writePixel(x + j, y, 7 - RGB3BIT(r, g, b)); + else + writePixel(x + j, y, RGB3BIT(r, g, b)); + break; + } + } + + ditherSwap(w); + endWrite(); +} diff --git a/src/include/ImageDither.cpp b/src/include/ImageDither.cpp new file mode 100644 index 0000000..d8bbd1f --- /dev/null +++ b/src/include/ImageDither.cpp @@ -0,0 +1,32 @@ +#include "Image.h" + +uint8_t Image::ditherGetPixel(uint8_t px, int i, int w, bool paletted) +{ + if (paletted) + px = ditherPalette[px]; + + uint8_t oldPixel = min((uint16_t)0xFF, (uint16_t)((uint16_t)ditherBuffer[0][i] + px)); + + uint8_t newPixel = oldPixel & B11100000; + uint8_t quantError = oldPixel - newPixel; + + ditherBuffer[1][i + 0] += (quantError * 5) >> 4; + if (i != w - 1) + { + ditherBuffer[0][i + 1] += (quantError * 7) >> 4; + ditherBuffer[1][i + 1] += (quantError * 1) >> 4; + } + if (i != 0) + ditherBuffer[1][i - 1] += (quantError * 3) >> 4; + + return newPixel >> 5; +} + +void Image::ditherSwap(int w) +{ + for (int i = 0; i < w; ++i) + { + ditherBuffer[0][i] = ditherBuffer[1][i]; + ditherBuffer[1][i] = 0; + } +} \ No newline at end of file diff --git a/src/include/defines.h b/src/include/defines.h index 2c19efa..7ad5dc8 100644 --- a/src/include/defines.h +++ b/src/include/defines.h @@ -16,8 +16,10 @@ #define PAD3 2 #define RGB3BIT(r, g, b) ((54UL * (r) + 183UL * (g) + 19UL * (b)) >> 13) -#define read32(c) (uint32_t)(*(c) | (*((c) + 1) << 8) | (*((c) + 2) << 16) | (*((c) + 3) << 24)); -#define read16(c) (uint16_t)(*(c) | (*((c) + 1) << 8)); -#define rowSize(w, c) (((int16_t)c * w + 31) >> 5) << 2 +#define RGB8BIT(r, g, b) ((54UL * (r) + 183UL * (g) + 19UL * (b)) >> 8) + +#define READ32(c) (uint32_t)(*(c) | (*((c) + 1) << 8) | (*((c) + 2) << 16) | (*((c) + 3) << 24)); +#define READ16(c) (uint16_t)(*(c) | (*((c) + 1) << 8)); +#define ROWSIZE(w, c) (((int16_t)c * w + 31) >> 5) << 2 #endif \ No newline at end of file diff --git a/test/bitmaps/bitmaps.ino b/test/bitmaps/bitmaps.ino index 5f8ddda..ca36d01 100644 --- a/test/bitmaps/bitmaps.ino +++ b/test/bitmaps/bitmaps.ino @@ -6,7 +6,17 @@ Inkplate display(INKPLATE_1BIT); +#define DELAYMS 1000 + const char *images[] = {"1bit.bmp", "4bit.bmp", "8bit.bmp", "16bit.bmp", "24bit.bmp", "32bit.bmp"}; +const char *imageUrls[] = { + "https://raw.githubusercontent.com/nitko12/Inkplate-revision/master/test/bitmaps/1bit.bmp", + "https://raw.githubusercontent.com/nitko12/Inkplate-revision/master/test/bitmaps/4bit.bmp", + "https://raw.githubusercontent.com/nitko12/Inkplate-revision/master/test/bitmaps/8bit.bmp", + "https://raw.githubusercontent.com/nitko12/Inkplate-revision/master/test/bitmaps/16bit.bmp", + "https://raw.githubusercontent.com/nitko12/Inkplate-revision/master/test/bitmaps/24bit.bmp", + "https://raw.githubusercontent.com/nitko12/Inkplate-revision/master/test/bitmaps/32bit.bmp", +}; const bool depth[] = {INKPLATE_1BIT, INKPLATE_3BIT, INKPLATE_3BIT, INKPLATE_3BIT, INKPLATE_3BIT, INKPLATE_3BIT}; void setup() @@ -91,7 +101,7 @@ void loop() display.clearDisplay(); delay(5000); - display.drawBitmapFromSD(images[j], 0, 0, dither, invert); + display.drawBitmapFromSd(images[j], 0, 0, dither, invert); display.display(); display.clearDisplay(); delay(5000); @@ -111,6 +121,7 @@ void loop() display.setCursor(100, 100); display.print("Displaying "); display.print(images[j]); + display.print(" from web"); if (!dither) display.print(" non"); display.print(" dithered and"); @@ -122,7 +133,7 @@ void loop() display.clearDisplay(); delay(5000); - display.drawBitmapFromSD(images[j], 0, 0, dither, invert); + display.drawBitmapFromWeb(imageUrls[j], 0, 0, dither, invert); display.display(); display.clearDisplay(); delay(5000); diff --git a/test/test.ino b/test/test.ino index adba110..00f1ebf 100644 --- a/test/test.ino +++ b/test/test.ino @@ -22,7 +22,17 @@ void loop() if (display.sdCardInit()) { - Serial.println(display.drawBitmapFromSD("8bit.bmp", 0, 0)); + Serial.println(display.drawBitmapFromSd("Lenna.bmp", 0, 0, 0, 0)); + } + display.display(); + + delay(5000); + + if (display.sdCardInit()) + { + int16_t t = millis(); + Serial.println(display.drawBitmapFromSd("Lenna.bmp", 0, 0, 1, 0)); + Serial.println(millis() - t); } display.display();