/*
A set of functions and utilites to help create nice graphics on arduino displays
By Fyodor.
*/
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#define TFT_DC 2
#define TFT_CS 15
Adafruit_ILI9341 display = Adafruit_ILI9341(TFT_CS, TFT_DC);
void setup() {
display.begin();
display.setRotation(1);
display.fillScreen(0x0000);
drawRoundGradientRect(2, 2, 316, 236, 8, 0x07E0, 0xFFE0, 1);
fillGradientRect(10, 10, 220, 220, 0x9018, 0x07FF, 0);
fillRoundGradientRect(240, 10, 70, 220, 10, 0xFD00, 0xFB35, 2);
fillGradientTriangle(15, 20, 15, 225, 220, 225, 0x0640, 0xFFE0, 3);
drawGradientTriangle(20, 15, 225, 15, 225, 220, 0xF800, 0xF3ED, 1);
fillGradientCircle(45, 195, 30, 0x865C, 0xFBE9, 0);
}
void loop() {
}
uint16_t interpolateColor(uint16_t color1, uint16_t color2, float fraction) {
int r = (((color1 >> 11) & 0x1F) * (1 - fraction)) + (((color2 >> 11) & 0x1F) * fraction);
int g = (((color1 >> 5) & 0x3F) * (1 - fraction)) + (((color2 >> 5) & 0x3F) * fraction);
int b = ((color1 & 0x1F) * (1 - fraction)) + ((color2 & 0x1F) * fraction);
return (r << 11) | (g << 5) | b;
}
void drawGradientRect(int x, int y, int w, int h, uint16_t color1, uint16_t color2, int direction) {
int adjustedDirection = (direction + display.getRotation()) % 4;
for (int i = 0; i < (adjustedDirection % 2 == 0 ? w : h); i++) {
float fraction = (float)i / (adjustedDirection % 2 == 0 ? w : h);
uint16_t interpolatedColor = interpolateColor(color1, color2, fraction);
switch (adjustedDirection) {
case 0: // Left to Right
display.drawRect(x + i, y, 1, h, interpolatedColor);
break;
case 1: // Top to Bottom
display.drawRect(x, y + i, w, 1, interpolatedColor);
break;
case 2: // Right to Left
display.drawRect(x + w - 1 - i, y, 1, h, interpolatedColor);
break;
case 3: // Bottom to Top
display.drawRect(x, y + h - 1 - i, w, 1, interpolatedColor);
break;
}
}
}
void fillGradientRect(int x, int y, int w, int h, uint16_t color1, uint16_t color2, int direction) {
int adjustedDirection = (direction + display.getRotation()) % 4;
for (int i = 0; i < (adjustedDirection % 2 == 0 ? w : h); i++) {
float fraction = (float)i / (adjustedDirection % 2 == 0 ? w : h);
uint16_t interpolatedColor = interpolateColor(color1, color2, fraction);
switch (adjustedDirection) {
case 0: // Left to Right
display.fillRect(x + i, y, 1, h, interpolatedColor);
break;
case 1: // Top to Bottom
display.fillRect(x, y + i, w, 1, interpolatedColor);
break;
case 2: // Right to Left
display.fillRect(x + w - 1 - i, y, 1, h, interpolatedColor);
break;
case 3: // Bottom to Top
display.fillRect(x, y + h - 1 - i, w, 1, interpolatedColor);
break;
}
}
}
void drawRoundGradientRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color1, uint16_t color2, int gradientRotation) {
// Create a 1-bit offscreen buffer for the shape
GFXcanvas1 shape(w, h);
// Draw a rounded rectangle into the buffer with 'color' set to 1
shape.drawRoundRect(0, 0, w, h, r, 1);
// Adjust gradient rotation based on display orientation
int adjustedRotation = (gradientRotation + display.getRotation()) % 4;
display.startWrite();
// Iterate over the buffer and apply the gradient colors
for (int16_t iy = 0; iy < h; iy++) {
for (int16_t ix = 0; ix < w; ix++) {
// Only draw where the shape buffer is set (i.e., where the rounded rectangle is)
if (shape.getPixel(ix, iy)) {
// Calculate the gradient fraction based on the current position and gradient direction
float fraction;
switch (adjustedRotation) {
case 0: // Left to Right
fraction = (float)ix / (w - 1);
break;
case 1: // Top to Bottom
fraction = (float)iy / (h - 1);
break;
case 2: // Right to Left
fraction = 1.0 - (float)ix / (w - 1);
break;
case 3: // Bottom to Top
fraction = 1.0 - (float)iy / (h - 1);
break;
}
// Interpolate the color based on the calculated fraction
uint16_t interpolatedColor = interpolateColor(color1, color2, fraction);
// Draw the pixel on the display with the interpolated color
display.writePixel(x + ix, y + iy, interpolatedColor);
}
}
}
display.endWrite();
}
void fillRoundGradientRect(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint16_t color1, uint16_t color2, int gradientRotation) {
// Create a 1-bit offscreen buffer for the shape
GFXcanvas1 shape(w, h);
// Draw a filled rounded rectangle into the buffer with 'color' set to 1
shape.fillRoundRect(0, 0, w, h, r, 1);
// Adjust gradient rotation based on display orientation
int adjustedRotation = (gradientRotation + display.getRotation()) % 4;
display.startWrite();
// Iterate over the buffer and apply the gradient colors
for (int16_t iy = 0; iy < h; iy++) {
for (int16_t ix = 0; ix < w; ix++) {
// Only draw where the shape buffer is set (i.e., where the rounded rectangle is)
if (shape.getPixel(ix, iy)) {
// Calculate the gradient fraction based on the current position and gradient direction
float fraction;
switch (adjustedRotation) {
case 0: // Left to Right
fraction = (float)ix / (w - 1);
break;
case 1: // Top to Bottom
fraction = (float)iy / (h - 1);
break;
case 2: // Right to Left
fraction = 1.0 - (float)ix / (w - 1);
break;
case 3: // Bottom to Top
fraction = 1.0 - (float)iy / (h - 1);
break;
}
// Interpolate the color based on the calculated fraction
uint16_t interpolatedColor = interpolateColor(color1, color2, fraction);
// Draw the pixel on the display with the interpolated color
display.writePixel(x + ix, y + iy, interpolatedColor);
}
}
}
display.endWrite();
}
void fillGradientTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color1, uint16_t color2, int gradientRotation) {
// Determine the bounding box of the triangle
int16_t minX = min(x0, min(x1, x2));
int16_t maxX = max(x0, max(x1, x2));
int16_t minY = min(y0, min(y1, y2));
int16_t maxY = max(y0, max(y1, y2));
int16_t w = maxX - minX + 1;
int16_t h = maxY - minY + 1;
// Create a 1-bit offscreen buffer for the triangle
GFXcanvas1 shape(w, h);
// Draw the triangle into the buffer with 'color' set to 1
shape.fillTriangle(x0 - minX, y0 - minY, x1 - minX, y1 - minY, x2 - minX, y2 - minY, 1);
// Adjust gradient rotation based on display orientation
int adjustedRotation = (gradientRotation + display.getRotation()) % 4;
// Iterate over the buffer and apply the gradient colors
display.startWrite();
for (int16_t iy = 0; iy < h; iy++) {
for (int16_t ix = 0; ix < w; ix++) {
// Only draw where the shape buffer is set (i.e., where the triangle is)
if (shape.getPixel(ix, iy)) {
// Calculate the gradient fraction based on the current position and gradient direction
float fraction;
switch (adjustedRotation) {
case 0: // Left to Right
fraction = (float)ix / (w - 1);
break;
case 1: // Top to Bottom
fraction = (float)iy / (h - 1);
break;
case 2: // Right to Left
fraction = 1.0 - (float)ix / (w - 1);
break;
case 3: // Bottom to Top
fraction = 1.0 - (float)iy / (h - 1);
break;
}
// Interpolate the color based on the calculated fraction
uint16_t interpolatedColor = interpolateColor(color1, color2, fraction);
// Draw the pixel on the display with the interpolated color
display.writePixel(minX + ix, minY + iy, interpolatedColor);
}
}
}
display.endWrite();
}
void drawGradientTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color1, uint16_t color2, int gradientRotation) {
// Determine the bounding box of the triangle
int16_t minX = min(x0, min(x1, x2));
int16_t maxX = max(x0, max(x1, x2));
int16_t minY = min(y0, min(y1, y2));
int16_t maxY = max(y0, max(y1, y2));
int16_t w = maxX - minX + 1;
int16_t h = maxY - minY + 1;
// Create a 1-bit offscreen buffer for the triangle
GFXcanvas1 shape(w, h);
// Draw the triangle into the buffer with 'color' set to 1
shape.drawTriangle(x0 - minX, y0 - minY, x1 - minX, y1 - minY, x2 - minX, y2 - minY, 1);
// Adjust gradient rotation based on display orientation
int adjustedRotation = (gradientRotation + display.getRotation()) % 4;
// Iterate over the buffer and apply the gradient colors
display.startWrite();
for (int16_t iy = 0; iy < h; iy++) {
for (int16_t ix = 0; ix < w; ix++) {
// Only draw where the shape buffer is set (i.e., where the triangle is)
if (shape.getPixel(ix, iy)) {
// Calculate the gradient fraction based on the current position and gradient direction
float fraction;
switch (adjustedRotation) {
case 0: // Left to Right
fraction = (float)ix / (w - 1);
break;
case 1: // Top to Bottom
fraction = (float)iy / (h - 1);
break;
case 2: // Right to Left
fraction = 1.0 - (float)ix / (w - 1);
break;
case 3: // Bottom to Top
fraction = 1.0 - (float)iy / (h - 1);
break;
}
// Interpolate the color based on the calculated fraction
uint16_t interpolatedColor = interpolateColor(color1, color2, fraction);
// Draw the pixel on the display with the interpolated color
display.writePixel(minX + ix, minY + iy, interpolatedColor);
}
}
}
display.endWrite();
}
void fillGradientCircle(int16_t x, int16_t y, int16_t radius, uint16_t color1, uint16_t color2, int gradientRotation) {
// Create a 1-bit offscreen buffer for the circle, slightly larger to avoid clipping
GFXcanvas1 shape(radius * 2 + 2, radius * 2 + 2);
// Draw the circle into the buffer with 'color' set to 1
shape.fillCircle(radius + 1, radius + 1, radius, 1);
// Iterate over the buffer and apply the gradient colors
for (int16_t iy = 0; iy < shape.height(); iy++) {
for (int16_t ix = 0; ix < shape.width(); ix++) {
// Only draw where the shape buffer is set (i.e., where the circle is)
if (shape.getPixel(ix, iy)) {
// Calculate the gradient fraction based on the current position
float fraction;
switch (gradientRotation) {
case 0: // Left to Right
fraction = (float)ix / (shape.width() - 1);
break;
case 1: // Top to Bottom
fraction = (float)iy / (shape.height() - 1);
break;
case 2: // Right to Left
fraction = 1.0 - (float)ix / (shape.width() - 1);
break;
case 3: // Bottom to Top
fraction = 1.0 - (float)iy / (shape.height() - 1);
break;
}
// Interpolate the color based on the calculated fraction
uint16_t interpolatedColor = interpolateColor(color1, color2, fraction);
// Draw the pixel on the display with the interpolated color
display.drawPixel(x + ix - radius - 1, y + iy - radius - 1, interpolatedColor);
}
}
}
}
void drawGradientCircle(int16_t x, int16_t y, int16_t radius, uint16_t color1, uint16_t color2, int gradientRotation) {
// Create a 1-bit offscreen buffer for the circle, slightly larger to avoid clipping
GFXcanvas1 shape(radius * 2 + 2, radius * 2 + 2);
// Draw the circle into the buffer with 'color' set to 1
shape.drawCircle(radius + 1, radius + 1, radius, 1);
// Iterate over the buffer and apply the gradient colors
for (int16_t iy = 0; iy < shape.height(); iy++) {
for (int16_t ix = 0; ix < shape.width(); ix++) {
// Only draw where the shape buffer is set (i.e., where the circle is)
if (shape.getPixel(ix, iy)) {
// Calculate the gradient fraction based on the current position
float fraction;
switch (gradientRotation) {
case 0: // Left to Right
fraction = (float)ix / (shape.width() - 1);
break;
case 1: // Top to Bottom
fraction = (float)iy / (shape.height() - 1);
break;
case 2: // Right to Left
fraction = 1.0 - (float)ix / (shape.width() - 1);
break;
case 3: // Bottom to Top
fraction = 1.0 - (float)iy / (shape.height() - 1);
break;
}
// Interpolate the color based on the calculated fraction
uint16_t interpolatedColor = interpolateColor(color1, color2, fraction);
// Draw the pixel on the display with the interpolated color
display.drawPixel(x + ix - radius - 1, y + iy - radius - 1, interpolatedColor);
}
}
}
}