/* * graphicsLibrary.c * * Created on: Feb 12, 2022 * Author: Brian.Bailey */ #include #include #include #include #include #include #include #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); }