Files
TX/source/Graphics/graphicsLibrary.c

856 lines
18 KiB
C
Raw Permalink Normal View History

/*
* graphicsLibrary.c
*
* Created on: Feb 12, 2022
* Author: Brian.Bailey
*/
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include "fsl_common.h"
#include "..\lcd.h"
#include "..\Fonts\fontLibrary.h"
#include "lcd.h"
#include "graphicsLibrary.h"
/*******************************************************************************
* Definitions
******************************************************************************/
#define BMP_WIDTH_INDEX 0 //Location of xSize in bitmap array
#define BMP_HEIGHT_INDEX 1 //Location of ySize in bitmap array
#define BMP_DATA_INDEX 2 //Location of data in bitmap array
#define ABS(x) ((x) > 0 ? (x) : -(x))
/*******************************************************************************
* Static Function Declarations
******************************************************************************/
static void DrawHLine(int16_t x0, int16_t y0, uint16_t width, LCD_DRAWMODE_t drawMode);
static void DrawVLine(int16_t x0, int16_t y0, uint16_t height, LCD_DRAWMODE_t drawMode);
static void xLine(int16_t x1, int16_t x2, int16_t y, LCD_DRAWMODE_t drawMode);
static void yLine(int16_t x, int16_t y1, int16_t y2, LCD_DRAWMODE_t drawMode);
/*******************************************************************************
* Static Functions
******************************************************************************/
/*
* Draw a horizontal line with given color
*/
static void DrawHLine(int16_t x0, int16_t y0, uint16_t width, LCD_DRAWMODE_t drawMode)
{
for(int16_t x = x0; x < (x0+width); x++)
{
LCD_DrawPixel(x, y0, drawMode);
}
}
/*
* Draw a vertical line with given color
*/
static void DrawVLine(int16_t x0, int16_t y0, uint16_t height, LCD_DRAWMODE_t drawMode)
{
for(int16_t y = y0; y < (y0+height); y++)
{
LCD_DrawPixel(x0, y, drawMode);
}
}
//Supporting functions for GL_DrawCircle2
static void xLine(int16_t x1, int16_t x2, int16_t y, LCD_DRAWMODE_t drawMode)
{
while (x1 <= x2)
{
LCD_DrawPixel(x1++, y, drawMode);
}
}
static void yLine(int16_t x, int16_t y1, int16_t y2, LCD_DRAWMODE_t drawMode)
{
while (y1 <= y2)
{
LCD_DrawPixel(x, y1++, drawMode);
}
}
/*******************************************************************************
* Public Functions
******************************************************************************/
/*
* Draw a line using Bresenham's algorithm with given color. //ADD WIDTH
*/
void GL_DrawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t thickness, LCD_DRAWMODE_t drawMode)
{
int16_t dx; //width
int16_t sx; //x increment direction
int16_t dy; //height
int16_t sy; //y increment direction
int16_t err;
int16_t e2;
int16_t widthOffsetLow; //line thickness low offset for perpendicular line
int16_t widthOffsetHigh; //line thickness high offset for perpendicular line
dx = ABS(x1 - x0);
sx = x0 < x1 ? 1 : -1;
dy = ABS(y1 - y0);
sy = y0 < y1 ? 1 : -1;
err = (dx > dy ? dx : -dy) / 2;
while (1)
{
if (x0 == x1 && y0 == y1)
{
break;
}
//Draw a line perpendicular to the first with length "thickness"
widthOffsetLow = ((thickness-1)*-1) >> 1;
widthOffsetHigh = thickness >> 1;
for(int16_t i =widthOffsetLow; i <= widthOffsetHigh; i++)
{
if(dy > dx) //if line is more horizontal, draw horizontal line
{
LCD_DrawPixel(x0 + i, y0, drawMode);
}
else //vertical line
{
LCD_DrawPixel(x0, y0 + i, drawMode);
}
}
e2 = err + err;
if (e2 > -dx)
{
err -= dy;
x0 += sx;
}
if (e2 < dy)
{
err += dx;
y0 += sy;
}
}
}
/*
* Draw a rectangle with given color and thickness.
* Corners will move outward with increasing thickness
*/
void GL_DrawRectangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t thickness, LCD_DRAWMODE_t drawMode)
{
/* Make sure x0 is smaller than x1. */
if (x0 > x1)
{
x0 = x0 + x1;
x1 = x0 - x1;
x0 = x0 - x1;
}
/* Make sure y0 is smaller than y1. */
if (y0 > y1)
{
y0 = y0 + y1;
y1 = y0 - y1;
y0 = y0 - y1;
}
uint16_t width = x1 - x0 + 1;
uint16_t height = y1 - y0 + 1;
//Draw concentric rectangles to increase thickness
//This overwrites the corner pixels
int16_t thicknessOffsetLow = ((thickness-1)*-1) >> 1; //line thickness low offset
int16_t thicknessOffsetHigh = thickness >> 1; //line thickness high offset
for(int16_t i = thicknessOffsetLow; i <= thicknessOffsetHigh; i++)
{
DrawHLine(x0 + i, y0 + i, width + 2*i*-1, drawMode); //top horizontal
DrawHLine(x0 - i, y1 + i, width + 2*i, drawMode); //bottom horizontal
DrawVLine(x0 + i, y0 + i, height + 2*i*-1, drawMode); //left vertical
DrawVLine(x1 + i, y0 - i, height + 2*i, drawMode); //right vertical
}
}
/*
* Draw a filled rectangle with given color.
*/
void GL_DrawFilledRectangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, LCD_DRAWMODE_t drawMode)
{
/* Make sure x0 is smaller than x1. */
if (x0 > x1)
{
x0 = x0 + x1;
x1 = x0 - x1;
x0 = x0 - x1;
}
/* Make sure y0 is smaller than y1. */
if (y0 > y1)
{
y0 = y0 + y1;
y1 = y0 - y1;
y0 = y0 - y1;
}
uint16_t width = x1 - x0 + 1;
uint16_t height = y1 - y0 + 1;
for (uint16_t i = 0; i < height; i++)
{
DrawHLine(x0, y0 + i, width, drawMode);
}
}
void GL_DrawCircle(int16_t xc, int16_t yc, int16_t r, LCD_DRAWMODE_t drawMode)
{
int16_t x = 0;
int16_t y = r;
int16_t d = 3 - 2 * r;
LCD_DrawPixel(xc + x, yc + y, drawMode);
LCD_DrawPixel(xc - x, yc + y, drawMode);
LCD_DrawPixel(xc + x, yc - y, drawMode);
LCD_DrawPixel(xc - x, yc - y, drawMode);
LCD_DrawPixel(xc + y, yc + x, drawMode);
LCD_DrawPixel(xc - y, yc + x, drawMode);
LCD_DrawPixel(xc + y, yc - x, drawMode);
LCD_DrawPixel(xc - y, yc - x, drawMode);
while (y >= x)
{
x++;
if (d > 0)
{
y--;
d = d + 4 * (x - y) + 10;
}
else
{
d = d + 4 * x + 6;
}
LCD_DrawPixel(xc + x, yc + y, drawMode);
LCD_DrawPixel(xc - x, yc + y, drawMode);
LCD_DrawPixel(xc + x, yc - y, drawMode);
LCD_DrawPixel(xc - x, yc - y, drawMode);
LCD_DrawPixel(xc + y, yc + x, drawMode);
LCD_DrawPixel(xc - y, yc + x, drawMode);
LCD_DrawPixel(xc + y, yc - x, drawMode);
LCD_DrawPixel(xc - y, yc - x, drawMode);
}
}
//Draw two circles with fill between them (circle with thickness)
//uses midpoint circle algorithm
// from https://stackoverflow.com/questions/27755514/circle-with-thickness-drawing-algorithm
void GL_DrawCircle2(int16_t xc, int16_t yc, int16_t rInner, int16_t rOuter, LCD_DRAWMODE_t drawMode)
{
if(rInner > rOuter) //exit if inner circle radius larger than rOuter
{
return;
}
int16_t xo = rOuter; //rOuter circle radius
int16_t xi = rInner; //rInner circle radius
int16_t y = 0;
int16_t erro = 1 - xo; //rOuter circle error
int16_t erri = 1 - xi; //rInner circle error
while(xo >= y)
{
xLine(xc + xi, xc + xo, yc + y, drawMode);
yLine(xc + y, yc + xi, yc + xo, drawMode);
xLine(xc - xo, xc - xi, yc + y, drawMode);
yLine(xc - y, yc + xi, yc + xo, drawMode);
xLine(xc - xo, xc - xi, yc - y, drawMode);
yLine(xc - y, yc - xo, yc - xi, drawMode);
xLine(xc + xi, xc + xo, yc - y, drawMode);
yLine(xc + y, yc - xo, yc - xi, drawMode);
y++;
if (erro < 0)
{
erro += 2 * y + 1;
}
else
{
xo--;
erro += 2 * (y - xo + 1);
}
if (y > rInner)
{
xi = y;
}
else
{
if (erri < 0)
{
erri += 2 * y + 1;
}
else
{
xi--;
erri += 2 * (y - xi + 1);
}
}
}
}
void GL_DrawFilledCircle(int16_t x0, int16_t y0, int16_t r, LCD_DRAWMODE_t drawMode)
{
int16_t x = 0;
int16_t y = r;
int16_t d = 3 - 2 * r;
while (y >= x)
{
DrawHLine(x0 - x, y0 + y, x * 2, drawMode);
DrawHLine(x0 - x, y0 - y, x * 2, drawMode);
DrawHLine(x0 - y, y0 + x, y * 2, drawMode);
DrawHLine(x0 - y, y0 - x, y * 2, drawMode);
x++;
if (d > 0)
{
y--;
d = d + 4 * (x - y) + 10;
}
else
{
d = d + 4 * x + 6;
}
}
}
void GL_DrawEllipse(int16_t x0, int16_t y0, int16_t a, int16_t b,
LCD_DRAWMODE_t drawMode)
{
int16_t wx, wy;
int32_t xa, ya;
int32_t t;
int32_t asq = a * a;
int32_t bsq = b * b;
LCD_DrawPixel(x0, y0 + b, drawMode);
LCD_DrawPixel(x0, y0 - b, drawMode);
wx = 0;
wy = b;
xa = 0;
ya = asq * 2 * b;
t = asq / 4 - asq * b;
while (1)
{
t += xa + bsq;
if (t >= 0)
{
ya -= asq * 2;
t -= ya;
wy--;
}
xa += bsq * 2;
wx++;
if (xa >= ya)
{
break;
}
LCD_DrawPixel(x0 + wx, y0 - wy, drawMode);
LCD_DrawPixel(x0 - wx, y0 - wy, drawMode);
LCD_DrawPixel(x0 + wx, y0 + wy, drawMode);
LCD_DrawPixel(x0 - wx, y0 + wy, drawMode);
}
LCD_DrawPixel(x0 + a, y0, drawMode);
LCD_DrawPixel(x0 - a, y0, drawMode);
wx = a;
wy = 0;
xa = bsq * 2 * a;
ya = 0;
t = bsq / 4 - bsq * a;
while (1)
{
t += ya + asq;
if (t >= 0)
{
xa -= bsq * 2;
t = t - xa;
wx--;
}
ya += asq * 2;
wy++;
if (ya > xa)
{
break;
}
LCD_DrawPixel(x0 + wx, y0 - wy, drawMode);
LCD_DrawPixel(x0 - wx, y0 - wy, drawMode);
LCD_DrawPixel(x0 + wx, y0 + wy, drawMode);
LCD_DrawPixel(x0 - wx, y0 + wy, drawMode);
}
}
void GL_DrawFilledEllipse(int16_t x0, int16_t y0, int16_t a, int16_t b,
LCD_DRAWMODE_t drawMode)
{
int16_t wx, wy;
int32_t xa, ya;
int32_t t;
int32_t asq = a * a;
int32_t bsq = b * b;
LCD_DrawPixel(x0, y0 + b, drawMode);
LCD_DrawPixel(x0, y0 - b, drawMode);
wx = 0;
wy = b;
xa = 0;
ya = asq * 2 * b;
t = asq / 4 - asq * b;
while (1)
{
t += xa + bsq;
if (t >= 0)
{
ya -= asq * 2;
t -= ya;
wy--;
}
xa += bsq * 2;
wx++;
if (xa >= ya)
{
break;
}
DrawHLine(x0 - wx, y0 - wy, wx * 2, drawMode);
DrawHLine(x0 - wx, y0 + wy, wx * 2, drawMode);
}
DrawHLine(x0 - a, y0, a * 2, drawMode);
wx = a;
wy = 0;
xa = bsq * 2 * a;
ya = 0;
t = bsq / 4 - bsq * a;
while (1)
{
t += ya + asq;
if (t >= 0)
{
xa -= bsq * 2;
t = t - xa;
wx--;
}
ya += asq * 2;
wy++;
if (ya > xa)
{
break;
}
DrawHLine(x0 - wx, y0 - wy, wx * 2, drawMode);
DrawHLine(x0 - wx, y0 + wy, wx * 2, drawMode);
}
}
void GL_DrawPolygon(int16_t numVertices, int16_t *vertices, int16_t thickness, LCD_DRAWMODE_t drawMode)
{
for (int16_t i = 0; i < numVertices - 1; i++)
{
GL_DrawLine(vertices[(i << 1) + 0], vertices[(i << 1) + 1],
vertices[(i << 1) + 2], vertices[(i << 1) + 3], thickness, drawMode);
}
GL_DrawLine(vertices[0], vertices[1], vertices[(numVertices << 1) - 2],
vertices[(numVertices << 1) - 1], thickness, drawMode);
}
/* Adapted from http://alienryderflex.com/polygon_fill/ */
void GL_DrawFilledPolygon(int16_t numVertices, int16_t *vertices, LCD_DRAWMODE_t drawMode)
{
uint16_t nodes[64];
int16_t y;
float x0;
float y0;
float x1;
float y1;
int16_t miny = LCD_HEIGHT_PIXELS;
int16_t maxy = 0;
for (uint8_t i = 0; i < numVertices; i++)
{
if (miny > vertices[(i << 1) + 1])
{
miny = vertices[(i << 1) + 1];
}
if (maxy < vertices[(i << 1) + 1])
{
maxy = vertices[(i << 1) + 1];
}
}
/* Loop through the rows of the image. */
for (y = miny; y < maxy; y++)
{
/* Build a list of nodes. */
int16_t count = 0;
int16_t j = numVertices - 1;
for (int16_t i = 0; i < numVertices; i++)
{
x0 = vertices[(i << 1) + 0];
y0 = vertices[(i << 1) + 1];
x1 = vertices[(j << 1) + 0];
y1 = vertices[(j << 1) + 1];
if ((y0 < (float) y && y1 >= (float) y)
|| (y1 < (float) y && y0 >= (float) y))
{
nodes[count] =
(int16_t) (x0 + (y - y0) / (y1 - y0) * (x1 - x0));
count++;
}
j = i;
}
/* Sort the nodes, via a simple “Bubble” sort. */
int16_t i = 0;
while (i < count - 1)
{
if (nodes[i] > nodes[i + 1])
{
int16_t swap = nodes[i];
nodes[i] = nodes[i + 1];
nodes[i + 1] = swap;
if (i)
{
i--;
}
}
else
{
i++;
}
}
/* Draw lines between nodes. */
for (int16_t i = 0; i < count; i += 2)
{
int16_t width = nodes[i + 1] - nodes[i];
DrawHLine(nodes[i], y, width, drawMode);
}
}
}
void GL_DrawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, int16_t thickness, LCD_DRAWMODE_t drawMode)
{
int16_t vertices[6] =
{ x0, y0, x1, y1, x2, y2 };
GL_DrawPolygon(3, vertices, thickness, drawMode);
}
void GL_DrawFilledTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, LCD_DRAWMODE_t drawMode)
{
int16_t vertices[6] =
{ x0, y0, x1, y1, x2, y2 };
GL_DrawFilledPolygon(3, vertices, drawMode);
}
void GL_DrawRoundedRectangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t r, LCD_DRAWMODE_t drawMode)
{
uint16_t width, height;
int16_t x, y, d;
/* Make sure x0 is smaller than x1. */
if (x0 > x1)
{
x0 = x0 + x1;
x1 = x0 - x1;
x0 = x0 - x1;
}
/* Make sure y0 is smaller than y1. */
if (y0 > y1)
{
y0 = y0 + y1;
y1 = y0 - y1;
y0 = y0 - y1;
}
/* Max radius is half of shortest edge. */
width = x1 - x0 + 1;
height = y1 - y0 + 1;
r = MIN(r, MIN(width / 2, height / 2));
DrawHLine(x0 + r, y0, width - 2 * r, drawMode);
DrawHLine(x0 + r, y1, width - 2 * r, drawMode);
DrawVLine(x0, y0 + r, height - 2 * r, drawMode);
DrawVLine(x1, y0 + r, height - 2 * r, drawMode);
x = 0;
y = r;
d = 3 - 2 * r;
while (y >= x)
{
x++;
if (d > 0)
{
y--;
d = d + 4 * (x - y) + 10;
}
else
{
d = d + 4 * x + 6;
}
/* Top right */
LCD_DrawPixel(x1 - r + x, y0 + r - y, drawMode);
LCD_DrawPixel(x1 - r + y, y0 + r - x, drawMode);
/* Top left */
LCD_DrawPixel(x0 + r - x, y0 + r - y, drawMode);
LCD_DrawPixel(x0 + r - y, y0 + r - x, drawMode);
/* Bottom right */
LCD_DrawPixel(x1 - r + x, y1 - r + y, drawMode);
LCD_DrawPixel(x1 - r + y, y1 - r + x, drawMode);
/* Bottom left */
LCD_DrawPixel(x0 + r - x, y1 - r + y, drawMode);
LCD_DrawPixel(x0 + r - y, y1 - r + x, drawMode);
}
}
;
void GL_DrawFilledRoundedRectangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t r, LCD_DRAWMODE_t drawMode)
{
uint16_t width, height;
int16_t rx0, ry0, rx1, x, y, d;
/* Make sure x0 is smaller than x1. */
if (x0 > x1)
{
x0 = x0 + x1;
x1 = x0 - x1;
x0 = x0 - x1;
}
/* Make sure y0 is smaller than y1. */
if (y0 > y1)
{
y0 = y0 + y1;
y1 = y0 - y1;
y0 = y0 - y1;
}
/* Max radius is half of shortest edge. */
width = x1 - x0 + 1;
height = y1 - y0 + 1;
r = MIN(r, MIN(width / 2, height / 2));
x = 0;
y = r;
d = 3 - 2 * r;
while (y >= x)
{
x++;
if (d > 0)
{
y--;
d = d + 4 * (x - y) + 10;
}
else
{
d = d + 4 * x + 6;
}
/* Top */
ry0 = y0 + r - x;
rx0 = x0 + r - y;
rx1 = x1 - r + y;
width = rx1 - rx0;
DrawHLine(rx0, ry0, width, drawMode);
ry0 = y0 + r - y;
rx0 = x0 + r - x;
rx1 = x1 - r + x;
width = rx1 - rx0;
DrawHLine(rx0, ry0, width, drawMode);
/* Bottom */
ry0 = y1 - r + y;
rx0 = x0 + r - x;
rx1 = x1 - r + x;
width = rx1 - rx0;
DrawHLine(rx0, ry0, width, drawMode);
ry0 = y1 - r + x;
rx0 = x0 + r - y;
rx1 = x1 - r + y;
width = rx1 - rx0;
DrawHLine(rx0, ry0, width, drawMode);
}
/* Center */
GL_DrawFilledRectangle(x0, y0 + r, x1, y1 - r, drawMode);
}
;
uint16_t GL_GetColorBitmapWidth(const uint16_t *bitmap)
{
return bitmap[BMP_WIDTH_INDEX];
}
uint16_t GL_GetColorBitmapHeight(const uint16_t *bitmap)
{
return bitmap[BMP_HEIGHT_INDEX];
}
/**
* Draw a bitmap in RGB565 color at the specified location
*
* \param *bitmap pointer to bitmap
* \param x0 x coordinate of bitmap (top left)
* \param y0 y coordinate of bitmap (top left)
* \return void
*/
void GL_DrawColorBitmap(const uint16_t *bitmap, uint16_t x0, uint16_t y0)
{
uint16_t xSize = bitmap[BMP_WIDTH_INDEX]; //bitmap width in pixels
uint16_t ySize = bitmap[BMP_HEIGHT_INDEX]; //bitmap height in pixels
bitmap += BMP_DATA_INDEX; //increment the bitmap pointer to the start of bitmap data
for (uint16_t y = 0; y < ySize; y++)
{
for (uint16_t x = 0; x < xSize; x++)
{
LCD_DrawPixel(x + x0, y + y0, bitmap[(y * xSize) + x]);
}
}
}
uint32_t GL_GetMonoBitmapWidth(const uint32_t *bitmap)
{
return bitmap[BMP_WIDTH_INDEX];
}
uint32_t GL_GetMonoBitmapHeight(const uint32_t *bitmap)
{
return bitmap[BMP_HEIGHT_INDEX];
}
/**
* Draw mono bitmap in a single RGB565 color at the specified location
* "1" pixels are drawn, "0" pixels are not drawn
*
* Data packed in uint16_t array, 1bpp, MSb first
*
* \param *bitmap pointer to bitmap
* \param x0 x coordinate of bitmap (top left)
* \param y0 y coordinate of bitmap (top left)
* \return void
*/
void GL_DrawMonoBitmap(const uint32_t *bitmap, uint16_t x0, uint16_t y0, LCD_DRAWMODE_t drawMode)
{
uint32_t xSize = bitmap[BMP_WIDTH_INDEX]; //bitmap width in pixels
uint32_t ySize = bitmap[BMP_HEIGHT_INDEX]; //bitmap height in pixels
const uint32_t *pData = &bitmap[BMP_DATA_INDEX];//data starts @ bitmap[2]
uint32_t startingBitMask = 0x80000000; //32-bit data, MDb first
uint32_t bitMask = startingBitMask;
for (uint16_t y = 0; y < ySize; y++)
{
for (uint16_t x = 0; x < xSize; x++)
{
if (bitMask == 0)
{
bitMask = startingBitMask;
pData++;
}
if (*pData & bitMask)
{
LCD_DrawPixel(x + x0, y + y0, drawMode);
}
bitMask = (bitMask >> 1);
}
}
}
/*
* Draw mono bitmap centered on screen
* \param *bitmap pointer to bitmap
* \param color color to draw bitmap
*/
void GL_DrawMonoBitmapCentered(const uint32_t *bitmap, LCD_DRAWMODE_t drawMode)
{
uint16_t x = LCD_X_MID - GL_GetMonoBitmapWidth(bitmap) / 2;
uint16_t y = LCD_Y_MID - GL_GetMonoBitmapHeight(bitmap) / 2;
GL_DrawMonoBitmap(bitmap, x, y, drawMode);
}