diff --git a/Inkplate.cpp b/Inkplate.cpp index 3809939..01eb7a2 100644 --- a/Inkplate.cpp +++ b/Inkplate.cpp @@ -298,12 +298,12 @@ void Inkplate::drawBitmap3Bit(int16_t _x, int16_t _y, const unsigned char *_p, 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); + 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); + 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); + drawPixel((j * 2) + 1 + _x, i + _y, (*(_p + xSize * (i) + j) & 0xff) >> 1); } } @@ -515,6 +515,120 @@ int Inkplate::drawBitmapFromWeb(char *url, int x, int y, bool dither, bool inver return ret; } +void Inkplate::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 Inkplate::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 Inkplate::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 Inkplate::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 Inkplate::drawThickLine(int x1, int y1, int x2, int y2, int color, float thickness) { float deg = atan2f((float)(y2 - y1), (float)(x2 - x1)); @@ -551,13 +665,13 @@ void Inkplate::drawGradientLine(int x1, int y1, int x2, int y2, int color1, int { 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); + (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); + (int)((float)x1 + (float)(i + 1) * px), (int)((float)y1 + (float)(i + 1) * py), + color1 + i, + thickness); } } @@ -1088,32 +1202,36 @@ int Inkplate::drawGrayscaleBitmap4Sd(SdFile *f, struct bitmapHeader bmpHeader, i uint8_t *bufferPtr; - if (dither) { + if (dither) + { bufferPtr = pixelBuffer; - f->read(pixelBuffer, w * 4 + (paddingBits? 4 : 0)); + f->read(pixelBuffer, w * 4 + (paddingBits ? 4 : 0)); - ditherStart(pixelBuffer, bufferPtr, w * 8 + (paddingBits? 4 : 0), invert, 4); + ditherStart(pixelBuffer, bufferPtr, w * 8 + (paddingBits ? 4 : 0), invert, 4); } for (j = 0; j < h; j++) { bufferPtr = pixelBuffer; - f->read(pixelBuffer, w * 4 + (paddingBits? 4 : 0)); + f->read(pixelBuffer, w * 4 + (paddingBits ? 4 : 0)); - if (dither && j != h - 1) { - ditherLoadNextLine(pixelBuffer, bufferPtr, w * 8 + (paddingBits? 4 : 0), invert, 4); + if (dither && j != h - 1) + { + ditherLoadNextLine(pixelBuffer, bufferPtr, w * 8 + (paddingBits ? 4 : 0), invert, 4); } for (i = 0; i < w; i++) { - if (dither) { + if (dither) + { 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); + drawPixel((i * 8) + n + x, h - 1 - j + y, ditherGetPixel((i * 8) + n, h - 1 - j, w * 8 + (paddingBits ? 4 : 0), h) >> 5); } } - else { + else + { uint32_t pixelRow = *(bufferPtr++) << 24 | *(bufferPtr++) << 16 | *(bufferPtr++) << 8 | *(bufferPtr++); if (invert) pixelRow = ~pixelRow; @@ -1125,13 +1243,15 @@ int Inkplate::drawGrayscaleBitmap4Sd(SdFile *f, struct bitmapHeader bmpHeader, i } if (paddingBits) { - if (dither) { + 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); + drawPixel((i * 8) + n + x, h - 1 - j + y, ditherGetPixel((i * 8) + n, h - 1 - j, w * 8 + (paddingBits ? 4 : 0), h) >> 5); } } - else { + else + { uint32_t pixelRow = *(bufferPtr++) << 24 | *(bufferPtr++) << 16 | *(bufferPtr++) << 8 | *(bufferPtr++); if (invert) pixelRow = ~pixelRow; @@ -1158,7 +1278,8 @@ int Inkplate::drawGrayscaleBitmap8Sd(SdFile *f, struct bitmapHeader bmpHeader, i uint8_t *bufferPtr; - if (dither) { + if (dither) + { bufferPtr = pixelBuffer; f->read(pixelBuffer, w); @@ -1170,7 +1291,8 @@ int Inkplate::drawGrayscaleBitmap8Sd(SdFile *f, struct bitmapHeader bmpHeader, i bufferPtr = pixelBuffer; f->read(pixelBuffer, w); - if (dither && j != h - 1) { + if (dither && j != h - 1) + { ditherLoadNextLine(pixelBuffer, bufferPtr, w, invert, 8); } @@ -1178,7 +1300,8 @@ int Inkplate::drawGrayscaleBitmap8Sd(SdFile *f, struct bitmapHeader bmpHeader, i { if (dither) drawPixel(i + x, h - 1 - j + y, ditherGetPixel(i, j, w, h) >> 5); - else { + else + { uint8_t px = 0; if (invert) px = 255 - *(bufferPtr++); @@ -1213,7 +1336,8 @@ int Inkplate::drawGrayscaleBitmap24Sd(SdFile *f, struct bitmapHeader bmpHeader, uint8_t *bufferPtr; - if (dither) { + if (dither) + { bufferPtr = pixelBuffer; f->read(pixelBuffer, w * 3); @@ -1225,7 +1349,8 @@ int Inkplate::drawGrayscaleBitmap24Sd(SdFile *f, struct bitmapHeader bmpHeader, bufferPtr = pixelBuffer; f->read(pixelBuffer, w * 3); - if (dither && j != h - 1) { + if (dither && j != h - 1) + { ditherLoadNextLine(pixelBuffer, bufferPtr, w, invert, 24); } @@ -1238,7 +1363,8 @@ int Inkplate::drawGrayscaleBitmap24Sd(SdFile *f, struct bitmapHeader bmpHeader, if (dither) drawPixel(i + x, h - 1 - j + y, ditherGetPixel(i, j, w, h) >> 5); - else { + else + { uint8_t px = 0; //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. if (invert) @@ -1268,31 +1394,38 @@ int Inkplate::drawGrayscaleBitmap24Sd(SdFile *f, struct bitmapHeader bmpHeader, } //Loads first line in current dither buffer -void Inkplate::ditherStart(uint8_t *pixelBuffer, uint8_t* bufferPtr, int w, bool invert, uint8_t bits) { +void Inkplate::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 (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) { + else if (bits == 8) + { if (invert) ditherBuffer[0][i] = 255 - *(bufferPtr++); else ditherBuffer[0][i] = *(bufferPtr++); } - if (bits == 4) { + 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++) { + 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) { + 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]; } @@ -1312,33 +1445,40 @@ void Inkplate::ditherStart(uint8_t *pixelBuffer, uint8_t* bufferPtr, int w, bool } //Loads next line, after this ditherGetPixel can be called and alters values in next line -void Inkplate::ditherLoadNextLine(uint8_t *pixelBuffer, uint8_t* bufferPtr, int w, bool invert, uint8_t bits) { +void Inkplate::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 (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) { + else if (bits == 8) + { if (invert) ditherBuffer[1][i] = 255 - *(bufferPtr++); else ditherBuffer[1][i] = *(bufferPtr++); } } - if (bits == 4) { + 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++) { + 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) { + 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]; } @@ -1358,7 +1498,8 @@ void Inkplate::ditherLoadNextLine(uint8_t *pixelBuffer, uint8_t* bufferPtr, int } //Gets specific pixel, mainly at i, j is just used for bound checking when changing next line values -uint8_t Inkplate::ditherGetPixel(int i, int j, int w, int h) { +uint8_t Inkplate::ditherGetPixel(int i, int j, int w, int h) +{ uint8_t oldpixel = ditherBuffer[0][i]; uint8_t newpixel = (oldpixel & B11100000); @@ -1379,7 +1520,8 @@ uint8_t Inkplate::ditherGetPixel(int i, int j, int w, int h) { } //Swaps current and next line, for next one to be overwritten -uint8_t Inkplate::ditherSwap(int w) { +uint8_t Inkplate::ditherSwap(int w) +{ for (int i = 0; i < w; ++i) ditherBuffer[0][i] = ditherBuffer[1][i]; } @@ -1467,9 +1609,10 @@ int Inkplate::drawGrayscaleBitmap4Web(WiFiClient *s, struct bitmapHeader bmpHead uint8_t *bufferPtr; uint8_t *f_pointer = buf + (bmpHeader.startRAW - 34); - if (dither) { + if (dither) + { bufferPtr = pixelBuffer; - for (i = 0; i < w * 4 + (paddingBits? 1 : 0); i++) + for (i = 0; i < w * 4 + (paddingBits ? 1 : 0); i++) pixelBuffer[i] = *(f_pointer++); ditherStart(pixelBuffer, bufferPtr, w, invert, 4); @@ -1478,23 +1621,26 @@ int Inkplate::drawGrayscaleBitmap4Web(WiFiClient *s, struct bitmapHeader bmpHead for (j = 0; j < h; j++) { bufferPtr = pixelBuffer; - for (i = 0; i < w * 4 + (paddingBits? 1 : 0); i++) + for (i = 0; i < w * 4 + (paddingBits ? 1 : 0); i++) pixelBuffer[i] = *(f_pointer++); - if (dither && j != h - 1) { - ditherLoadNextLine(pixelBuffer, bufferPtr, w * 8 + (paddingBits? 4 : 0), invert, 4); + if (dither && j != h - 1) + { + ditherLoadNextLine(pixelBuffer, bufferPtr, w * 8 + (paddingBits ? 4 : 0), invert, 4); } for (i = 0; i < w; i++) { - if (dither) { + if (dither) + { 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); + drawPixel((i * 8) + n + x, h - 1 - j + y, ditherGetPixel((i * 8) + n, h - 1 - j, w * 8 + (paddingBits ? 4 : 0), h) >> 5); } } - else { + else + { uint32_t pixelRow = *(bufferPtr++) << 24 | *(bufferPtr++) << 16 | *(bufferPtr++) << 8 | *(bufferPtr++); if (invert) pixelRow = ~pixelRow; @@ -1506,13 +1652,15 @@ int Inkplate::drawGrayscaleBitmap4Web(WiFiClient *s, struct bitmapHeader bmpHead } if (paddingBits) { - if (dither) { + 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); + drawPixel((i * 8) + n + x, h - 1 - j + y, ditherGetPixel((i * 8) + n, h - 1 - j, w * 8 + (paddingBits ? 4 : 0), h) >> 5); } } - else { + else + { uint32_t pixelRow = *(bufferPtr++) << 24 | *(bufferPtr++) << 16 | *(bufferPtr++) << 8 | *(bufferPtr++); if (invert) pixelRow = ~pixelRow; @@ -1559,7 +1707,8 @@ int Inkplate::drawGrayscaleBitmap8Web(WiFiClient *s, struct bitmapHeader bmpHead int i, j; - if (dither) { + if (dither) + { bufferPtr = pixelBuffer; for (i = 0; i < w; i++) pixelBuffer[i] = *(f_pointer++); @@ -1573,7 +1722,8 @@ int Inkplate::drawGrayscaleBitmap8Web(WiFiClient *s, struct bitmapHeader bmpHead for (i = 0; i < w; i++) pixelBuffer[i] = *(f_pointer++); - if (dither && j != h - 1) { + if (dither && j != h - 1) + { ditherLoadNextLine(buf, bufferPtr, w, invert, 8); } @@ -1581,7 +1731,8 @@ int Inkplate::drawGrayscaleBitmap8Web(WiFiClient *s, struct bitmapHeader bmpHead { if (dither) drawPixel(i + x, h - 1 - j + y, ditherGetPixel(i, j, w, h) >> 5); - else { + else + { uint8_t px = 0; if (invert) px = 255 - *(bufferPtr++); @@ -1636,7 +1787,8 @@ int Inkplate::drawGrayscaleBitmap24Web(WiFiClient *s, struct bitmapHeader bmpHea int i, j; - if (dither) { + if (dither) + { bufferPtr = pixelBuffer; for (i = 0; i < w * 3; i++) pixelBuffer[i] = *(f_pointer++); @@ -1650,7 +1802,8 @@ int Inkplate::drawGrayscaleBitmap24Web(WiFiClient *s, struct bitmapHeader bmpHea for (i = 0; i < w * 3; i++) pixelBuffer[i] = *(f_pointer++); - if (dither && j != h - 1) { + if (dither && j != h - 1) + { ditherLoadNextLine(buf, bufferPtr, w, invert, 24); } @@ -1664,7 +1817,8 @@ int Inkplate::drawGrayscaleBitmap24Web(WiFiClient *s, struct bitmapHeader bmpHea //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. if (dither) drawPixel(i + x, h - 1 - j + y, ditherGetPixel(i, j, w, h) >> 5); - else { + else + { uint8_t px = 0; if (invert) px = ((255 - *(bufferPtr++)) * 2126 / 10000) + ((255 - *(bufferPtr++)) * 7152 / 10000) + ((255 - *(bufferPtr++)) * 722 / 10000); diff --git a/Inkplate.h b/Inkplate.h index 5c4e0f9..c482a59 100644 --- a/Inkplate.h +++ b/Inkplate.h @@ -16,6 +16,7 @@ #include "Adafruit_MCP23017.h" #include "SdFat.h" #include "WiFiClient.h" +#include "Triangulate.h" #define INKPLATE_GAMMA 1.45 #define E_INK_WIDTH 800 @@ -160,19 +161,19 @@ public: uint8_t *_partial; uint8_t *D_memory4Bit; uint8_t *_pBuffer; - const uint8_t LUT2[16] ={ B10101010, B10101001, B10100110, B10100101, B10011010, B10011001, B10010110, B10010101, B01101010, B01101001, B01100110, B01100101, B01011010, B01011001, B01010110, B01010101 }; - const uint8_t LUTW[16] ={ B11111111, B11111110, B11111011, B11111010, B11101111, B11101110, B11101011, B11101010, B10111111, B10111110, B10111011, B10111010, B10101111, B10101110, B10101011, B10101010 }; - const uint8_t LUTB[16] ={ B11111111, B11111101, B11110111, B11110101, B11011111, B11011101, B11010111, B11010101, B01111111, B01111101, B01110111, B01110101, B01011111, B01011101, B01010111, B01010101 }; - const uint8_t pixelMaskLUT[8] ={ B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000 }; - const uint8_t pixelMaskGLUT[2] ={ B00001111, B11110000 }; - const uint8_t discharge[16] ={ B11111111, B11111100, B11110011, B11110000, B11001111, B11001100, B11000011, B11000000, B00111111, B00111100, B00110011, B00110000, B00001111, B00001100, B00000011, B00000000 }; + const uint8_t LUT2[16] = {B10101010, B10101001, B10100110, B10100101, B10011010, B10011001, B10010110, B10010101, B01101010, B01101001, B01100110, B01100101, B01011010, B01011001, B01010110, B01010101}; + const uint8_t LUTW[16] = {B11111111, B11111110, B11111011, B11111010, B11101111, B11101110, B11101011, B11101010, B10111111, B10111110, B10111011, B10111010, B10101111, B10101110, B10101011, B10101010}; + const uint8_t LUTB[16] = {B11111111, B11111101, B11110111, B11110101, B11011111, B11011101, B11010111, B11010101, B01111111, B01111101, B01110111, B01110101, B01011111, B01011101, B01010111, B01010101}; + const uint8_t pixelMaskLUT[8] = {B00000001, B00000010, B00000100, B00001000, B00010000, B00100000, B01000000, B10000000}; + const uint8_t pixelMaskGLUT[2] = {B00001111, B11110000}; + const uint8_t discharge[16] = {B11111111, B11111100, B11110011, B11110000, B11001111, B11001100, B11000011, B11000000, B00111111, B00111100, B00110011, B00110000, B00001111, B00001100, B00000011, B00000000}; //BLACK->WHITE //THIS IS OKAYISH WAVEFORM FOR GRAYSCALE. IT CAN BE MUCH BETTER. - const uint8_t waveform3Bit[8][7] ={ { 0, 0, 0, 0, 1, 1, 1 }, { 0, 0, 1, 1, 1, 2, 1 }, { 0, 1, 1, 2, 1, 2, 1 }, { 0, 0, 1, 1, 2, 1, 2 }, { 1, 1, 1, 2, 2, 1, 2 }, { 0, 0, 1, 1, 1, 2, 2 }, { 0, 1, 1, 2, 1, 2, 2 }, { 0, 0, 0, 0, 0, 0, 2 } }; + const uint8_t waveform3Bit[8][7] = {{0, 0, 0, 0, 1, 1, 1}, {0, 0, 1, 1, 1, 2, 1}, {0, 1, 1, 2, 1, 2, 1}, {0, 0, 1, 1, 2, 1, 2}, {1, 1, 1, 2, 2, 1, 2}, {0, 0, 1, 1, 1, 2, 2}, {0, 1, 1, 2, 1, 2, 2}, {0, 0, 0, 0, 0, 0, 2}}; //const uint8_t waveform3Bit[8][12] = {{3,3,3,1,1,1,1,1,1,1,2,0}, {3,3,3,3,1,1,1,1,1,1,2,0}, {3,3,3,3,3,1,1,1,1,1,2,0}, {3,3,3,3,3,3,1,1,1,1,2,0}, {3,3,3,3,3,3,3,1,1,1,2,0}, {3,3,3,3,3,3,3,2,1,1,2,0}, {3,3,3,3,3,3,3,3,3,1,2,0}, {3,3,3,3,3,3,3,3,3,3,2,0}}; //const uint8_t waveform3Bit[16][12] = {{0,0,0,0,0,0,1,2,1,1,0,3},{0,0,1,1,1,2,2,2,1,1,0,3},{0,0,0,1,1,2,2,2,1,1,0,3}, {0,0,0,1,2,1,2,1,2,1,3}, {0,0,2,1,2,1,2,1,2,1,3}, {0,0,1,2,2,1,1,1,1,2,0,3}, {0,0,0,2,1,1,1,1,0,2,0,3}, {0,0,2,1,2,2,1,1,1,2,0,3}, {0,0,0,2,2,2,1,1,1,2,0,3}, {0,0,0,0,0,0,2,1,1,2,0,3}, {0,0,0,0,0,2,2,1,1,2,0,3}, {0,0,0,0,0,1,1,1,2,2,0,3}, {0,0,0,0,1,2,1,2,1,2,0,3}, {0,0,0,0,1,1,2,2,1,2,0,3},{0,0,0,0,1,1,1,2,2,2,0,3}, {0,0,0,0,0,0,0,0,0,2,0,3}}; //PVI waveform for cleaning screen, not sure if it is correct, but it cleans screen properly. - const uint32_t waveform[50] ={ 0x00000008, 0x00000008, 0x00200408, 0x80281888, 0x60a81898, 0x60a8a8a8, 0x60a8a8a8, 0x6068a868, 0x6868a868, 0x6868a868, 0x68686868, 0x6a686868, 0x5a686868, 0x5a686868, 0x5a586a68, 0x5a5a6a68, 0x5a5a6a68, 0x55566a68, 0x55565a64, 0x55555654, 0x55555556, 0x55555556, 0x55555556, 0x55555516, 0x55555596, 0x15555595, 0x95955595, 0x95959595, 0x95949495, 0x94949495, 0x94949495, 0xa4949494, 0x9494a4a4, 0x84a49494, 0x84948484, 0x84848484, 0x84848484, 0x84848484, 0xa5a48484, 0xa9a4a4a8, 0xa9a8a8a8, 0xa5a9a9a4, 0xa5a5a5a4, 0xa1a5a5a1, 0xa9a9a9a9, 0xa9a9a9a9, 0xa9a9a9a9, 0xa9a9a9a9, 0x15151515, 0x11111111 }; + const uint32_t waveform[50] = {0x00000008, 0x00000008, 0x00200408, 0x80281888, 0x60a81898, 0x60a8a8a8, 0x60a8a8a8, 0x6068a868, 0x6868a868, 0x6868a868, 0x68686868, 0x6a686868, 0x5a686868, 0x5a686868, 0x5a586a68, 0x5a5a6a68, 0x5a5a6a68, 0x55566a68, 0x55565a64, 0x55555654, 0x55555556, 0x55555556, 0x55555556, 0x55555516, 0x55555596, 0x15555595, 0x95955595, 0x95959595, 0x95949495, 0x94949495, 0x94949495, 0xa4949494, 0x9494a4a4, 0x84a49494, 0x84948484, 0x84848484, 0x84848484, 0x84848484, 0xa5a48484, 0xa9a4a4a8, 0xa9a8a8a8, 0xa5a9a9a4, 0xa5a5a5a4, 0xa1a5a5a1, 0xa9a9a9a9, 0xa9a9a9a9, 0xa9a9a9a9, 0xa9a9a9a9, 0x15151515, 0x11111111}; struct bitmapHeader { @@ -203,6 +204,10 @@ public: int drawBitmapFromSD(char *fileName, int x, int y, bool dither = false, bool invert = false); int drawBitmapFromWeb(WiFiClient *s, int x, int y, int len, bool dither = false, bool invert = false); int drawBitmapFromWeb(char *url, int x, int y, bool dither = false, bool invert = false); + 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); int sdCardInit(); @@ -234,12 +239,14 @@ private: uint8_t _blockPartial = 1; uint8_t _beginDone = 0; + Triangulate triangulate; + void display1b(); void display3b(); uint32_t read32(uint8_t *c); uint16_t read16(uint8_t *c); - 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 ditherStart(uint8_t *pixelBuffer, uint8_t *bufferPtr, int w, bool invert, uint8_t bits); + void ditherLoadNextLine(uint8_t *pixelBuffer, uint8_t *bufferPtr, int w, bool invert, uint8_t bits); uint8_t ditherGetPixel(int i, int j, int w, int h); uint8_t ditherSwap(int w); void readBmpHeaderSd(SdFile *_f, struct bitmapHeader *_h); diff --git a/Triangulate.cpp b/Triangulate.cpp new file mode 100644 index 0000000..9c3cd43 --- /dev/null +++ b/Triangulate.cpp @@ -0,0 +1,145 @@ +#include "Arduino.h" +#include "math.h" +#include "Triangulate.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/Triangulate.h b/Triangulate.h new file mode 100644 index 0000000..7fa2a5c --- /dev/null +++ b/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/examples/1. Basic Inkplate Functionality/1-Inkplate_basic_monochrome/1-Inkplate_basic_monochrome.ino b/examples/1. Basic Inkplate Functionality/1-Inkplate_basic_monochrome/1-Inkplate_basic_monochrome.ino index 0cc16d2..e869cde 100644 --- a/examples/1. Basic Inkplate Functionality/1-Inkplate_basic_monochrome/1-Inkplate_basic_monochrome.ino +++ b/examples/1. Basic Inkplate Functionality/1-Inkplate_basic_monochrome/1-Inkplate_basic_monochrome.ino @@ -361,6 +361,59 @@ void loop() display.display(); delay(DELAY_MS); + // Draws an elipse with x radius, y radius, center x, center y and color + display.clearDisplay(); + display.drawElipse(100, 200, 400, 300, BLACK); + displayCurrentAction("Drawing an elipse"); + display.display(); + + delay(DELAY_MS); + + // Fills an elipse with x radius, y radius, center x, center y and color + display.clearDisplay(); + display.fillElipse(100, 200, 400, 300, BLACK); + displayCurrentAction("Drawing a filled elipse"); + display.display(); + + delay(DELAY_MS); + + // Code block for generating random points and sorting them in a counter + // clockwise direction. + int xt[10]; + int yt[10]; + int n = 10; + for (int i = 0; i < n; ++i) + { + xt[i] = random(100, 700); + yt[i] = random(100, 500); + } + int k; + for (int i = 0; i < n - 1; ++i) + for (int j = i + 1; j < n; ++j) + if (atan2(yt[j] - 300, xt[j] - 400) < atan2(yt[i] - 300, xt[i] - 400)) + { + k = xt[i], xt[i] = xt[j], xt[j] = k; + k = yt[i], yt[i] = yt[j], yt[j] = k; + } + + // Draws a polygon, from x and y coordinate arrays of n points in color c + display.clearDisplay(); + display.drawPolygon(xt, yt, n, BLACK); + displayCurrentAction("Drawing a polygon"); + display.display(); + + delay(DELAY_MS); + + // Fills a polygon, from x and y coordinate arrays of n points in color c, + // Points need to be counter clockwise sorted + // Method can be quite slow for now, probably will improve + display.clearDisplay(); + display.fillPolygon(xt, yt, n, BLACK); + displayCurrentAction("Drawing a filled polygon"); + display.display(); + + delay(DELAY_MS); + //Write text and rotate it by 90 deg. forever int r = 0; display.setTextSize(8); diff --git a/examples/1. Basic Inkplate Functionality/2-Inkplate_basic_grayscale/2-Inkplate_basic_grayscale.ino b/examples/1. Basic Inkplate Functionality/2-Inkplate_basic_grayscale/2-Inkplate_basic_grayscale.ino index 4cda363..393994c 100644 --- a/examples/1. Basic Inkplate Functionality/2-Inkplate_basic_grayscale/2-Inkplate_basic_grayscale.ino +++ b/examples/1. Basic Inkplate Functionality/2-Inkplate_basic_grayscale/2-Inkplate_basic_grayscale.ino @@ -54,9 +54,9 @@ void loop() //Now, let's draw some random pixels! display.clearDisplay(); //Clear everything that is inside frame buffer in ESP32 for (int i = 0; i < 1000; i++) - { //Write 1000 random colored pixels at random locations + { //Write 1000 random colored pixels at random locations display.drawPixel(random(0, 799), random(0, 599), random(0, 7)); //We are setting color of the pixels using numbers from 0 to 7, - } //where 0 mens black, 7 white and gray is in between + } //where 0 mens black, 7 white and gray is in between displayCurrentAction("Drawing 600 random pixels in random colors"); display.display(); //Write everything from frame buffer to screen delay(DELAY_MS); //Wait @@ -275,6 +275,59 @@ void loop() display.display(); delay(DELAY_MS); + // Draws an elipse with x radius, y radius, center x, center y and color + display.clearDisplay(); + display.drawElipse(100, 200, 400, 300, 0); + displayCurrentAction("Drawing an elipse"); + display.display(); + + delay(DELAY_MS); + + // Fills an elipse with x radius, y radius, center x, center y and color + display.clearDisplay(); + display.fillElipse(100, 200, 400, 300, 0); + displayCurrentAction("Drawing a filled elipse"); + display.display(); + + delay(DELAY_MS); + + // Code block for generating random points and sorting them in a counter + // clockwise direction. + int xt[10]; + int yt[10]; + int n = 10; + for (int i = 0; i < n; ++i) + { + xt[i] = random(100, 700); + yt[i] = random(100, 500); + } + int k; + for (int i = 0; i < n - 1; ++i) + for (int j = i + 1; j < n; ++j) + if (atan2(yt[j] - 300, xt[j] - 400) < atan2(yt[i] - 300, xt[i] - 400)) + { + k = xt[i], xt[i] = xt[j], xt[j] = k; + k = yt[i], yt[i] = yt[j], yt[j] = k; + } + + // Draws a polygon, from x and y coordinate arrays of n points in color c + display.clearDisplay(); + display.drawPolygon(xt, yt, n, 0); + displayCurrentAction("Drawing a polygon"); + display.display(); + + delay(DELAY_MS); + + // Fills a polygon, from x and y coordinate arrays of n points in color c, + // Points need to be counter clockwise sorted + // Method can be quite slow for now, probably will improve + display.clearDisplay(); + display.fillPolygon(xt, yt, n, 0); + displayCurrentAction("Drawing a filled polygon"); + display.display(); + + delay(DELAY_MS); + //Write text and rotate it by 90 deg. forever int r = 0; display.setTextSize(8); diff --git a/fontconvert/fontconvert b/fontconvert/fontconvert new file mode 100755 index 0000000..3e1b624 Binary files /dev/null and b/fontconvert/fontconvert differ diff --git a/fontconvert/script.sh b/fontconvert/script.sh new file mode 100644 index 0000000..e69de29 diff --git a/fontconvert/toconvert/Arial.ttf b/fontconvert/toconvert/Arial.ttf new file mode 100644 index 0000000..6b30996 Binary files /dev/null and b/fontconvert/toconvert/Arial.ttf differ diff --git a/gfxfont.h b/gfxfont.h index c691de6..d7a75a3 100755 --- a/gfxfont.h +++ b/gfxfont.h @@ -8,22 +8,24 @@ #define _GFXFONT_H_ /// Font data stored PER GLYPH -typedef struct { - uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap - uint8_t width; ///< Bitmap dimensions in pixels - uint8_t height; ///< Bitmap dimensions in pixels - uint8_t xAdvance; ///< Distance to advance cursor (x axis) - int8_t xOffset; ///< X dist from cursor pos to UL corner - int8_t yOffset; ///< Y dist from cursor pos to UL corner +typedef struct +{ + uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap + uint8_t width; ///< Bitmap dimensions in pixels + uint8_t height; ///< Bitmap dimensions in pixels + uint8_t xAdvance; ///< Distance to advance cursor (x axis) + int8_t xOffset; ///< X dist from cursor pos to UL corner + int8_t yOffset; ///< Y dist from cursor pos to UL corner } GFXglyph; /// Data stored for FONT AS A WHOLE -typedef struct { - uint8_t *bitmap; ///< Glyph bitmaps, concatenated - GFXglyph *glyph; ///< Glyph array - uint8_t first; ///< ASCII extents (first char) - uint8_t last; ///< ASCII extents (last char) - uint8_t yAdvance; ///< Newline distance (y axis) +typedef struct +{ + uint8_t *bitmap; ///< Glyph bitmaps, concatenated + GFXglyph *glyph; ///< Glyph array + uint8_t first; ///< ASCII extents (first char) + uint8_t last; ///< ASCII extents (last char) + uint8_t yAdvance; ///< Newline distance (y axis) } GFXfont; #endif // _GFXFONT_H_