diff --git a/Inkplate.cpp b/Inkplate.cpp index b5840c6..9139c4b 100644 --- a/Inkplate.cpp +++ b/Inkplate.cpp @@ -1,8 +1,9 @@ -#include "Inkplate.h" - #include #include "Adafruit_GFX.h" +#include "WiFi.h" +#include "HTTPClient.h" +#include "Inkplate.h" Adafruit_MCP23017 mcp; SPIClass spi2(HSPI); SdFat sd(&spi2); @@ -87,12 +88,12 @@ void Inkplate::begin(void) { //Draw function, used by Adafruit GFX. 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) { case 1: _swap_int16_t(x0, y0); - x0 = _height - x0 - 1; + x0 = _width - x0 - 1; break; case 2: 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) { _rotation = r % 4; - switch (_rotation) { + switch (rotation) { case 0: _width = E_INK_WIDTH; _height = E_INK_HEIGHT; @@ -329,13 +330,13 @@ uint8_t Inkplate::getDisplayMode() { 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; struct bitmapHeader bmpHeader; - readBmpHeader(p, &bmpHeader); - if (bmpHeader.signature != 0x4D42 || bmpHeader.compression != 0 || !(bmpHeader.color == 1 || bmpHeader.color == 24)) return 0; + readBmpHeaderSd(p, &bmpHeader); + 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); } @@ -343,68 +344,71 @@ int Inkplate::drawBitmapFromSD(SdFile* p, int x, int y) { selectDisplayMode(INKPLATE_1BIT); } - if (bmpHeader.color == 1) drawMonochromeBitmap(p, bmpHeader, x, y); - if (bmpHeader.color == 24) drawGrayscaleBitmap24(p, bmpHeader, x, y); + if (bmpHeader.color == 1) drawMonochromeBitmapSd(p, bmpHeader, x, y, invert); + 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; } -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; SdFile dat; if (dat.open(fileName, O_RDONLY)) { - return drawBitmapFromSD(&dat, x, y); + return drawBitmapFromSD(&dat, x, y, invert); } else { return 0; } } -void Inkplate::drawThickLine(int x1, int y1, int x2, int y2, int color, float thickness) -{ - float deg = atan2f((float)(y2 - y1), (float)(x2 - x1)); +int Inkplate::drawBitmapFromWeb(WiFiClient* s, int x, int y, int len, bool invert) { + struct bitmapHeader bmpHeader; + 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); - float k1 = (float)y1 - l1 * (float)x1; + if ((bmpHeader.color == 4 || bmpHeader.color == 8 || bmpHeader.color == 24 || bmpHeader.color == 32) && getDisplayMode() != INKPLATE_3BIT) { + 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)); - 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); + return 1; } -void Inkplate::drawGradientLine(int x1, int y1, int x2, int y2, int color1, int color2, float thickness) { - int n = color2 - color1; +int Inkplate::drawBitmapFromWeb(char* url, int x, int y, bool invert) { + if (WiFi.status() != WL_CONNECTED) return 0; + int ret = 0; - float px = (float)(x2 - x1) / (float)n; - float py = (float)(y2 - y1) / (float)n; + bool sleep = WiFi.getSleep(); + WiFi.setSleep(false); - for (int i = 0; i < n; ++i) - { - if (abs(thickness + 1) < 0.1) - drawLine((int)((float)x1 + (float)i * px), (int)((float)y1 + (float)i * py), - (int)((float)x1 + (float)(i + 1) * px), (int)((float)y1 + (float)(i + 1) * py), - color1 + i); - else - drawThickLine((int)((float)x1 + (float)i * px), (int)((float)y1 + (float)i * py), - (int)((float)x1 + (float)(i + 1) * px), (int)((float)y1 + (float)(i + 1) * py), - color1 + i, - thickness); + HTTPClient http; + http.getStream().setNoDelay(true); + http.getStream().setTimeout(1); + http.begin(url); + + int httpCode = http.GET(); + if (httpCode == 200) { + int32_t len = http.getSize(); + if (len > 0) { + WiFiClient * dat = http.getStreamPtr(); + ret = drawBitmapFromWeb(dat, x, y, len, invert); } + } + + http.end(); + WiFi.setSleep(sleep); + return ret; } - int Inkplate::sdCardInit() { spi2.begin(14, 12, 13, 15); sdCardOk = sd.begin(15, SD_SCK_MHZ(25)); @@ -815,7 +819,7 @@ uint16_t Inkplate::read16(uint8_t* c) { 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]; _f->rewind(); _f->read(header, 100); @@ -830,7 +834,21 @@ void Inkplate::readBmpHeader(SdFile *_f, struct bitmapHeader *_h) { 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 h = bmpHeader.height; 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 (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 < 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) { 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 * 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; } -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 h = bmpHeader.height; 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)); //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, px>>5); - //drawPixel(i + x, h - j + y, px/32); + 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++) { @@ -885,9 +966,192 @@ int Inkplate::drawGrayscaleBitmap24(SdFile *f, struct bitmapHeader bmpHeader, in 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) { for (int i = 0; i < 256; i++) { c[i] = int(round((pow(i / 255.0, gamma)) * 15)); } } -