From 6fd572b52f3241ddeae8b7cc18f8a0d602f77c3f Mon Sep 17 00:00:00 2001 From: nitko12 Date: Mon, 7 Sep 2020 15:34:51 +0200 Subject: [PATCH] Few new fixes. --- src/Inkplate.h | 3 - src/include/Image.cpp | 168 +++------------ src/include/Image.h | 10 +- src/include/ImageBMP.cpp | 398 +++++++++++++----------------------- src/include/ImageJPEG.cpp | 72 +++++++ src/include/Network.cpp | 75 +++++++ src/include/Network.h | 8 +- src/include/Shapes.cpp | 152 +++++++++++++- src/include/Shapes.h | 10 + src/include/Triangulate.cpp | 143 +++++++++++++ src/include/Triangulate.h | 30 +++ test/test.ino | 4 +- 12 files changed, 666 insertions(+), 407 deletions(-) create mode 100644 src/include/Triangulate.cpp create mode 100644 src/include/Triangulate.h diff --git a/src/Inkplate.h b/src/Inkplate.h index d93ba44..f5b2359 100644 --- a/src/Inkplate.h +++ b/src/Inkplate.h @@ -12,9 +12,6 @@ #include "include/defines.h" -static void ckvClock(); -static void usleep1(); - class Inkplate : public System, public GFX { public: diff --git a/src/include/Image.cpp b/src/include/Image.cpp index 03f6087..26eaa21 100644 --- a/src/include/Image.cpp +++ b/src/include/Image.cpp @@ -9,7 +9,7 @@ #define GREEN(a) ((((a)&0x07e0) >> 5) << 2) #define BLUE(a) (((a)&0x001f) << 3) -static Image *_imagePtrJpeg = nullptr; +Image *_imagePtrJpeg = nullptr; Image::Image() { @@ -193,147 +193,7 @@ uint8_t Image::ditherSwap(int w) ditherBuffer[0][i] = ditherBuffer[1][i]; } -bool Image::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; -} - -bool Image::drawJpegFromSD(SdFile *p, int x, int y, bool dither, bool invert) -{ - uint8_t ret = 0; - - TJpgDec.setJpgScale(1); - TJpgDec.setCallback(drawJpegChunk); - - uint32_t pnt = 0; - uint32_t total = p->fileSize(); - uint8_t *buf = (uint8_t *)ps_malloc(total); - if (buf == NULL) - return 0; - - while (pnt < total) - { - uint32_t toread = p->available(); - if (toread > 0) - { - int read = p->read(buf + pnt, toread); - if (read > 0) - pnt += read; - } - } - p->close(); - - selectDisplayMode(INKPLATE_3BIT); - - if (TJpgDec.drawJpg(x, y, buf, total, dither, invert) == 0) - ret = 1; - - free(buf); - - return ret; -} - -bool Image::drawJpegChunk(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap, bool _dither, bool _invert) -{ - if (!_imagePtrJpeg) - return 0; - - int i, j; - for (j = 0; j < h; ++j) - { - for (i = 0; i < w; ++i) - { - uint16_t rgb = bitmap[j * w + i]; - if (_invert) - rgb = ~rgb; - uint8_t px = (RED(rgb) * 2126 / 10000) + (GREEN(rgb) * 7152 / 10000) + (BLUE(rgb) * 722 / 10000); - _imagePtrJpeg->drawPixel(i + x, j + y, px >> 5); - } - } - - return 1; -} - -bool Image::drawJpegFromSD(const char *fileName, int x, int y, bool dither, bool invert) -{ - SdFile dat; - if (dat.open(fileName, O_RDONLY)) - return drawJpegFromSD(&dat, x, y, dither, invert); - return 0; -} - -void Image::drawBitmap3Bit(int16_t _x, int16_t _y, const unsigned char *_p, int16_t _w, int16_t _h) -{ - if (getDisplayMode() != INKPLATE_3BIT) - return; - uint8_t _rem = _w % 2; - int i, j; - int xSize = _w / 2 + _rem; - - for (i = 0; i < _h; i++) - { - for (j = 0; j < xSize - 1; j++) - { - drawPixel((j * 2) + _x, i + _y, (*(_p + xSize * (i) + j) >> 4) >> 1); - drawPixel((j * 2) + 1 + _x, i + _y, (*(_p + xSize * (i) + j) & 0xff) >> 1); - } - drawPixel((j * 2) + _x, i + _y, (*(_p + xSize * (i) + j) >> 4) >> 1); - if (_rem == 0) - drawPixel((j * 2) + 1 + _x, i + _y, (*(_p + xSize * (i) + j) & 0xff) >> 1); - } -} - -// FUTURE COMPATIBILITY FUNCTIONS; DO NOT USE! -void Image::drawBitmap(int16_t x, int16_t y, 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) / 8; // Bitmap scanline pad = whole byte uint8_t byte = 0; @@ -357,6 +217,30 @@ void Image::drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t endWrite(); } +void Image::drawBitmap3Bit(int16_t _x, int16_t _y, const unsigned char *_p, int16_t _w, int16_t _h) +{ + if (getDisplayMode() != INKPLATE_3BIT) + return; + uint8_t _rem = _w & 1; + int i, j; + int xSize = (_w >> 1) + _rem; + + startWrite(); + for (i = 0; i < _h; i++) + { + for (j = 0; j < xSize - 1; j++) + { + writePixel((j * 2) + _x, i + _y, (*(_p + xSize * (i) + j) >> 4) >> 1); + writePixel((j * 2) + 1 + _x, i + _y, (*(_p + xSize * (i) + j) & 0xff) >> 1); + } + writePixel((j * 2) + _x, i + _y, (*(_p + xSize * (i) + j) >> 4) >> 1); + if (_rem == 0) + writePixel((j * 2) + 1 + _x, i + _y, (*(_p + xSize * (i) + j) & 0xff) >> 1); + } + endWrite(); +} + +// FUTURE COMPATIBILITY FUNCTIONS; DO NOT USE! void Image::drawGrayscaleBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h) { startWrite(); diff --git a/src/include/Image.h b/src/include/Image.h index 5d1d9f2..180406d 100644 --- a/src/include/Image.h +++ b/src/include/Image.h @@ -3,6 +3,7 @@ #include "../libs/SdFat/SdFat.h" #include "Arduino.h" +#include "Network.h" #include "WiFiClient.h" #include "defines.h" @@ -18,7 +19,7 @@ struct bitmapHeader uint32_t compression; }; -class Image +class Image : Network { public: Image(); @@ -35,7 +36,8 @@ class Image bool drawImage(const SdFile *path, int x, int y, bool dither = 1, bool invert = 0); bool drawImage(const WiFiClient *s, int x, int y, int len = -1, bool dither = 1, bool invert = 0); - void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg = 0xFFFF); + void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, + 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); @@ -75,8 +77,10 @@ class Image uint32_t read32(uint8_t *c); uint16_t read16(uint8_t *c); + void readBmpHeader(uint8_t *buf, bitmapHeader *_h); void readBmpHeaderSd(SdFile *_f, bitmapHeader *_h); - inline void displayBmpLine(int16_t x, int16_t y, SdFile *f, bitmapHeader *bmpHeader, bool dither, bool invert); + + inline void displayBmpLine(int16_t x, int16_t y, bitmapHeader *bmpHeader, bool dither, bool invert); bool drawMonochromeBitmapWeb(WiFiClient *s, bitmapHeader bmpHeader, int x, int y, int len, bool invert); bool drawGrayscaleBitmap4Web(WiFiClient *s, bitmapHeader bmpHeader, int x, int y, int len, bool dither, diff --git a/src/include/ImageBMP.cpp b/src/include/ImageBMP.cpp index 4e70a9a..6aec546 100644 --- a/src/include/ImageBMP.cpp +++ b/src/include/ImageBMP.cpp @@ -1,8 +1,11 @@ #include "Image.h" +#define RGB3BIT(r, g, b) ((54UL * (r) + 183UL * (g) + 19UL * (b)) >> 13) + void Image::readBmpHeaderSd(SdFile *_f, bitmapHeader *_h) { uint8_t header[100]; + _f->rewind(); _f->read(header, 100); _h->signature = read16(header + 0); @@ -32,32 +35,50 @@ void Image::readBmpHeaderSd(SdFile *_f, bitmapHeader *_h) { uint32_t c = read32(paletteRGB + (i << 2)); - Serial.println(c, 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); + } + } +} + +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); + + uint32_t totalColors = read32(buf + 46); + + uint8_t paletteRGB[1024]; + + if (_h->color <= 8) + { + if (!totalColors) + totalColors = (1ULL << _h->color); + + memcpy(paletteRGB, buf + 53, totalColors * 4); + memset(pallete, 0, sizeof pallete); + + for (int i = 0; i < totalColors; ++i) + { + 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; - uint8_t a = (c & 0x000000FF); - Serial.println(b, 2); - Serial.println(g, 2); - Serial.println(r, 2); - Serial.println(a, 2); - - pallete[i >> 1] |= ((54UL * r + 183UL * g + 19UL * b) >> 13) << (i & 1 ? 0 : 4); + pallete[i >> 1] |= RGB3BIT(r, g, b) << (i & 1 ? 0 : 4); } } - - for (int i = 0; i < totalColors / 2; ++i) - { - Serial.println(pallete[i] >> 8); - Serial.println(pallete[i] & 0xF); - } - - Serial.println(); - - return; -} +}; bool Image::drawBitmapFromSD(const char *fileName, int x, int y, bool dither, bool invert) { @@ -87,28 +108,27 @@ bool Image::drawBitmapFromSD(SdFile *p, int x, int y, bool dither, bool invert) selectDisplayMode(INKPLATE_3BIT); int16_t w = bmpHeader.width, h = bmpHeader.height; + int8_t c = bmpHeader.color; p->seekSet(bmpHeader.startRAW); - if (bmpHeader.color == 1) - Serial.println("nice"); - for (int i = 0; i < h; ++i) - displayBmpLine(x, y + i, p, &bmpHeader, dither, invert); + { + int16_t rowSize = (((int16_t)c * w + 31) >> 5) << 2; + p->read(pixelBuffer, rowSize); + displayBmpLine(x, y + i, &bmpHeader, dither, invert); + } return 1; } -void Image::displayBmpLine(int16_t x, int16_t y, SdFile *f, bitmapHeader *bmpHeader, bool dither, bool invert) +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; - int16_t rowSize = (((int16_t)c * w + 31) >> 5) << 2; - startWrite(); - f->read(pixelBuffer, rowSize); for (int j = 0; j < w; ++j) { switch (c) @@ -142,9 +162,9 @@ void Image::displayBmpLine(int16_t x, int16_t y, SdFile *f, bitmapHeader *bmpHea uint8_t b = (px & 0x1F) << 3; if (invert) - writePixel(x + j, y, 7 - ((54UL * r + 183UL * g + 19UL * b) >> 13)); + writePixel(x + j, y, 7 - RGB3BIT(r, g, b)); else - writePixel(x + j, y, (54UL * r + 183UL * b + 19UL * g) >> 13); + writePixel(x + j, y, RGB3BIT(r, g, b)); break; } case 24: { @@ -153,9 +173,9 @@ void Image::displayBmpLine(int16_t x, int16_t y, SdFile *f, bitmapHeader *bmpHea uint8_t b = pixelBuffer[j * 3 + 2]; if (invert) - writePixel(x + j, y, 7 - ((54UL * r + 183UL * g + 19UL * b) >> 13)); + writePixel(x + j, y, 7 - RGB3BIT(r, g, b)); else - writePixel(x + j, y, (54UL * r + 183UL * b + 19UL * g) >> 13); + writePixel(x + j, y, RGB3BIT(r, g, b)); break; } case 32: @@ -164,9 +184,9 @@ void Image::displayBmpLine(int16_t x, int16_t y, SdFile *f, bitmapHeader *bmpHea uint8_t b = pixelBuffer[j * 4 + 2]; if (invert) - writePixel(x + j, y, 7 - ((54UL * r + 183UL * g + 19UL * b) >> 13)); + writePixel(x + j, y, 7 - RGB3BIT(r, g, b)); else - writePixel(x + j, y, (54UL * r + 183UL * b + 19UL * g) >> 13); + writePixel(x + j, y, RGB3BIT(r, g, b)); break; } } @@ -174,240 +194,110 @@ void Image::displayBmpLine(int16_t x, int16_t y, SdFile *f, bitmapHeader *bmpHea endWrite(); } -// bool Image::drawMonochromeBitmapSd(SdFile *f, bitmapHeader bmpHeader, int x, int y, bool invert) -// { -// int w = bmpHeader.width; -// int h = bmpHeader.height; -// uint8_t paddingBits = w % 32; -// w /= 32; +bool Image::drawBitmapFromWeb(const char *url, int x, int y, bool dither, bool invert) +{ + uint8_t *buf = (uint8_t *)ps_malloc(800 * 600 * 4 + 100); // TODO: allocate as mush as used + downloadFile(buf, url); + struct bitmapHeader bmpHeader; -// int i, j; -// for (j = 0; j < h; j++) -// { -// uint8_t *bufferPtr = pixelBuffer; + readBmpHeader(buf, &bmpHeader); -// for (i = 0; i < w; i++) -// { -// uint32_t pixelRow = *(bufferPtr++) << 24 | *(bufferPtr++) << 16 | *(bufferPtr++) << 8 | *(bufferPtr++); -// 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 = 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 - 1 - j + y, !(pixelRow & (1ULL << (31 - n)))); -// } -// } -// } -// f->close(); -// return 1; -// } + uint8_t *bufferPtr = buf + bmpHeader.startRAW; + for (int i = 0; i < bmpHeader.height; ++i) + { + memcpy(pixelBuffer, bufferPtr, bmpHeader.width); + displayBmpLine(x, y + i, &bmpHeader, dither, invert); + bufferPtr += bmpHeader.width; + } -// bool Image::drawGrayscaleBitmap4Sd(SdFile *f, bitmapHeader bmpHeader, int x, int y, bool dither, bool invert) -// { -// int w = bmpHeader.width; -// int h = bmpHeader.height; -// uint8_t paddingBits = w % 8; -// w /= 8; + return 1; +} -// f->seekSet(bmpHeader.startRAW); -// int i, j; +bool Image::drawBitmapFromWeb(WiFiClient *s, int x, int y, int len, bool dither, bool invert) +{ + struct bitmapHeader bmpHeader; + // readBmpHeaderWeb(s, &bmpHeader); -// uint8_t *bufferPtr; + if (bmpHeader.signature != 0x4D42 || bmpHeader.compression != 0 || + !(bmpHeader.color == 1 || bmpHeader.color == 4 || bmpHeader.color == 8 || bmpHeader.color == 16 || + bmpHeader.color == 24)) + return 0; -// if (dither) -// { -// bufferPtr = pixelBuffer; -// f->read(pixelBuffer, w * 4 + (paddingBits ? 4 : 0)); + if ((bmpHeader.color == 4 || bmpHeader.color == 8 || bmpHeader.color == 16 || bmpHeader.color == 24 || + bmpHeader.color == 32) && + getDisplayMode() != INKPLATE_3BIT) + { + selectDisplayMode(INKPLATE_3BIT); + } -// ditherStart(pixelBuffer, bufferPtr, w * 8 + (paddingBits ? 4 : 0), invert, 4); -// } + if (bmpHeader.color == 1 && getDisplayMode() != INKPLATE_1BIT) + { + selectDisplayMode(INKPLATE_1BIT); + } -// for (j = 0; j < h; j++) -// { -// bufferPtr = pixelBuffer; -// f->read(pixelBuffer, w * 4 + (paddingBits ? 4 : 0)); + if (bmpHeader.color == 1) + drawMonochromeBitmapWeb(s, bmpHeader, x, y, len, invert); + if (bmpHeader.color == 4) + drawGrayscaleBitmap4Web(s, bmpHeader, x, y, len, dither, invert); + if (bmpHeader.color == 8) + drawGrayscaleBitmap8Web(s, bmpHeader, x, y, len, dither, invert); + if (bmpHeader.color == 24) + drawGrayscaleBitmap24Web(s, bmpHeader, x, y, len, dither, invert); -// if (dither && j != h - 1) -// { -// ditherLoadNextLine(pixelBuffer, bufferPtr, w * 8 + (paddingBits ? 4 : 0), invert, 4); -// } + return 1; +} -// for (i = 0; i < w; i++) -// { -// if (dither) -// { +bool Image::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; -// for (int n = 0; n < 8; n++) -// { -// drawPixel((i * 8) + n + x, h - 1 - j + y, -// ditherGetPixel((i * 8) + n, h - 1 - j, w * 8 + (paddingBits ? 4 : 0), h) >> 5); -// } -// } -// else -// { -// uint32_t pixelRow = *(bufferPtr++) << 24 | *(bufferPtr++) << 16 | *(bufferPtr++) << 8 | -// *(bufferPtr++); 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) -// { -// if (dither) -// { -// for (int n = 0; n < paddingBits; n++) -// { -// drawPixel((i * 8) + n + x, h - 1 - j + y, -// ditherGetPixel((i * 8) + n, h - 1 - j, w * 8 + (paddingBits ? 4 : 0), h) >> 5); -// } -// } -// else -// { -// uint32_t pixelRow = *(bufferPtr++) << 24 | *(bufferPtr++) << 16 | *(bufferPtr++) << 8 | -// *(bufferPtr++); 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)); -// } -// } -// } -// if (dither) -// ditherSwap(w * 8 + paddingBits); -// } -// f->close(); -// return 1; -// } + uint8_t *buf = (uint8_t *)ps_malloc(total); + if (buf == NULL) + return 0; -// bool Image::drawGrayscaleBitmap8Sd(SdFile *f, bitmapHeader bmpHeader, int x, int y, bool dither, bool invert) -// { -// int w = bmpHeader.width; -// int h = bmpHeader.height; -// char padding = w & 3; -// f->seekSet(bmpHeader.startRAW); -// int i, j; + 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; + } + } -// uint8_t *bufferPtr; + 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)))); + } + } + } -// if (dither) -// { -// bufferPtr = pixelBuffer; -// f->read(pixelBuffer, w); + free(buf); -// ditherStart(pixelBuffer, bufferPtr, w, invert, 8); -// } - -// for (j = 0; j < h; j++) -// { -// bufferPtr = pixelBuffer; -// f->read(pixelBuffer, w); - -// if (dither && j != h - 1) -// { -// ditherLoadNextLine(pixelBuffer, bufferPtr, w, invert, 8); -// } - -// for (i = 0; i < w; i++) -// { -// if (dither) -// drawPixel(i + x, h - 1 - j + y, ditherGetPixel(i, j, w, h) >> 5); -// else -// { -// uint8_t px = 0; -// if (invert) -// px = 255 - *(bufferPtr++); -// else -// px = *(bufferPtr++); -// drawPixel(i + x, h - 1 - j + y, px >> 5); -// } -// } -// } - -// if (dither) -// ditherSwap(w); - -// if (padding) -// { -// for (int p = 0; p < 4 - padding; p++) -// { -// f->read(); -// } -// } -// f->close(); -// return 1; -// } - -// bool Image::drawGrayscaleBitmap24Sd(SdFile *f, bitmapHeader bmpHeader, int x, int y, bool dither, bool invert) -// { -// int w = bmpHeader.width; -// int h = bmpHeader.height; -// char padding = w & 3; -// f->seekSet(bmpHeader.startRAW); -// int i, j; - -// uint8_t *bufferPtr; - -// if (dither) -// { -// bufferPtr = pixelBuffer; -// f->read(pixelBuffer, w * 3); - -// ditherStart(pixelBuffer, bufferPtr, w, invert, 24); -// } - -// for (j = 0; j < h; j++) -// { -// bufferPtr = pixelBuffer; -// f->read(pixelBuffer, w * 3); - -// if (dither && j != h - 1) -// { -// ditherLoadNextLine(pixelBuffer, bufferPtr, w, invert, 24); -// } - -// for (i = 0; i < w; i++) -// { -// if (dither) -// drawPixel(i + x, h - 1 - j + y, ditherGetPixel(i, j, w, h) >> 5); -// else -// { -// uint8_t px = 0; -// if (invert) -// px = ((255 - *(bufferPtr++)) * 2126 / 10000) + ((255 - *(bufferPtr++)) * 7152 / 10000) + -// ((255 - *(bufferPtr++)) * 722 / 10000); -// else -// px = (*(bufferPtr++) * 2126 / 10000) + (*(bufferPtr++) * 7152 / 10000) + -// (*(bufferPtr++) * 722 / 10000); -// drawPixel(i + x, h - 1 - j + y, px >> 5); -// } -// } - -// if (dither) -// ditherSwap(w); - -// if (padding) -// { -// for (int p = 0; p < padding; p++) -// { -// f->read(); -// } -// } -// } -// f->close(); -// return 1; -// } \ No newline at end of file + return 1; +} \ No newline at end of file diff --git a/src/include/ImageJPEG.cpp b/src/include/ImageJPEG.cpp index 8cf2f9f..331964a 100644 --- a/src/include/ImageJPEG.cpp +++ b/src/include/ImageJPEG.cpp @@ -1,5 +1,77 @@ #include "Image.h" +#include "../libs/TJpeg/TJpg_Decoder.h" + #define RED(a) ((((a)&0xf800) >> 11) << 3) #define GREEN(a) ((((a)&0x07e0) >> 5) << 2) #define BLUE(a) (((a)&0x001f) << 3) + +#define RGB3BIT(r, g, b) ((54UL * (r) + 183UL * (g) + 19UL * (b)) >> 13) + +extern Image *_imagePtrJpeg; + +bool Image::drawJpegChunk(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t *bitmap, bool _dither, bool _invert) +{ + if (!_imagePtrJpeg) + return 0; + + _imagePtrJpeg->startWrite(); + for (int j = 0; j < h; ++j) + { + for (int i = 0; i < w; ++i) + { + uint16_t rgb = bitmap[j * w + i]; + if (_invert) + _imagePtrJpeg->drawPixel(i + x, j + y, 7 - RGB3BIT(RED(rgb), GREEN(rgb), BLUE(rgb))); + else + _imagePtrJpeg->drawPixel(i + x, j + y, RGB3BIT(RED(rgb), GREEN(rgb), BLUE(rgb))); + } + } + _imagePtrJpeg->endWrite(); + + return 1; +} + +bool Image::drawJpegFromSD(const char *fileName, int x, int y, bool dither, bool invert) +{ + SdFile dat; + if (dat.open(fileName, O_RDONLY)) + return drawJpegFromSD(&dat, x, y, dither, invert); + return 0; +} + +bool Image::drawJpegFromSD(SdFile *p, int x, int y, bool dither, bool invert) +{ + uint8_t ret = 0; + + TJpgDec.setJpgScale(1); + TJpgDec.setCallback(drawJpegChunk); + + uint32_t pnt = 0; + uint32_t total = p->fileSize(); + uint8_t *buf = (uint8_t *)ps_malloc(total); + + if (buf == NULL) + return 0; + + while (pnt < total) + { + uint32_t toread = p->available(); + if (toread > 0) + { + int read = p->read(buf + pnt, toread); + if (read > 0) + pnt += read; + } + } + p->close(); + + selectDisplayMode(INKPLATE_3BIT); + + if (TJpgDec.drawJpg(x, y, buf, total, dither, invert) == 0) + ret = 1; + + free(buf); + + return ret; +} \ No newline at end of file diff --git a/src/include/Network.cpp b/src/include/Network.cpp index d576a29..b929d34 100644 --- a/src/include/Network.cpp +++ b/src/include/Network.cpp @@ -1 +1,76 @@ #include "Network.h" + +bool Network::joinAP(char *ssid, char *pass) +{ + WiFi.mode(WIFI_MODE_STA); + WiFi.begin(ssid, pass); + + int cnt = 0; + while (!isConnected()) + { + if (cnt > 15) + return 0; + delay(1000); + ++cnt; + } + + return 1; +} + +void Network::disconnect() +{ + WiFi.mode(WIFI_OFF); +} + +bool Network::isConnected() +{ + return WiFi.status() == WL_CONNECTED; +} + +bool Network::downloadFile(uint8_t *buffer, const char *url) +{ + if (!isConnected()) + return 0; + + int ret = 0; + uint8_t *bufferPtr = buffer; + + bool sleep = WiFi.getSleep(); + WiFi.setSleep(false); + + HTTPClient http; + http.getStream().setNoDelay(true); + http.getStream().setTimeout(1); + http.begin(url); + + int httpCode = http.GET(); + if (httpCode == 200) + { + int total = http.getSize(); + int len = total; + + uint8_t buff[128] = {0}; + + WiFiClient *stream = http.getStreamPtr(); + + while (http.connected() && (len > 0 || len == -1)) + { + size_t size = stream->available(); + if (size) + { + int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); + + memcpy(bufferPtr, buff, c); + bufferPtr += c; + + if (len > 0) + len -= c; + } + yield(); + } + } + + http.end(); + WiFi.setSleep(sleep); + return ret; +} diff --git a/src/include/Network.h b/src/include/Network.h index 28338b8..e740b91 100644 --- a/src/include/Network.h +++ b/src/include/Network.h @@ -8,7 +8,13 @@ class Network { public: - private: + bool joinAP(char *ssid, char *pass); + void disconnect(); + bool isConnected(); + + bool downloadFile(uint8_t *buffer, const char *url); + + private: }; #endif \ No newline at end of file diff --git a/src/include/Shapes.cpp b/src/include/Shapes.cpp index a8e3757..6682b49 100644 --- a/src/include/Shapes.cpp +++ b/src/include/Shapes.cpp @@ -16,14 +16,14 @@ void Shapes::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { startWrite(); - writeFastHLine(x, y, h, color); + writeFastVLine(x, y, h, color); endWrite(); } void Shapes::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { startWrite(); - writeLine(x, y, x + w - 1, y, color); + writeFastHLine(x, y, w, color); endWrite(); } @@ -327,3 +327,151 @@ void Shapes::fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_ } endWrite(); } + + +void Shapes::drawElipse(int rx, int ry, int xc, int yc, int c) +{ + float dx, dy, d1, d2, x, y; + x = 0; + y = ry; + + d1 = (ry * ry) - (rx * rx * ry) + (0.25 * rx * rx); + dx = 2 * ry * ry * x; + dy = 2 * rx * rx * y; + + while (dx < dy) + { + drawPixel(x + xc, y + yc, c); + drawPixel(-x + xc, y + yc, c); + drawPixel(x + xc, -y + yc, c); + drawPixel(-x + xc, -y + yc, c); + + if (d1 < 0) + { + x++; + dx = dx + (2 * ry * ry); + d1 = d1 + dx + (ry * ry); + } + else + { + x++; + y--; + dx = dx + (2 * ry * ry); + dy = dy - (2 * rx * rx); + d1 = d1 + dx - dy + (ry * ry); + } + } + + d2 = ((ry * ry) * ((x + 0.5) * (x + 0.5))) + ((rx * rx) * ((y - 1) * (y - 1))) - (rx * rx * ry * ry); + while (y >= 0) + { + drawPixel(x + xc, y + yc, c); + drawPixel(-x + xc, y + yc, c); + drawPixel(x + xc, -y + yc, c); + drawPixel(-x + xc, -y + yc, c); + + if (d2 > 0) + { + y--; + dy = dy - (2 * rx * rx); + d2 = d2 + (rx * rx) - dy; + } + else + { + y--; + x++; + dx = dx + (2 * ry * ry); + dy = dy - (2 * rx * rx); + d2 = d2 + dx - dy + (rx * rx); + } + } +} + +void Shapes::fillElipse(int rx, int ry, int xc, int yc, int c) +{ + int hh = ry * ry; + int ww = rx * rx; + int hhww = hh * ww; + int x0 = rx; + int dx = 0; + + for (int x = -rx; x <= rx; x++) + drawPixel(xc + x, yc, c); + + for (int y = 1; y <= ry; y++) + { + int x1 = x0 - (dx - 1); + for (; x1 > 0; x1--) + if (x1 * x1 * hh + y * y * ww <= hhww) + break; + dx = x0 - x1; + x0 = x1; + + for (int x = -x0; x <= x0; x++) + { + drawPixel(xc + x, yc - y, c); + drawPixel(xc + x, yc + y, c); + } + } +} + +void Shapes::fillPolygon(int *x, int *y, int n, int color) +{ + int tx[100], ty[100]; + triangulate.triangulate(x, y, n, tx, ty); + + for (int i = 0; i < n - 2; ++i) + { + fillTriangle(tx[i * 3 + 0], ty[i * 3 + 0], tx[i * 3 + 1], ty[i * 3 + 1], tx[i * 3 + 2], ty[i * 3 + 2], color); + } +} + +void Shapes::drawPolygon(int *x, int *y, int n, int color) +{ + for (int i = 0; i < n; ++i) + drawLine(x[i], y[i], x[(i + 1) % n], y[(i + 1) % n], color); +} + +void Shapes::drawThickLine(int x1, int y1, int x2, int y2, int color, float thickness) +{ + float deg = atan2f((float)(y2 - y1), (float)(x2 - x1)); + + float l1 = tan(deg); + float k1 = (float)y1 - l1 * (float)x1; + + float degShift = (l1 < 0 ? M_PI_2 : -M_PI_2); + + 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); +} + +void Shapes::drawGradientLine(int x1, int y1, int x2, int y2, int color1, int color2, float thickness) +{ + int n = color2 - color1; + + float px = (float)(x2 - x1) / (float)n; + float py = (float)(y2 - y1) / (float)n; + + 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); + } +} diff --git a/src/include/Shapes.h b/src/include/Shapes.h index bbda152..21c3e86 100644 --- a/src/include/Shapes.h +++ b/src/include/Shapes.h @@ -2,6 +2,7 @@ #define __SHAPES_H__ #include "Arduino.h" +#include "Triangulate.h" class Shapes { @@ -29,7 +30,16 @@ class Shapes void drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, int16_t radius, uint16_t color); void fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, int16_t radius, uint16_t color); + void drawElipse(int rx, int ry, int xc, int yc, int c); + void fillElipse(int rx, int ry, int xc, int yc, int c); + void drawPolygon(int *x, int *y, int n, int color); + void fillPolygon(int *x, int *y, int n, int color); + void drawThickLine(int x1, int y1, int x2, int y2, int color, float thickness); + void drawGradientLine(int x1, int y1, int x2, int y2, int color1, int color2, float thickness = -1); + private: + Triangulate triangulate; + virtual void startWrite(void) = 0; virtual void writePixel(int16_t x, int16_t y, uint16_t color) = 0; virtual void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) = 0; diff --git a/src/include/Triangulate.cpp b/src/include/Triangulate.cpp new file mode 100644 index 0000000..46b3099 --- /dev/null +++ b/src/include/Triangulate.cpp @@ -0,0 +1,143 @@ +#include "Triangulate.h" +#include "Arduino.h" +#include "math.h" + +float Triangulate::area(int x1, int y1, int x2, int y2, int x3, int y3) +{ + return fabs((float)((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2))) / 2.0); +} + +bool Triangulate::isInside(int x1, int y1, int x2, int y2, int x3, int y3, int x, int y) +{ + float A = area(x1, y1, x2, y2, x3, y3); + float A1 = area(x, y, x2, y2, x3, y3); + float A2 = area(x1, y1, x, y, x3, y3); + float A3 = area(x1, y1, x2, y2, x, y); + return fabs(-A + A1 + A2 + A3) < 1e-3; +} + +void Triangulate::preProcess(int *x, int *y, int n) +{ + for (int i = 0; i < n; ++i) + { + int prev = (i - 1 + n) % n; + int next = (i + 1 + n) % n; + float deg = atan2(y[prev] - y[i], x[prev] - x[i]) - atan2(y[next] - y[i], x[next] - x[i]); + if (deg < 0.0) + deg += 2 * M_PI; + innerAngle[i] = deg; + } +} + +void Triangulate::updateVertex(int p, int *x, int *y, int n) +{ + int prev = (p - 1 + n) % n; + int next = (p + 1 + n) % n; + float deg = atan2(y[prev] - y[p], x[prev] - x[p]) - atan2(y[next] - y[p], x[next] - x[p]); + if (deg < 0.0) + deg += 2 * M_PI; + innerAngle[p] = deg; + bool f = 0; + for (int j = 0; j < n; ++j) + { + if (prev != j && p != j && next != j && innerAngle[p] > M_PI && + isInside(x[prev], y[prev], x[p], y[p], x[next], y[next], x[j], y[j])) + f = 1; + } + earTip[p] = !f; +} + +bool Triangulate::isConvex(int *x, int *y, int n) +{ + for (int i = 0; i < n; ++i) + if (innerAngle[i] > M_PI) + return 0; + return 1; +} + +void Triangulate::trivialTriangles(int *x, int *y, int n) +{ + for (int i = 0; i < n - 2; ++i) + { + tx[tc] = x[0]; + ty[tc] = y[0]; + ++tc; + tx[tc] = x[i + 1]; + ty[tc] = y[i + 1]; + ++tc; + tx[tc] = x[i + 2]; + ty[tc] = y[i + 2]; + ++tc; + } +} + +void Triangulate::findEars(int *x, int *y, int n) +{ + for (int i = 0; i < n; ++i) + { + if (innerAngle[i] > M_PI) + continue; + int prev = (i - 1 + n) % n; + int next = (i + 1 + n) % n; + bool f = 0; + for (int j = 0; j < n; ++j) + { + if (prev != j && i != j && next != j && innerAngle[i] > M_PI && + isInside(x[prev], y[prev], x[i], y[i], x[next], y[next], x[j], y[j])) + f = 1; + } + earTip[i] = !f; + } +} + +int Triangulate::smallestEar(int *x, int *y, int n) +{ + int mn = 0; + for (int i = 1; i < n; ++i) + if (earTip[i] && innerAngle[i] < innerAngle[mn]) + mn = i; + return mn; +} + +void Triangulate::nonTrivialTriangles(int *x, int *y, int n) +{ + findEars(x, y, n); + int initialN = n; + while (tc / 3 < initialN - 2) + { + int pos = smallestEar(x, y, n); + int prev = (pos - 1 + n) % n; + int next = (pos + 1 + n) % n; + tx[tc] = x[prev]; + ty[tc] = y[prev]; + ++tc; + tx[tc] = x[pos]; + ty[tc] = y[pos]; + ++tc; + tx[tc] = x[next]; + ty[tc] = y[next]; + ++tc; + for (int i = pos; i < n - 1; i++) + { + x[i] = x[i + 1]; + y[i] = y[i + 1]; + innerAngle[i] = innerAngle[i + 1]; + earTip[i] = earTip[i + 1]; + } + --n; + updateVertex(prev, x, y, n); + updateVertex(prev + 1, x, y, n); + } +} + +void Triangulate::triangulate(int *x, int *y, int n, int *_tx, int *_ty) +{ + tc = 0; + preProcess(x, y, n); + if (isConvex(x, y, n)) + trivialTriangles(x, y, n); + else + nonTrivialTriangles(x, y, n); + memcpy(_tx, tx, 100); + memcpy(_ty, ty, 100); +} \ No newline at end of file diff --git a/src/include/Triangulate.h b/src/include/Triangulate.h new file mode 100644 index 0000000..9c927bd --- /dev/null +++ b/src/include/Triangulate.h @@ -0,0 +1,30 @@ +#ifndef TRIANGULATE_H +#define TRIANGULATE_H + +#include "Arduino.h" + +class Triangulate +{ + private: + int tx[100]; + int ty[100]; + int tc = 0; + + float innerAngle[100]; + bool earTip[100]; + + float area(int x1, int y1, int x2, int y2, int x3, int y3); + bool isInside(int x1, int y1, int x2, int y2, int x3, int y3, int x, int y); + void preProcess(int *x, int *y, int n); + void updateVertex(int p, int *x, int *y, int n); + bool isConvex(int *x, int *y, int n); + void trivialTriangles(int *x, int *y, int n); + void findEars(int *x, int *y, int n); + int smallestEar(int *x, int *y, int n); + void nonTrivialTriangles(int *x, int *y, int n); + + public: + void triangulate(int *x, int *y, int n, int *_tx, int *_ty); +}; + +#endif \ No newline at end of file diff --git a/test/test.ino b/test/test.ino index 2ae3ecb..eae9d57 100644 --- a/test/test.ino +++ b/test/test.ino @@ -24,7 +24,7 @@ #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) + INKPLATE_3BIT); // 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() @@ -49,7 +49,7 @@ void loop() // 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("4bitish.bmp", 200, 0, 0)) + if (!display.drawBitmapFromSD("4bitTest.bmp", 0, 0, 0)) { // 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