Update Inkplate.cpp

This commit is contained in:
David Zovko 2020-07-31 11:54:45 +02:00 committed by GitHub
parent 5e961c5d0c
commit 8d19ff62ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 322 additions and 58 deletions

View File

@ -1,8 +1,9 @@
#include "Inkplate.h"
#include <stdlib.h> #include <stdlib.h>
#include "Adafruit_GFX.h" #include "Adafruit_GFX.h"
#include "WiFi.h"
#include "HTTPClient.h"
#include "Inkplate.h"
Adafruit_MCP23017 mcp; Adafruit_MCP23017 mcp;
SPIClass spi2(HSPI); SPIClass spi2(HSPI);
SdFat sd(&spi2); SdFat sd(&spi2);
@ -87,12 +88,12 @@ void Inkplate::begin(void) {
//Draw function, used by Adafruit GFX. //Draw function, used by Adafruit GFX.
void Inkplate::drawPixel(int16_t x0, int16_t y0, uint16_t color) { void Inkplate::drawPixel(int16_t x0, int16_t y0, uint16_t color) {
if (x0 > _width - 1 || y0 > _height - 1 || x0 < 0 || y0 < 0) return; if (x0 > 799 || y0 > 599 || x0 < 0 || y0 < 0) return;
switch (_rotation) { switch (_rotation) {
case 1: case 1:
_swap_int16_t(x0, y0); _swap_int16_t(x0, y0);
x0 = _height - x0 - 1; x0 = _width - x0 - 1;
break; break;
case 2: case 2:
x0 = _width - x0 - 1; x0 = _width - x0 - 1;
@ -237,7 +238,7 @@ void Inkplate::drawBitmap3Bit(int16_t _x, int16_t _y, const unsigned char* _p, i
void Inkplate::setRotation(uint8_t r) { void Inkplate::setRotation(uint8_t r) {
_rotation = r % 4; _rotation = r % 4;
switch (_rotation) { switch (rotation) {
case 0: case 0:
_width = E_INK_WIDTH; _width = E_INK_WIDTH;
_height = E_INK_HEIGHT; _height = E_INK_HEIGHT;
@ -329,13 +330,13 @@ uint8_t Inkplate::getDisplayMode() {
return _displayMode; return _displayMode;
} }
int Inkplate::drawBitmapFromSD(SdFile* p, int x, int y) { int Inkplate::drawBitmapFromSD(SdFile* p, int x, int y, bool invert) {
if(sdCardOk == 0) return 0; if(sdCardOk == 0) return 0;
struct bitmapHeader bmpHeader; struct bitmapHeader bmpHeader;
readBmpHeader(p, &bmpHeader); readBmpHeaderSd(p, &bmpHeader);
if (bmpHeader.signature != 0x4D42 || bmpHeader.compression != 0 || !(bmpHeader.color == 1 || bmpHeader.color == 24)) return 0; if (bmpHeader.signature != 0x4D42 || bmpHeader.compression != 0 || !(bmpHeader.color == 1 || bmpHeader.color == 4 || bmpHeader.color == 8 || bmpHeader.color == 24)) return 0;
if ((bmpHeader.color == 24 || bmpHeader.color == 32) && getDisplayMode() != INKPLATE_3BIT) { if ((bmpHeader.color == 4 || bmpHeader.color == 8 || bmpHeader.color == 24 || bmpHeader.color == 32) && getDisplayMode() != INKPLATE_3BIT) {
selectDisplayMode(INKPLATE_3BIT); selectDisplayMode(INKPLATE_3BIT);
} }
@ -343,68 +344,71 @@ int Inkplate::drawBitmapFromSD(SdFile* p, int x, int y) {
selectDisplayMode(INKPLATE_1BIT); selectDisplayMode(INKPLATE_1BIT);
} }
if (bmpHeader.color == 1) drawMonochromeBitmap(p, bmpHeader, x, y); if (bmpHeader.color == 1) drawMonochromeBitmapSd(p, bmpHeader, x, y, invert);
if (bmpHeader.color == 24) drawGrayscaleBitmap24(p, bmpHeader, x, y); if (bmpHeader.color == 4) drawGrayscaleBitmap4Sd(p, bmpHeader, x, y, invert);
if (bmpHeader.color == 8) drawGrayscaleBitmap8Sd(p, bmpHeader, x, y, invert);
if (bmpHeader.color == 24) drawGrayscaleBitmap24Sd(p, bmpHeader, x, y, invert);
return 1; return 1;
} }
int Inkplate::drawBitmapFromSD(char* fileName, int x, int y) { int Inkplate::drawBitmapFromSD(char* fileName, int x, int y, bool invert) {
if(sdCardOk == 0) return 0; if(sdCardOk == 0) return 0;
SdFile dat; SdFile dat;
if (dat.open(fileName, O_RDONLY)) { if (dat.open(fileName, O_RDONLY)) {
return drawBitmapFromSD(&dat, x, y); return drawBitmapFromSD(&dat, x, y, invert);
} else { } else {
return 0; return 0;
} }
} }
void Inkplate::drawThickLine(int x1, int y1, int x2, int y2, int color, float thickness) int Inkplate::drawBitmapFromWeb(WiFiClient* s, int x, int y, int len, bool invert) {
{ struct bitmapHeader bmpHeader;
float deg = atan2f((float)(y2 - y1), (float)(x2 - x1)); readBmpHeaderWeb(s, &bmpHeader);
if (bmpHeader.signature != 0x4D42 || bmpHeader.compression != 0 || !(bmpHeader.color == 1 || bmpHeader.color == 4 || bmpHeader.color == 8 || bmpHeader.color == 24)) return 0;
float l1 = tan(deg); if ((bmpHeader.color == 4 || bmpHeader.color == 8 || bmpHeader.color == 24 || bmpHeader.color == 32) && getDisplayMode() != INKPLATE_3BIT) {
float k1 = (float)y1 - l1 * (float)x1; selectDisplayMode(INKPLATE_3BIT);
}
float degShift = (l1 < 0 ? M_PI_2 : -M_PI_2); if (bmpHeader.color == 1 && getDisplayMode() != INKPLATE_1BIT) {
selectDisplayMode(INKPLATE_1BIT);
}
if (bmpHeader.color == 1) drawMonochromeBitmapWeb(s, bmpHeader, x, y, len, invert);
if (bmpHeader.color == 4) drawGrayscaleBitmap4Web(s, bmpHeader, x, y, len, invert);
if (bmpHeader.color == 8) drawGrayscaleBitmap8Web(s, bmpHeader, x, y, len, invert);
if (bmpHeader.color == 24) drawGrayscaleBitmap24Web(s, bmpHeader, x, y, len, invert);
int x3 = (int)round((float)x1 + thickness / 2.0 * cos(deg + degShift)); return 1;
int y3 = (int)round((float)y1 + thickness / 2.0 * sin(deg + degShift));
int x4 = (int)round((float)x2 + thickness / 2.0 * cos(deg + degShift));
int y4 = (int)round((float)y2 + thickness / 2.0 * sin(deg + degShift));
x1 = (int)round((float)x1 + thickness / 2.0 * cos(deg - degShift));
y1 = (int)round((float)y1 + thickness / 2.0 * sin(deg - degShift));
x2 = (int)round((float)x2 + thickness / 2.0 * cos(deg - degShift));
y2 = (int)round((float)y2 + thickness / 2.0 * sin(deg - degShift));
fillTriangle(x1, y1, x2, y2, x3, y3, color);
fillTriangle(x2, y2, x4, y4, x3, y3, color);
} }
void Inkplate::drawGradientLine(int x1, int y1, int x2, int y2, int color1, int color2, float thickness) { int Inkplate::drawBitmapFromWeb(char* url, int x, int y, bool invert) {
int n = color2 - color1; if (WiFi.status() != WL_CONNECTED) return 0;
int ret = 0;
float px = (float)(x2 - x1) / (float)n; bool sleep = WiFi.getSleep();
float py = (float)(y2 - y1) / (float)n; WiFi.setSleep(false);
for (int i = 0; i < n; ++i) HTTPClient http;
{ http.getStream().setNoDelay(true);
if (abs(thickness + 1) < 0.1) http.getStream().setTimeout(1);
drawLine((int)((float)x1 + (float)i * px), (int)((float)y1 + (float)i * py), http.begin(url);
(int)((float)x1 + (float)(i + 1) * px), (int)((float)y1 + (float)(i + 1) * py),
color1 + i); int httpCode = http.GET();
else if (httpCode == 200) {
drawThickLine((int)((float)x1 + (float)i * px), (int)((float)y1 + (float)i * py), int32_t len = http.getSize();
(int)((float)x1 + (float)(i + 1) * px), (int)((float)y1 + (float)(i + 1) * py), if (len > 0) {
color1 + i, WiFiClient * dat = http.getStreamPtr();
thickness); ret = drawBitmapFromWeb(dat, x, y, len, invert);
} }
}
http.end();
WiFi.setSleep(sleep);
return ret;
} }
int Inkplate::sdCardInit() { int Inkplate::sdCardInit() {
spi2.begin(14, 12, 13, 15); spi2.begin(14, 12, 13, 15);
sdCardOk = sd.begin(15, SD_SCK_MHZ(25)); sdCardOk = sd.begin(15, SD_SCK_MHZ(25));
@ -815,7 +819,7 @@ uint16_t Inkplate::read16(uint8_t* c) {
return (*(c) | (*(c + 1) << 8)); return (*(c) | (*(c + 1) << 8));
} }
void Inkplate::readBmpHeader(SdFile *_f, struct bitmapHeader *_h) { void Inkplate::readBmpHeaderSd(SdFile *_f, struct bitmapHeader *_h) {
uint8_t header[100]; uint8_t header[100];
_f->rewind(); _f->rewind();
_f->read(header, 100); _f->read(header, 100);
@ -830,7 +834,21 @@ void Inkplate::readBmpHeader(SdFile *_f, struct bitmapHeader *_h) {
return; return;
} }
int Inkplate::drawMonochromeBitmap(SdFile *f, struct bitmapHeader bmpHeader, int x, int y) { void Inkplate::readBmpHeaderWeb(WiFiClient *_s, struct bitmapHeader *_h) {
uint8_t header[34];
_s->read(header, 34);
_h->signature = read16(header + 0);
_h->fileSize = read32(header + 2);
_h->startRAW = read32(header + 10);
_h->dibHeaderSize = read32(header + 14);
_h->width = read32(header + 18);
_h->height = read32(header + 22);
_h->color = read16(header + 28);
_h->compression = read32(header + 30);
return;
}
int Inkplate::drawMonochromeBitmapSd(SdFile *f, struct bitmapHeader bmpHeader, int x, int y, bool invert) {
int w = bmpHeader.width; int w = bmpHeader.width;
int h = bmpHeader.height; int h = bmpHeader.height;
uint8_t paddingBits = w % 32; uint8_t paddingBits = w % 32;
@ -841,14 +859,18 @@ int Inkplate::drawMonochromeBitmap(SdFile *f, struct bitmapHeader bmpHeader, int
for (j = 0; j < h; j++) { for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) { for (i = 0; i < w; i++) {
uint32_t pixelRow = f->read() << 24 | f->read() << 16 | f->read() << 8 | f->read(); uint32_t pixelRow = f->read() << 24 | f->read() << 16 | f->read() << 8 | f->read();
if (invert)
pixelRow = ~pixelRow;
for (int n = 0; n < 32; n++) { for (int n = 0; n < 32; n++) {
drawPixel((i * 32) + n + x, h - j + y, !(pixelRow & (1ULL << (31 - n)))); drawPixel((i * 32) + n + x, h - 1 - j + y, !(pixelRow & (1ULL << (31 - n))));
} }
} }
if (paddingBits) { if (paddingBits) {
uint32_t pixelRow = f->read() << 24 | f->read() << 16 | f->read() << 8 | f->read(); uint32_t pixelRow = f->read() << 24 | f->read() << 16 | f->read() << 8 | f->read();
if (invert)
pixelRow = ~pixelRow;
for (int n = 0; n < paddingBits; n++) { for (int n = 0; n < paddingBits; n++) {
drawPixel((i * 32) + n + x, h - j + y, !(pixelRow & (1ULL << (31 - n)))); drawPixel((i * 32) + n + x, h - 1 - j + y, !(pixelRow & (1ULL << (31 - n))));
} }
} }
} }
@ -856,7 +878,62 @@ int Inkplate::drawMonochromeBitmap(SdFile *f, struct bitmapHeader bmpHeader, int
return 1; return 1;
} }
int Inkplate::drawGrayscaleBitmap24(SdFile *f, struct bitmapHeader bmpHeader, int x, int y) { int Inkplate::drawGrayscaleBitmap4Sd(SdFile *f, struct bitmapHeader bmpHeader, int x, int y, bool invert) {
int w = bmpHeader.width;
int h = bmpHeader.height;
uint8_t paddingBits = w % 8;
w /= 8;
f->seekSet(bmpHeader.startRAW);
int i, j;
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
uint32_t pixelRow = f->read() << 24 | f->read() << 16 | f->read() << 8 | f->read();
if (invert)
pixelRow = ~pixelRow;
for (int n = 0; n < 8; n++) {
drawPixel((i * 8) + n + x, h - 1 - j + y, (pixelRow & (0xFULL << (28 - n*4))) >> (28 - n*4 + 1));
}
}
if (paddingBits) {
uint32_t pixelRow = f->read() << 24 | f->read() << 16 | f->read() << 8 | f->read();
if (invert)
pixelRow = ~pixelRow;
for (int n = 0; n < paddingBits; n++) {
drawPixel((i * 8) + n + x, h - 1 - j + y, ((pixelRow & (0xFULL << (28 - n*4)))) >> (28 - n*4 + 1));
}
}
}
f->close();
return 1;
}
int Inkplate::drawGrayscaleBitmap8Sd(SdFile *f, struct bitmapHeader bmpHeader, int x, int y, bool invert) {
int w = bmpHeader.width;
int h = bmpHeader.height;
char padding = w % 4;
f->seekSet(bmpHeader.startRAW);
int i, j;
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
uint8_t px = 0;
if (invert)
px = 255-f->read();
else
px = f->read();
drawPixel(i + x, h - 1 - j + y, px>>5);
}
if (padding) {
for (int p = 0; p < 4-padding; p++) {
f->read();
}
}
}
f->close();
return 1;
}
int Inkplate::drawGrayscaleBitmap24Sd(SdFile *f, struct bitmapHeader bmpHeader, int x, int y, bool invert) {
int w = bmpHeader.width; int w = bmpHeader.width;
int h = bmpHeader.height; int h = bmpHeader.height;
char padding = w % 4; char padding = w % 4;
@ -870,10 +947,14 @@ int Inkplate::drawGrayscaleBitmap24(SdFile *f, struct bitmapHeader bmpHeader, in
//display.drawPixel(i + x, h - j + y, (uint8_t)(px*7)); //display.drawPixel(i + x, h - j + y, (uint8_t)(px*7));
//So then, we are convertng it to grayscale using good old average and gamma correction (from LUT). With this metod, it is still slow (full size image takes 4 seconds), but much beter than prev mentioned method. //So then, we are convertng it to grayscale using good old average and gamma correction (from LUT). With this metod, it is still slow (full size image takes 4 seconds), but much beter than prev mentioned method.
uint8_t px = (f->read() * 2126 / 10000) + (f->read() * 7152 / 10000) + (f->read() * 722 / 10000); uint8_t px = 0;
if (invert)
px = ((255-f->read()) * 2126 / 10000) + ((255-f->read()) * 7152 / 10000) + ((255-f->read()) * 722 / 10000);
else
px = (f->read() * 2126 / 10000) + (f->read() * 7152 / 10000) + (f->read() * 722 / 10000);
//drawPixel(i + x, h - j + y, gammaLUT[px]); //drawPixel(i + x, h - j + y, gammaLUT[px]);
drawPixel(i + x, h - j + y, px>>5); drawPixel(i + x, h - 1 - j + y, px>>5);
//drawPixel(i + x, h - j + y, px/32); //drawPixel(i + x, h - j + y, px/32);
} }
if (padding) { if (padding) {
for (int p = 0; p < padding; p++) { for (int p = 0; p < padding; p++) {
@ -885,9 +966,192 @@ int Inkplate::drawGrayscaleBitmap24(SdFile *f, struct bitmapHeader bmpHeader, in
return 1; return 1;
} }
int Inkplate::drawMonochromeBitmapWeb(WiFiClient *s, struct bitmapHeader bmpHeader, int x, int y, int len, bool invert) {
int w = bmpHeader.width;
int h = bmpHeader.height;
uint8_t paddingBits = w % 32;
int total = len - 34;
w /= 32;
uint8_t* buf = (uint8_t*) ps_malloc(total);
if (buf == NULL)
return 0;
int pnt = 0;
while (pnt < total) {
int toread = s->available();
if (toread > 0) {
int read = s->read(buf+pnt, toread);
if (read > 0)
pnt += read;
}
}
int i, j, k = bmpHeader.startRAW - 34;
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
uint32_t pixelRow = buf[k++] << 24 | buf[k++] << 16 | buf[k++] << 8 | buf[k++];
if (invert)
pixelRow = ~pixelRow;
for (int n = 0; n < 32; n++) {
drawPixel((i * 32) + n + x, h - 1 - j + y, !(pixelRow & (1ULL << (31 - n))));
}
}
if (paddingBits) {
uint32_t pixelRow = buf[k++] << 24 | buf[k++] << 16 | buf[k++] << 8 | buf[k++];
if (invert)
pixelRow = ~pixelRow;
for (int n = 0; n < paddingBits; n++) {
drawPixel((i * 32) + n + x, h - 1 - j + y, !(pixelRow & (1ULL << (31 - n))));
}
}
}
free(buf);
return 1;
}
int Inkplate::drawGrayscaleBitmap4Web(WiFiClient *s, struct bitmapHeader bmpHeader, int x, int y, int len, bool invert) {
int w = bmpHeader.width;
int h = bmpHeader.height;
char paddingBits = w % 8;
int total = len - 34;
w /= 8;
uint8_t* buf = (uint8_t*) ps_malloc(total);
if (buf == NULL)
return 0;
int pnt = 0;
while (pnt < total) {
int toread = s->available();
if (toread > 0) {
int read = s->read(buf+pnt, toread);
if (read > 0)
pnt += read;
}
}
int i, j, k = bmpHeader.startRAW - 34;
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
uint32_t pixelRow = buf[k++] << 24 | buf[k++] << 16 | buf[k++] << 8 | buf[k++];
if (invert)
pixelRow = ~pixelRow;
for (int n = 0; n < 8; n++) {
drawPixel((i * 8) + n + x, h - 1 - j + y, (pixelRow & (0xFULL << (28 - n*4))) >> (28 - n*4 + 1));
}
}
if (paddingBits) {
uint32_t pixelRow = buf[k++] << 24 | buf[k++] << 16 | buf[k++] << 8 | buf[k++];
if (invert)
pixelRow = ~pixelRow;
for (int n = 0; n < paddingBits; n++) {
drawPixel((i * 8) + n + x, h - 1 - j + y, ((pixelRow & (0xFULL << (28 - n*4)))) >> (28 - n*4 + 1));
}
}
}
free(buf);
return 1;
}
int Inkplate::drawGrayscaleBitmap8Web(WiFiClient *s, struct bitmapHeader bmpHeader, int x, int y, int len, bool invert) {
int w = bmpHeader.width;
int h = bmpHeader.height;
char padding = w % 4;
int total = len - 34;
uint8_t* buf = (uint8_t*) ps_malloc(total);
if (buf == NULL)
return 0;
int pnt = 0;
while (pnt < total) {
int toread = s->available();
if (toread > 0) {
int read = s->read(buf+pnt, toread);
if (read > 0)
pnt += read;
}
}
int i, j, k = bmpHeader.startRAW - 34;
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
uint8_t px = 0;
if (invert)
px = 255-buf[k++];
else
px = buf[k++];
drawPixel(i + x, h - 1 - j + y, px>>5);
}
if (padding) {
for (int p = 0; p < 4-padding; p++) {
k++;
}
}
}
free(buf);
return 1;
}
int Inkplate::drawGrayscaleBitmap24Web(WiFiClient *s, struct bitmapHeader bmpHeader, int x, int y, int len, bool invert) {
int w = bmpHeader.width;
int h = bmpHeader.height;
char padding = w % 4;
int total = len - 34;
uint8_t* buf = (uint8_t*) ps_malloc(total);
if (buf == NULL)
return 0;
int pnt = 0;
while (pnt < total) {
int toread = s->available();
if (toread > 0) {
int read = s->read(buf+pnt, toread);
if (read > 0)
pnt += read;
}
}
int i, j, k = bmpHeader.startRAW - 34;
for (j = 0; j < h; j++) {
for (i = 0; i < w; i++) {
//This is the proper way of converting True Color (24 Bit RGB) bitmap file into grayscale, but it takes waaay too much time (full size picture takes about 17s to decode!)
//float px = (0.2126 * (readByteFromSD(&file) / 255.0)) + (0.7152 * (readByteFromSD(&file) / 255.0)) + (0.0722 * (readByteFromSD(&file) / 255.0));
//px = pow(px, 1.5);
//display.drawPixel(i + x, h - j + y, (uint8_t)(px*7));
//So then, we are convertng it to grayscale using good old average and gamma correction (from LUT). With this metod, it is still slow (full size image takes 4 seconds), but much beter than prev mentioned method.
uint8_t px = 0;
if (invert)
px = ((255-buf[k++]) * 2126 / 10000) + ((255-buf[k++]) * 7152 / 10000) + ((255-buf[k++]) * 722 / 10000);
else
px = (buf[k++] * 2126 / 10000) + (buf[k++] * 7152 / 10000) + (buf[k++] * 722 / 10000);
//drawPixel(i + x, h - j + y, gammaLUT[px]);
drawPixel(i + x, h - 1 - j + y, px>>5);
//drawPixel(i + x, h - j + y, px/32);
}
if (padding) {
for (int p = 0; p < padding; p++) {
k++;
}
}
}
free(buf);
return 1;
}
void Inkplate::precalculateGamma(uint8_t* c, float gamma) { void Inkplate::precalculateGamma(uint8_t* c, float gamma) {
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
c[i] = int(round((pow(i / 255.0, gamma)) * 15)); c[i] = int(round((pow(i / 255.0, gamma)) * 15));
} }
} }