Polygon drawing, untested.

This commit is contained in:
nitko12 2020-08-25 15:53:09 +02:00
parent 4e2989b02b
commit a0d91f1004
8 changed files with 319 additions and 83 deletions

View File

@ -515,6 +515,21 @@ int Inkplate::drawBitmapFromWeb(char *url, int x, int y, bool dither, bool inver
return ret;
}
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::drawThickLine(int x1, int y1, int x2, int y2, int color, float thickness)
{
float deg = atan2f((float)(y2 - y1), (float)(x2 - x1));
@ -1088,7 +1103,8 @@ 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));
@ -1100,20 +1116,23 @@ int Inkplate::drawGrayscaleBitmap4Sd(SdFile *f, struct bitmapHeader bmpHeader, i
bufferPtr = pixelBuffer;
f->read(pixelBuffer, w * 4 + (paddingBits ? 4 : 0));
if (dither && j != h - 1) {
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);
}
}
else {
else
{
uint32_t pixelRow = *(bufferPtr++) << 24 | *(bufferPtr++) << 16 | *(bufferPtr++) << 8 | *(bufferPtr++);
if (invert)
pixelRow = ~pixelRow;
@ -1125,13 +1144,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);
}
}
else {
else
{
uint32_t pixelRow = *(bufferPtr++) << 24 | *(bufferPtr++) << 16 | *(bufferPtr++) << 8 | *(bufferPtr++);
if (invert)
pixelRow = ~pixelRow;
@ -1158,7 +1179,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 +1192,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 +1201,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 +1237,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 +1250,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 +1264,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 +1295,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 +1346,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 +1399,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 +1421,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,7 +1510,8 @@ 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++)
pixelBuffer[i] = *(f_pointer++);
@ -1481,20 +1525,23 @@ int Inkplate::drawGrayscaleBitmap4Web(WiFiClient *s, struct bitmapHeader bmpHead
for (i = 0; i < w * 4 + (paddingBits ? 1 : 0); i++)
pixelBuffer[i] = *(f_pointer++);
if (dither && j != h - 1) {
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);
}
}
else {
else
{
uint32_t pixelRow = *(bufferPtr++) << 24 | *(bufferPtr++) << 16 | *(bufferPtr++) << 8 | *(bufferPtr++);
if (invert)
pixelRow = ~pixelRow;
@ -1506,13 +1553,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);
}
}
else {
else
{
uint32_t pixelRow = *(bufferPtr++) << 24 | *(bufferPtr++) << 16 | *(bufferPtr++) << 8 | *(bufferPtr++);
if (invert)
pixelRow = ~pixelRow;
@ -1559,7 +1608,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 +1623,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 +1632,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 +1688,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 +1703,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 +1718,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);

View File

@ -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
@ -203,6 +204,7 @@ 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 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,6 +236,8 @@ private:
uint8_t _blockPartial = 1;
uint8_t _beginDone = 0;
Triangulate triangulate;
void display1b();
void display3b();
uint32_t read32(uint8_t *c);

145
Triangulate.cpp Normal file
View File

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

30
Triangulate.h Normal file
View File

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

BIN
fontconvert/fontconvert Executable file

Binary file not shown.

0
fontconvert/script.sh Normal file
View File

Binary file not shown.

View File

@ -8,7 +8,8 @@
#define _GFXFONT_H_
/// Font data stored PER GLYPH
typedef struct {
typedef struct
{
uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap
uint8_t width; ///< Bitmap dimensions in pixels
uint8_t height; ///< Bitmap dimensions in pixels
@ -18,7 +19,8 @@ typedef struct {
} GFXglyph;
/// Data stored for FONT AS A WHOLE
typedef struct {
typedef struct
{
uint8_t *bitmap; ///< Glyph bitmaps, concatenated
GFXglyph *glyph; ///< Glyph array
uint8_t first; ///< ASCII extents (first char)