Added bmp dither.

This commit is contained in:
nitko12 2020-09-09 14:18:37 +02:00
parent e5ca63391a
commit 82a082003a
10 changed files with 656 additions and 640 deletions

View File

@ -2,11 +2,11 @@
1_Inkplate_WiFi_HTTP example for e-radionica.com Inkplate 6 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 For this example you will need USB cable, Inkplate 6 and stable WiFi Internet connection
Select "Inkplate 6(ESP32)" from Tools -> Board menu. 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/ 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 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. 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 15 July 2020 by e-radionica.com
*/ */
#include "Inkplate.h" //Include Inkplate library to the sketch #include "Inkplate.h" //Include Inkplate library to the sketch
#include <WiFi.h> //Include ESP32 WiFi library to our sketch #include <HTTPClient.h> //Include HTTP library to this sketch
#include <HTTPClient.h> //Include HTTP library to this sketch #include <WiFi.h> //Include ESP32 WiFi library to our sketch
#define ssid "" //Name of the WiFi network (SSID) that you want to connect Inkplate to #define ssid "" // Name of the WiFi network (SSID) that you want to connect Inkplate to
#define pass "" //Password of that WiFi network #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() { void setup()
display.begin(); //Init Inkplate library (you should call this function ONLY ONCE) {
display.clearDisplay(); //Clear frame buffer of display display.begin(); // Init Inkplate library (you should call this function ONLY ONCE)
display.display(); //Put clear image on display display.clearDisplay(); // Clear frame buffer of display
display.setTextSize(2); //Set text scaling to two (text will be two times bigger) display.display(); // Put clear image on display
display.setCursor(0, 0); //Set print position display.setTextSize(2); // Set text scaling to two (text will be two times bigger)
display.setTextColor(BLACK, WHITE); //Set text color to black and background color to white display.setCursor(0, 0); // Set print position
display.println("Scanning for WiFi networks..."); //Write text display.setTextColor(BLACK, WHITE); // Set text color to black and background color to white
display.display(); //Send everything to display (refresh display) 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 int n = WiFi.scanNetworks(); // Start searching WiFi networks and put the nubmer of found WiFi networks in variable
if (n == 0) { //If you did not find any network, show the message and stop the program. // n
display.print("No WiFi networks found!"); if (n == 0)
display.partialUpdate(); { // If you did not find any network, show the message and stop the program.
while (true); display.print("No WiFi networks found!");
} else { display.partialUpdate();
if (n > 10) n = 0; //If you did find, print name (SSID), encryption and signal strength of first 10 networks while (true)
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 else
} {
if (n > 10)
display.clearDisplay(); //Clear everything in frame buffer n = 0; // If you did find, print name (SSID), encryption and signal strength of first 10 networks
display.setCursor(0,0); //Set print cursor to new position for (int i = 0; i < n; i++)
display.print("Connecting to "); //Print the name of WiFi network {
display.print(ssid); display.print(WiFi.SSID(i));
WiFi.begin(ssid, pass); //Try to connect to WiFi network display.print((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? 'O' : '*');
while (WiFi.status() != WL_CONNECTED) { display.print('\n');
delay(1000); //While it is connecting to network, display dot every second, just to know that Inkplate is alive. display.print(WiFi.RSSI(i), DEC);
display.print('.'); }
display.partialUpdate(); display.partialUpdate(); //(Partial) refresh thescreen
} }
display.print("connected"); //If it's connected, notify user
display.partialUpdate(); display.clearDisplay(); // Clear everything in frame buffer
display.setCursor(0, 0); // Set print cursor to new position
HTTPClient http; display.print("Connecting to "); // Print the name of WiFi network
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 :)) display.print(ssid);
if(http.GET()>0) { //If connection was successful, try to read content of the Web page and display it on screen WiFi.begin(ssid, pass); // Try to connect to WiFi network
String htmlText; while (WiFi.status() != WL_CONNECTED)
htmlText = http.getString(); {
display.setTextSize(1); //Set smaller text size, so everything can fit on screen delay(1000); // While it is connecting to network, display dot every second, just to know that Inkplate is
display.clearDisplay(); // alive.
display.setCursor(0, 0); display.print('.');
display.print(htmlText); display.partialUpdate();
display.display(); }
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() { void loop()
//Nothing {
// Nothing
} }

View File

@ -21,52 +21,60 @@
15 July 2020 by e-radionica.com 15 July 2020 by e-radionica.com
*/ */
#include "Inkplate.h" //Include Inkplate library to the sketch #include "Inkplate.h" //Include Inkplate library to the sketch
#include "SdFat.h" //Include library for SD card #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) Inkplate display(
SdFile file; //Create SdFile object used for accessing files on SD card 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); Serial.begin(115200);
display.begin(); //Init Inkplate library (you should call this function ONLY ONCE) display.begin(); // Init Inkplate library (you should call this function ONLY ONCE)
display.clearDisplay(); //Clear frame buffer of display display.clearDisplay(); // Clear frame buffer of display
display.display(); //Put clear image on display display.display(); // Put clear image on display
//Init SD card. Display if SD card is init propery or not. // Init SD card. Display if SD card is init propery or not.
if (display.sdCardInit()) { if (display.sdCardInit())
{
display.println("SD Card OK! Reading image..."); display.println("SD Card OK! Reading image...");
display.partialUpdate(); display.partialUpdate();
//If card is properly init, try to load image and display it on e-paper at position X=0, Y=0 // 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 // NOTE: Both drawBitmapFromSd methods allow for an optional fourth "invert" parameter. Setting this parameter
//will flip all colors on the image, making black white and white black. This may be necessary when exporting bitmaps from // to true will flip all colors on the image, making black white and white black. This may be necessary when
//certain softwares. // exporting bitmaps from certain softwares.
if (!display.drawBitmapFromSD("image1.bmp", 0, 0, 1)) { 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! // If is something failed (wrong filename or wrong bitmap format), write error message on the screen.
//You can turn of dithering for somewhat faster image load by changing the last 1 to 0, or removing the 1 argument completely // 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.println("Image open error");
display.display(); display.display();
} }
display.display(); display.display();
} }
else { else
//If SD card init not success, display error on screen and stop the program (using infinite loop) {
// If SD card init not success, display error on screen and stop the program (using infinite loop)
display.println("SD Card error!"); display.println("SD Card error!");
display.partialUpdate(); display.partialUpdate();
while (true); while (true)
;
} }
delay(5000); delay(5000);
//Now try to load image using SdFat library class (for more advanced users) and display image on epaper. // 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)) { if (file.open("image2.bmp", O_RDONLY))
display.drawBitmapFromSD(&file, 0, 0); {
display.drawBitmapFromSd(&file, 0, 0);
display.display(); display.display();
} }
} }
void loop() { void loop()
//Nothing... {
// Nothing...
} }

View File

@ -1,24 +1,25 @@
/* /*
Inkplate_Slave_Mode sketch for e-radionica.com Inkplate 6 Inkplate_Slave_Mode sketch for e-radionica.com Inkplate 6
Select "Inkplate 6(ESP32)" from Tools -> Board menu. 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/ 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). 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! 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. 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. 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. 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) 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. 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 Don't forget you need to send #L(1)* after each command to show it on the display
(equal to display.display()). (equal to display.display()).
Want to learn more about Inkplate? Visit www.inkplate.io Want to learn more about Inkplate? Visit www.inkplate.io
Looking to get support? Write on our forums: http://forum.e-radionica.com/en/ 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 #define BUFFER_SIZE 1000
char commandBuffer[BUFFER_SIZE + 1]; char commandBuffer[BUFFER_SIZE + 1];
char strTemp[2001]; char strTemp[2001];
void setup() { void setup()
display.begin(); {
Serial.begin(115200); display.begin();
memset(commandBuffer, 0, BUFFER_SIZE); Serial.begin(115200);
memset(commandBuffer, 0, BUFFER_SIZE);
} }
void loop() { void loop()
// put your main code here, to run repeatedly: {
if (Serial.available()) { // put your main code here, to run repeatedly:
while (Serial.available()) { if (Serial.available())
for (int i = 0; i < (BUFFER_SIZE - 1); i++) { {
commandBuffer[i] = commandBuffer[i + 1]; while (Serial.available())
} {
commandBuffer[BUFFER_SIZE - 1] = Serial.read(); for (int i = 0; i < (BUFFER_SIZE - 1); i++)
} {
} commandBuffer[i] = commandBuffer[i + 1];
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) { commandBuffer[BUFFER_SIZE - 1] = Serial.read();
Serial.println("#J(1)*"); }
Serial.flush(); }
} char *s = NULL;
} char *e = NULL;
break; for (int i = 0; i < BUFFER_SIZE; i++)
{
case 'K': if (commandBuffer[i] == '#' && s == NULL)
sscanf(s + 3, "%c", &b); s = &commandBuffer[i];
if (b == '1') { if (commandBuffer[i] == '*' && e == NULL)
//Serial.print("display.clearDisplay();\n"); e = &commandBuffer[i];
display.clearDisplay(); }
} if (s != NULL && e != NULL)
break; {
if ((e - s) > 0)
case 'L': {
sscanf(s + 3, "%c", &b); int x, x1, x2, y, y1, y2, x3, y3, l, c, w, h, r, n;
if (b == '1') { char b;
//Serial.print("display.display();\n"); char temp[150];
display.display(); switch (*(s + 1))
} {
break; case '?':
Serial.print("OK");
case 'M': break;
sscanf(s + 3, "%d,%d,%d", &y1, &x2, &y2);
//sprintf(temp, "display.partialUpdate(%d, %d, %d);\n", y1, x2, y2); case '0':
//Serial.print(temp); sscanf(s + 3, "%d,%d,%d", &x, &y, &c);
display.partialUpdate(); // sprintf(temp, "display.drawPixel(%d, %d, %d)\n\r", x, y, c);
break; // Serial.print(temp);
display.drawPixel(x, y, c);
case 'N': break;
sscanf(s + 3, "%c", &b);
if (b == '?') { case '1':
Serial.print("#N("); sscanf(s + 3, "%d,%d,%d,%d,%d", &x1, &y1, &x2, &y2, &c);
Serial.print(display.readTemperature(), DEC); // sprintf(temp, "display.drawLine(%d, %d, %d, %d, %d)\n\r", x1, y1, x2, y2, c);
//Serial.print(23, DEC); // Serial.print(temp);
Serial.println(")*"); display.drawLine(x1, y1, x2, y2, c);
Serial.flush(); break;
}
break; case '2':
sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &l, &c);
case 'O': // sprintf(temp, "display.drawFastVLine(%d, %d, %d, %d)\n\r", x, y, l, c);
sscanf(s + 3, "%d", &c); // Serial.print(temp);
if (c >= 0 && c <= 2) { display.drawFastVLine(x, y, l, c);
Serial.print("#O("); break;
Serial.print(display.readTouchpad(c), DEC);
//Serial.print(0, DEC); case '3':
Serial.println(")*"); sscanf(s + 3, "%d,%d,%d,%d", &x, &y, &l, &c);
Serial.flush(); // sprintf(temp, "display.drawFastHLine(%d, %d, %d, %d)\n\r", x, y, l, c);
} // Serial.print(temp);
break; display.drawFastHLine(x, y, l, c);
break;
case 'P':
sscanf(s + 3, "%c", &b); case '4':
if (b == '?') { sscanf(s + 3, "%d,%d,%d,%d,%d", &x, &y, &w, &h, &c);
Serial.print("#P("); // sprintf(temp, "display.drawRect(%d, %d, %d, %d, %d)\n\r", x, y, w, h, c);
Serial.print(display.readBattery(), 2); // Serial.print(temp);
//Serial.print(3.54, 2); display.drawRect(x, y, w, h, c);
Serial.println(")*"); break;
Serial.flush();
} case '5':
break; 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);
case 'Q': // Serial.print(temp);
sscanf(s + 3, "%d", &c); display.drawCircle(x, y, r, c);
c &= 1; break;
//if (c == 0) Serial.print("display.einkOff();\n");
//if (c == 1) Serial.print("display.einkOn();\n"); case '6':
if (c == 0) display.einkOff(); sscanf(s + 3, "%d,%d,%d,%d,%d,%d,%d", &x1, &y1, &x2, &y2, &x3, &y3, &c);
if (c == 1) display.einkOn(); // sprintf(temp, "display.drawTriangle(%d, %d, %d, %d, %d, %d, %d)\n\r", x1, y1, x2, y2, x3, y3, c);
break; // Serial.print(temp);
display.drawTriangle(x1, y1, x2, y2, x3, y3, c);
case 'R': break;
sscanf(s + 3, "%c", &b);
if (b == '?') { case '7':
Serial.print("#R("); sscanf(s + 3, "%d,%d,%d,%d,%d,%d", &x, &y, &w, &h, &r, &c);
Serial.print(display.getPanelState(), DEC); // sprintf(temp, "display.drawRoundRect(%d, %d, %d, %d, %d, %d)\n\r", x, y, w, h, r, c);
//Serial.print(1, DEC); // Serial.print(temp);
Serial.println(")*"); display.drawRoundRect(x, y, w, h, r, c);
Serial.flush(); break;
}
break; case '8':
} sscanf(s + 3, "%d,%d,%d,%d,%d", &x, &y, &w, &h, &c);
*s = 0; // sprintf(temp, "display.fillRect(%d, %d, %d, %d, %d)\n\r", x, y, w, h, c);
*e = 0; // 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) { int hexToChar(char c)
if (c >= '0' && c <= '9') {
return c - '0'; if (c >= '0' && c <= '9')
if (c >= 'A' && c <= 'F') return c - '0';
return c - 'A' + 10; if (c >= 'A' && c <= 'F')
if (c >= 'a' && c <= 'f') return c - 'A' + 10;
return c - 'a' + 10; if (c >= 'a' && c <= 'f')
return -1; return c - 'a' + 10;
return -1;
} }

View File

@ -29,7 +29,7 @@ bool Image::drawImage(const char *path, int x, int y, bool dither, bool invert)
else else
{ {
if (strstr(path, ".bmp") != NULL) 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) if (strstr(path, ".jpg") != NULL || strstr(path, ".jpeg") != NULL)
return drawJpegFromSD(path, x, y, dither, invert); 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) 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 int16_t byteWidth = (w + 7) >> 3; // Bitmap scanline pad = whole byte

View File

@ -28,8 +28,8 @@ class Image : virtual public Network
uint16_t bg = 0xFFFF); uint16_t bg = 0xFFFF);
void drawBitmap3Bit(int16_t _x, int16_t _y, const unsigned char *_p, int16_t _w, int16_t _h); 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(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(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(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); 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); bool _invert);
uint8_t pixelBuffer[800 * 4 + 5]; uint8_t pixelBuffer[800 * 4 + 5];
uint8_t ditherBuffer[800 * 3 + 5][2]; uint8_t ditherBuffer[2][800 + 5];
uint8_t pallete[128]; // 2 colors per byte, _###_### uint8_t ditherPalette[256]; // 8 bit colors
uint8_t palette[128]; // 2 3 bit colors per byte, _###_###
bool legalBmp(bitmapHeader *bmpHeader); bool legalBmp(bitmapHeader *bmpHeader);
void ditherStart(uint8_t *pixelBuffer, uint8_t *bufferPtr, int w, bool invert, uint8_t bits); 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); 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 ditherGetPixel(uint8_t px, int i, int w, bool paletted);
uint8_t ditherSwap(int w); void ditherSwap(int w);
void readBmpHeader(uint8_t *buf, bitmapHeader *_h); void readBmpHeader(uint8_t *buf, bitmapHeader *_h);
void readBmpHeaderSd(SdFile *_f, bitmapHeader *_h); void readBmpHeaderSd(SdFile *_f, bitmapHeader *_h);

View File

@ -1,5 +1,12 @@
#include "Image.h" #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) void Image::readBmpHeaderSd(SdFile *_f, bitmapHeader *_h)
{ {
uint8_t header[55]; uint8_t header[55];
@ -7,8 +14,8 @@ void Image::readBmpHeaderSd(SdFile *_f, bitmapHeader *_h)
_f->rewind(); _f->rewind();
_f->read(header, 55); _f->read(header, 55);
uint16_t color = read16(header + 28); uint16_t color = READ16(header + 28);
uint32_t totalColors = read32(header + 46); uint32_t totalColors = READ32(header + 46);
if (color <= 8) if (color <= 8)
{ {
@ -31,16 +38,16 @@ void Image::readBmpHeaderSd(SdFile *_f, bitmapHeader *_h)
void Image::readBmpHeader(uint8_t *buf, bitmapHeader *_h) void Image::readBmpHeader(uint8_t *buf, bitmapHeader *_h)
{ {
_h->signature = read16(buf + 0); _h->signature = READ16(buf + 0);
_h->fileSize = read32(buf + 2); _h->fileSize = READ32(buf + 2);
_h->startRAW = read32(buf + 10); _h->startRAW = READ32(buf + 10);
_h->dibHeaderSize = read32(buf + 14); _h->dibHeaderSize = READ32(buf + 14);
_h->width = read32(buf + 18); _h->width = READ32(buf + 18);
_h->height = read32(buf + 22); _h->height = READ32(buf + 22);
_h->color = read16(buf + 28); _h->color = READ16(buf + 28);
_h->compression = read32(buf + 30); _h->compression = READ32(buf + 30);
uint32_t totalColors = read32(buf + 46); uint32_t totalColors = READ32(buf + 46);
uint8_t paletteRGB[1024]; uint8_t paletteRGB[1024];
@ -50,110 +57,33 @@ void Image::readBmpHeader(uint8_t *buf, bitmapHeader *_h)
totalColors = (1ULL << _h->color); totalColors = (1ULL << _h->color);
memcpy(paletteRGB, buf + 53, totalColors * 4); memcpy(paletteRGB, buf + 53, totalColors * 4);
memset(pallete, 0, sizeof pallete); memset(palette, 0, sizeof palette);
for (int i = 0; i < totalColors; ++i) 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 r = (c & 0xFF000000) >> 24;
uint8_t g = (c & 0x00FF0000) >> 16; uint8_t g = (c & 0x00FF0000) >> 16;
uint8_t b = (c & 0x0000FF00) >> 8; 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) bool Image::drawBitmapFromSd(const char *fileName, int x, int y, 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)
{ {
SdFile dat; SdFile dat;
if (dat.open(fileName, O_RDONLY)) if (dat.open(fileName, O_RDONLY))
return drawBitmapFromSD(&dat, x, y, dither, invert); return drawBitmapFromSd(&dat, x, y, dither, invert);
else else
return 0; 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; bitmapHeader bmpHeader;
@ -174,11 +104,11 @@ bool Image::drawBitmapFromSD(SdFile *p, int x, int y, bool dither, bool invert)
int8_t c = bmpHeader.color; int8_t c = bmpHeader.color;
p->seekSet(bmpHeader.startRAW); p->seekSet(bmpHeader.startRAW);
if (dither)
memset(ditherBuffer, 0, sizeof ditherBuffer);
for (int i = 0; i < h; ++i) for (int i = 0; i < h; ++i)
{ {
int16_t n = rowSize(w, c); int16_t n = ROWSIZE(w, c);
p->read(pixelBuffer, n); p->read(pixelBuffer, n);
displayBmpLine(x, y + bmpHeader.height - i, &bmpHeader, dither, invert); 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) getDisplayMode() != INKPLATE_3BIT)
selectDisplayMode(INKPLATE_3BIT); selectDisplayMode(INKPLATE_3BIT);
if (dither)
memset(ditherBuffer, 0, sizeof ditherBuffer);
uint8_t *bufferPtr = buf + bmpHeader.startRAW; uint8_t *bufferPtr = buf + bmpHeader.startRAW;
for (int i = 0; i < bmpHeader.height; ++i) 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); displayBmpLine(x, y + bmpHeader.height - i, &bmpHeader, dither, invert);
bufferPtr += rowSize(bmpHeader.width, bmpHeader.color); bufferPtr += ROWSIZE(bmpHeader.width, bmpHeader.color);
} }
free(buf); free(buf);
return 1; 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();
}

View File

@ -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;
}
}

View File

@ -16,8 +16,10 @@
#define PAD3 2 #define PAD3 2
#define RGB3BIT(r, g, b) ((54UL * (r) + 183UL * (g) + 19UL * (b)) >> 13) #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 RGB8BIT(r, g, b) ((54UL * (r) + 183UL * (g) + 19UL * (b)) >> 8)
#define read16(c) (uint16_t)(*(c) | (*((c) + 1) << 8));
#define rowSize(w, c) (((int16_t)c * w + 31) >> 5) << 2 #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 #endif

View File

@ -6,7 +6,17 @@
Inkplate display(INKPLATE_1BIT); Inkplate display(INKPLATE_1BIT);
#define DELAYMS 1000
const char *images[] = {"1bit.bmp", "4bit.bmp", "8bit.bmp", "16bit.bmp", "24bit.bmp", "32bit.bmp"}; 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}; const bool depth[] = {INKPLATE_1BIT, INKPLATE_3BIT, INKPLATE_3BIT, INKPLATE_3BIT, INKPLATE_3BIT, INKPLATE_3BIT};
void setup() void setup()
@ -91,7 +101,7 @@ void loop()
display.clearDisplay(); display.clearDisplay();
delay(5000); delay(5000);
display.drawBitmapFromSD(images[j], 0, 0, dither, invert); display.drawBitmapFromSd(images[j], 0, 0, dither, invert);
display.display(); display.display();
display.clearDisplay(); display.clearDisplay();
delay(5000); delay(5000);
@ -111,6 +121,7 @@ void loop()
display.setCursor(100, 100); display.setCursor(100, 100);
display.print("Displaying "); display.print("Displaying ");
display.print(images[j]); display.print(images[j]);
display.print(" from web");
if (!dither) if (!dither)
display.print(" non"); display.print(" non");
display.print(" dithered and"); display.print(" dithered and");
@ -122,7 +133,7 @@ void loop()
display.clearDisplay(); display.clearDisplay();
delay(5000); delay(5000);
display.drawBitmapFromSD(images[j], 0, 0, dither, invert); display.drawBitmapFromWeb(imageUrls[j], 0, 0, dither, invert);
display.display(); display.display();
display.clearDisplay(); display.clearDisplay();
delay(5000); delay(5000);

View File

@ -22,7 +22,17 @@ void loop()
if (display.sdCardInit()) 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(); display.display();