- Fixed drawing of round objects (Circles, Rounded Rects) using a lookup table

- Added function to read out the display_buffer via USB-Serial
 - Added basic structure and files for later complete firmware (still in progress)
 - Added Doc folder with schematic in it
 - Added Python script and batch file to read out the display buffer and open the image in gimp
This commit is contained in:
2025-09-07 08:55:39 +02:00
parent 714b5be13c
commit 128d42c586
66 changed files with 29943 additions and 225 deletions

View File

@@ -7,6 +7,7 @@
#include "Display_Shapes.h"
#include "Display_Color.h"
#include "Display_Objects.h"
#include "Round_Corners_Lookup_Table.h"
#include "hardware/dma.h"
@@ -27,8 +28,6 @@
#define DEG2RAD (float)(M_PI / 180)
#define RECT_WIDTH_DMA_THRESHOLD 16
// ============================================================================================
// Variables
@@ -43,6 +42,9 @@ static dma_channel_config _DMA_Config_Drawing;
// ============================================================================================
// Function Declarations
void Display_Shapes_Draw_Rounded_Rect_Frame_1 (int16_t x, int16_t y, uint16_t width, uint16_t height, uint16_t radius, Display_Color color);
void Display_Shapes_Draw_Circle_Frame_1 (int16_t center_x, int16_t center_y, uint16_t radius, Display_Color color);
void Display_Shapes_Draw_Circle_Helper(int16_t x0, int16_t y0, uint16_t radius, uint16_t thickness, uint8_t cornername, Display_Color color);
void Display_Shapes_Draw_Circle_Helper_Improved(int16_t x0, int16_t y0, uint16_t radius, uint16_t thickness, uint8_t cornername, Display_Color color);
void Display_Shapes_Draw_Circle_Helper_Single_Pixel(int16_t x0, int16_t y0, uint16_t radius, uint8_t cornername, Display_Color color);
@@ -234,11 +236,21 @@ void Display_Shapes_Draw_Line_Rad(int16_t x, int16_t y, float angle, uint16_t ra
void Display_Shapes_Draw_Rect_Frame(int16_t x, int16_t y, uint16_t width, uint16_t height, uint16_t thickness, Display_Color color)
{
Display_Shapes_Draw_HLine(x , y , width , thickness, color);
Display_Shapes_Draw_HLine(x , y + height, width + thickness , thickness, color);
// The Rectangle Frame stays within the given height and width. Meaning the Border is drawn inside the width an height.
// For Example:
// If you specify a width = 50 and height = 30 with a thickness = 3, the
// inside area will be (50 - 2*3) x (30 - 2*3) = 44x24 pixels
Display_Shapes_Draw_VLine(x , y , height , thickness, color);
Display_Shapes_Draw_VLine(x + width , y , height + thickness, thickness, color);
int16_t X_Left = x;
int16_t X_Right = x + width - thickness;
int16_t Y_Top = y;
int16_t Y_Bottom = y + height - thickness;
Display_Shapes_Draw_HLine(X_Left, Y_Top , width , thickness, color);
Display_Shapes_Draw_HLine(X_Left, Y_Bottom , width , thickness, color);
Display_Shapes_Draw_VLine(X_Left , Y_Top, height, thickness, color);
Display_Shapes_Draw_VLine(X_Right , Y_Top, height, thickness, color);
}
void Display_Shapes_Draw_Rect_Filled(int16_t x, int16_t y, uint16_t width, uint16_t height, Display_Color color)
@@ -261,143 +273,119 @@ void Display_Shapes_Draw_Rect_Filled(int16_t x, int16_t y, uint16_t width, uint1
return;
}
// For wide rectangles, use DMA for each horizontal row (memory-contiguous)
if (Clipped_Width >= RECT_WIDTH_DMA_THRESHOLD) // DMA threshold for width
// For narrow rectangles, use optimized nested loop
for (int16_t row = Start_Y; row < End_Y; row++)
{
static uint32_t Fill_Value;
Fill_Value = (color << 16) | color; // Pack two 16-bit values
for (int16_t row = Start_Y; row < End_Y; row++) {
// Calculate destination address for this row
Display_Color* Dst = &(*_Current_Buffer)->Dim_2[row][Start_X];
// Use DMA for horizontal row fill (contiguous memory)
dma_channel_configure(_DMA_Channel_Drawing, &_DMA_Config_Drawing, Dst, &Fill_Value, Clipped_Width / 2, false);
dma_channel_start(_DMA_Channel_Drawing);
dma_channel_wait_for_finish_blocking(_DMA_Channel_Drawing);
// Handle odd width
if (Clipped_Width & 1) {
(*_Current_Buffer)->Dim_2[row][End_X - 1] = color;
}
}
}
else
{
// For narrow rectangles, use optimized nested loop
for (int16_t row = Start_Y; row < End_Y; row++)
{
// Each row is contiguous in memory - cache friendly
Display_Color* Row_Ptr = &(*_Current_Buffer)->Dim_2[row][Start_X];
// Each row is contiguous in memory - cache friendly
Display_Color* Row_Ptr = &(*_Current_Buffer)->Dim_2[row][Start_X];
for (int16_t col = 0; col < Clipped_Width; col++)
{
Row_Ptr[col] = color;
}
}
}
for (int16_t col = 0; col < Clipped_Width; col++)
{
Row_Ptr[col] = color;
}
}
}
void Display_Shapes_Draw_Circle_Frame(int16_t center_x, int16_t center_y, uint16_t radius, uint16_t thickness, Display_Color color)
{
if(thickness == 0) {
return;
}
if (thickness == 0 || radius == 0) {
return;
}
int16_t f = 1 - radius;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * radius;
int16_t X = 0;
int16_t Y = radius;
if(thickness == 1) {
Display_Shapes_Draw_Circle_Frame_1(center_x, center_y, radius, color);
return;
}
uint16_t Inner_Radius = radius - thickness;
if(thickness == 1)
{
Display_Shapes_Draw_Pixel_Safe(center_x , center_y+radius , color);
Display_Shapes_Draw_Pixel_Safe(center_x , center_y-radius , color);
Display_Shapes_Draw_Pixel_Safe(center_x+radius , center_y , color);
Display_Shapes_Draw_Pixel_Safe(center_x-radius , center_y , color);
}
else
{
Display_Shapes_Draw_Circle_Filled(center_x , center_y+radius , thickness >> 1, color);
Display_Shapes_Draw_Circle_Filled(center_x , center_y-radius , thickness >> 1, color);
Display_Shapes_Draw_Circle_Filled(center_x+radius , center_y , thickness >> 1, color);
Display_Shapes_Draw_Circle_Filled(center_x-radius , center_y , thickness >> 1, color);
}
while (X<Y)
{
if (f >= 0)
{
Y--;
ddF_y += 2;
f += ddF_y;
}
X++;
ddF_x += 2;
f += ddF_x;
Display_Shapes_Draw_HLine(center_x - radius , center_y - 1, thickness, 2, color);
Display_Shapes_Draw_HLine(center_x + radius - thickness , center_y - 1, thickness, 2, color);
if(thickness == 1)
{
Display_Shapes_Draw_Pixel_Safe(center_x + X, center_y + Y, color);
Display_Shapes_Draw_Pixel_Safe(center_x - X, center_y + Y, color);
Display_Shapes_Draw_Pixel_Safe(center_x + X, center_y - Y, color);
Display_Shapes_Draw_Pixel_Safe(center_x - X, center_y - Y, color);
Display_Shapes_Draw_Pixel_Safe(center_x + Y, center_y + X, color);
Display_Shapes_Draw_Pixel_Safe(center_x - Y, center_y + X, color);
Display_Shapes_Draw_Pixel_Safe(center_x + Y, center_y - X, color);
Display_Shapes_Draw_Pixel_Safe(center_x - Y, center_y - X, color);
}
else
{
Display_Shapes_Draw_Circle_Filled(center_x + X, center_y + Y, thickness >> 1, color);
Display_Shapes_Draw_Circle_Filled(center_x - X, center_y + Y, thickness >> 1, color);
Display_Shapes_Draw_Circle_Filled(center_x + X, center_y - Y, thickness >> 1, color);
Display_Shapes_Draw_Circle_Filled(center_x - X, center_y - Y, thickness >> 1, color);
Display_Shapes_Draw_Circle_Filled(center_x + Y, center_y + X, thickness >> 1, color);
Display_Shapes_Draw_Circle_Filled(center_x - Y, center_y + X, thickness >> 1, color);
Display_Shapes_Draw_Circle_Filled(center_x + Y, center_y - X, thickness >> 1, color);
Display_Shapes_Draw_Circle_Filled(center_x - Y, center_y - X, thickness >> 1, color);
}
}
Display_Shapes_Draw_VLine(center_x - 1, center_y - radius , thickness, 2, color);
Display_Shapes_Draw_VLine(center_x - 1, center_y + radius - thickness , thickness, 2, color);
const uint8_t* Outer_Data = _Corner_Lookup_Tables[radius].Data;
uint8_t Outer_Size = _Corner_Lookup_Tables[radius].Size;
const uint8_t* Inner_Data = _Corner_Lookup_Tables[Inner_Radius].Data;
uint8_t Inner_Size = _Corner_Lookup_Tables[Inner_Radius].Size;
// Draw frame using lookup table data
for (uint8_t i = 0; i < Outer_Size; i++)
{
int16_t Outer_X_Offset = Outer_Data[i];
int16_t Y_Offset = i;
uint16_t Line_Width = 0;
if (i < thickness) {
// Full outer circle width for top/bottom thickness rows
Line_Width = radius - Outer_X_Offset;
} else {
// Frame width = outer - inner
uint8_t Inner_Index = i - thickness;
if (Inner_Index < Inner_Size) {
int16_t Inner_X_Offset = Inner_Data[Inner_Index];
Line_Width = (radius - Outer_X_Offset) - (Inner_Radius - Inner_X_Offset);
}
}
if (Line_Width > 0) {
int16_t Top_Y = center_y - radius + Y_Offset - 1;
int16_t Bottom_Y = center_y + radius - Y_Offset;
// Left side of frame
int16_t Left_X = center_x - (radius - Outer_X_Offset) - 1;
Display_Shapes_Draw_HLine(Left_X, Top_Y, Line_Width, 1, color);
if (Y_Offset > 0) {
Display_Shapes_Draw_HLine(Left_X, Bottom_Y, Line_Width, 1, color);
}
// Right side of frame
int16_t Right_X = center_x + (radius - Outer_X_Offset) - Line_Width + 1;
Display_Shapes_Draw_HLine(Right_X, Top_Y, Line_Width, 1, color);
if (Y_Offset > 0) {
Display_Shapes_Draw_HLine(Right_X, Bottom_Y, Line_Width, 1, color);
}
}
}
}
void Display_Shapes_Draw_Circle_Filled(int16_t center_x, int16_t center_y, uint16_t radius, Display_Color color)
{
Display_Shapes_Draw_VLine(center_x, center_y-radius, 2*radius+1, 1, color);
if (radius == 0) {
Display_Shapes_Draw_Pixel_Safe(center_x, center_y, color);
return;
}
Display_Shapes_Draw_HLine(center_x - radius, center_y - 1, 2*radius, 2, color);
uint8_t Corner_Name = 3;
int16_t Delta = 0;
int16_t f = 1 - radius;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * radius;
int16_t X = 0;
int16_t Y = radius;
while(X<Y)
{
if(f >= 0)
{
Y--;
ddF_y += 2;
f += ddF_y;
}
X++;
ddF_x += 2;
f += ddF_x;
if ((Corner_Name & 0x1) > 0)
{
Display_Shapes_Draw_VLine(center_x+X, center_y-Y, 2*Y+1+Delta, 1, color);
Display_Shapes_Draw_VLine(center_x+Y, center_y-X, 2*X+1+Delta, 1, color);
}
if ((Corner_Name & 0x2)>0)
{
Display_Shapes_Draw_VLine(center_x-X, center_y-Y, 2*Y+1+Delta, 1, color);
Display_Shapes_Draw_VLine(center_x-Y, center_y-X, 2*X+1+Delta, 1, color);
}
}
const uint8_t* Data = _Corner_Lookup_Tables[radius].Data;
uint8_t Size = _Corner_Lookup_Tables[radius].Size;
// Draw horizontal lines using lookup table data
for (uint8_t i = 0; i < Size; i++)
{
int16_t X_Offset = Data[i];
int16_t Y_Offset = i;
// Calculate line width for this Y position
uint16_t Line_Width = 2 * (radius - X_Offset) + 2;
// Draw upper and lower horizontal lines
int16_t Top_Y = center_y - radius + Y_Offset - 0;
int16_t Bottom_Y = center_y + radius - Y_Offset;
int16_t Start_X = center_x - (radius - X_Offset) - 1;
Display_Shapes_Draw_HLine(Start_X, Top_Y, Line_Width, 1, color);
if (Y_Offset > 0) { // Avoid drawing center line twice
Display_Shapes_Draw_HLine(Start_X, Bottom_Y, Line_Width, 1, color);
}
}
}
void Display_Shapes_Draw_Round_Rect_Frame(int16_t x, int16_t y, uint16_t width, uint16_t height, uint16_t radius, uint16_t thickness, Display_Color color)
@@ -407,61 +395,110 @@ void Display_Shapes_Draw_Round_Rect_Frame(int16_t x, int16_t y, uint16_t width,
return;
}
if(thickness == 1) {
Display_Shapes_Draw_Rounded_Rect_Frame_1(x, y, width, height, radius, color);
return;
}
// Clamp radius to maximum possible value
uint16_t max_radius = ((width < height) ? width : height) / 2;
if (radius > max_radius) {
radius = max_radius;
uint16_t Max_Radius = ((width < height) ? width : height) / 2;
if (radius > Max_Radius) {
radius = Max_Radius;
}
// For very small rectangles, just draw a filled rectangle
if (radius == 0 || width <= 2*radius || height <= 2*radius) {
// For no radius, draw a regular Rectangle Frame
if (radius == 0) {
Display_Shapes_Draw_Rect_Frame(x, y, width, height, thickness, color);
return;
}
// Draw the straight edges (avoiding corners)
// Top edge
if (width > 2 * radius) {
Display_Shapes_Draw_HLine(x + radius, y, width - 2 * radius, thickness, color);
}
// Calculate inner rectangle dimensions
int16_t Inner_Width = width - 2 * thickness;
int16_t Inner_Height = height - 2 * thickness;
// Bottom edge
if (width > 2 * radius) {
Display_Shapes_Draw_HLine(x + radius, y + height - thickness, width - 2 * radius, thickness, color);
}
// Calculate inner radius (ensuring it's valid)
uint16_t Inner_Radius = (radius > thickness) ? radius - thickness : 0;
uint16_t Outer_Radius = radius;
const uint8_t* Outer_Data = _Corner_Lookup_Tables[Outer_Radius].Data;
uint8_t Outer_Size = _Corner_Lookup_Tables[Outer_Radius].Size;
const uint8_t* Inner_Data = _Corner_Lookup_Tables[Inner_Radius].Data;
uint8_t Inner_Size = _Corner_Lookup_Tables[Inner_Radius].Size;
// Draw straight edges
Display_Shapes_Draw_HLine(x + radius , y , width - 2 * radius, thickness, color);
Display_Shapes_Draw_HLine(x + radius , y + height - thickness, width - 2 * radius, thickness, color);
Display_Shapes_Draw_VLine(x , y + radius , height - 2 * radius, thickness, color);
Display_Shapes_Draw_VLine(x + width - thickness , y + radius , height - 2 * radius, thickness, color);
// Left edge
if (height > 2 * radius) {
Display_Shapes_Draw_VLine(x, y + radius, height - 2 * radius, thickness, color);
// Draw corner regions
for (uint8_t i = 0; i < Outer_Size; i++)
{
int16_t Outer_X_Offset = Outer_Data[i];
// Calculate corner positions for outer edge
int16_t TL_Outer_X = x + Outer_X_Offset;
int16_t TR_Outer_X = x + width - 1 - Outer_X_Offset;
int16_t BL_Outer_X = x + Outer_X_Offset;
int16_t BR_Outer_X = x + width - 1 - Outer_X_Offset;
int16_t Top_Y = y + i;
int16_t Bottom_Y = y + height - 1 - i;
uint16_t Line_Width = 0;
if(i < thickness) {
Line_Width = Outer_Radius - Outer_X_Offset;
} else {
uint8_t Inner_Radius_Index = i - thickness;
int16_t Inner_X_Offset = Inner_Data[Inner_Radius_Index];
Line_Width = (Outer_Radius - Outer_X_Offset) - (Inner_Radius - Inner_X_Offset);
}
Display_Shapes_Draw_HLine(TL_Outer_X, Top_Y , Line_Width, 1, color);
Display_Shapes_Draw_HLine(BL_Outer_X, Bottom_Y , Line_Width, 1, color);
Display_Shapes_Draw_HLine(TR_Outer_X - Line_Width + 1, Top_Y , Line_Width, 1, color);
Display_Shapes_Draw_HLine(BR_Outer_X - Line_Width + 1, Bottom_Y , Line_Width, 1, color);
}
// Right edge
if (height > 2 * radius) {
Display_Shapes_Draw_VLine(x + width - thickness, y + radius, height - 2 * radius, thickness, color);
}
// Draw the four corner arcs with corrected positioning
Display_Shapes_Draw_Circle_Helper_Improved(x + radius, y + radius, radius, thickness, CORNER_TOP_LEFT, color); // Top-left corner
Display_Shapes_Draw_Circle_Helper_Improved(x + width - radius - 1, y + radius, radius, thickness, CORNER_TOP_RIGHT, color); // Top-right corner
Display_Shapes_Draw_Circle_Helper_Improved(x + width - radius - 1, y + height - radius - 1, radius, thickness, CORNER_BOTTOM_RIGHT, color); // Bottom-right corner
Display_Shapes_Draw_Circle_Helper_Improved(x + radius, y + height - radius - 1, radius, thickness, CORNER_BOTTOM_LEFT, color); // Bottom-left corner
}
void Display_Shapes_Draw_Round_Rect_Filled(int16_t x, int16_t y, uint16_t width, uint16_t height, uint16_t radius, Display_Color color)
{
uint16_t Max_Radius = ((width < height) ? width : height) / 2; // 1/2 minor axis
uint16_t Max_Radius = ((width < height) ? width : height) / 2;
if (radius > Max_Radius)
{
radius = Max_Radius;
}
if (radius > Max_Radius) {
radius = Max_Radius;
}
// Smarter Version
Display_Shapes_Draw_Rect_Filled(x + radius, y, width - 2 * radius, height, color);
// Draw the main body rectangle (excluding corner regions)
if (height > 2 * radius) {
Display_Shapes_Draw_Rect_Filled(x, y + radius, width, height - 2 * radius, color);
}
// Draw Four Corners
Display_Shapes_Draw_Circle_Helper_Filled(x + width - radius - 1 , y + radius, radius, 1, height - 2 * radius - 1, color);
Display_Shapes_Draw_Circle_Helper_Filled(x + radius , y + radius, radius, 2, height - 2 * radius - 1, color);
const uint8_t* Data = _Corner_Lookup_Tables[radius].Data;
uint8_t Size = _Corner_Lookup_Tables[radius].Size;
// Draw corner regions using lookup table
for (uint8_t i = 0; i < Size; i++)
{
int16_t X_Offset = Data[i];
// Calculate Y positions for top and bottom
int16_t Top_Y = y + i;
int16_t Bottom_Y = y + height - 1 - i;
// Calculate line width for this Y position
uint16_t Line_Width = width - 2 * X_Offset;
// Draw Top Corners
Display_Shapes_Draw_HLine(x + X_Offset, Top_Y, Line_Width, 1, color);
// Draw Bottom Corners
Display_Shapes_Draw_HLine(x + X_Offset, Bottom_Y, Line_Width, 1, color);
}
}
void Display_Shapes_Draw_Arc_Frame(int16_t center_x, int16_t center_y, int16_t radius, uint16_t thickness, float angle_start, float angle_end, uint16_t steps, Display_Color color)
@@ -574,6 +611,119 @@ Coordinates Display_Shapes_Polar_To_XY(int16_t origin_x, int16_t origin_y, float
/*******************************************************************
Internal Functions
*******************************************************************/
void Display_Shapes_Draw_Rounded_Rect_Frame_1(int16_t x, int16_t y, uint16_t width, uint16_t height, uint16_t radius, Display_Color color)
{
// Validate input parameters
if (width < 2 || height < 2) {
return;
}
// Clamp radius to maximum possible value
uint16_t Max_Radius = ((width < height) ? width : height) / 2;
if (radius > Max_Radius) {
radius = Max_Radius;
}
// For no radius, draw a regular Rectangle Frame
if (radius == 0) {
Display_Shapes_Draw_Rect_Frame(x, y, width, height, 1, color);
return;
}
// Draw straight edges
Display_Shapes_Draw_HLine(x + radius , y , width - 2 * radius, 1, color);
Display_Shapes_Draw_HLine(x + radius , y + height - 1, width - 2 * radius, 1, color);
Display_Shapes_Draw_VLine(x , y + radius , height - 2 * radius, 1, color);
Display_Shapes_Draw_VLine(x + width - 1 , y + radius , height - 2 * radius, 1, color);
const uint8_t* Data = _Corner_Lookup_Tables[radius].Data;
uint8_t Size = _Corner_Lookup_Tables[radius].Size;
int16_t Last_X = Data[0];
// Draw corners with gap filling
for (uint8_t i = 0; i <Size; i++)
{
int16_t Current_X = Data[i];
int16_t X_Step = Last_X - Current_X;
// Calculate positions for all four corners
int16_t TL_X = x + Current_X;
int16_t TL_Y = y + i;
int16_t TR_X = x + width - 1 - Current_X;
int16_t TR_Y = y + i;
int16_t BL_X = x + Current_X;
int16_t BL_Y = y + height - 1 - i;
int16_t BR_X = x + width - 1 - Current_X;
int16_t BR_Y = y + height - 1 - i;
if(X_Step > 1) {
Display_Shapes_Draw_HLine(TL_X, TL_Y, X_Step, 1, color);
Display_Shapes_Draw_HLine(BL_X, BL_Y, X_Step, 1, color);
Display_Shapes_Draw_HLine(TR_X - X_Step + 1, TR_Y, X_Step, 1, color);
Display_Shapes_Draw_HLine(BR_X - X_Step + 1, BR_Y, X_Step, 1, color);
}
else {
Display_Shapes_Draw_Pixel_Safe(TL_X, TL_Y, color);
Display_Shapes_Draw_Pixel_Safe(BL_X, BL_Y, color);
Display_Shapes_Draw_Pixel_Safe(TR_X, TR_Y, color);
Display_Shapes_Draw_Pixel_Safe(BR_X, BR_Y, color);
}
Last_X = Data[i];
}
}
void Display_Shapes_Draw_Circle_Frame_1(int16_t center_x, int16_t center_y, uint16_t radius, Display_Color color)
{
if(radius == 0) {
return;
}
const uint8_t* Data = _Corner_Lookup_Tables[radius].Data;
uint8_t Size = _Corner_Lookup_Tables[radius].Size;
int16_t Last_X = Data[0];
// Draw corners with gap filling
for (uint8_t i = 0; i <Size; i++)
{
int16_t Current_X = Data[i];
int16_t X_Step = Last_X - Current_X;
// Calculate positions for all four corners
int16_t TL_X = center_x - radius + Current_X - 1;
int16_t TL_Y = center_y - radius + i;
int16_t TR_X = center_x + radius - Current_X;
int16_t TR_Y = TL_Y;
int16_t BL_X = TL_X;
int16_t BL_Y = center_y + radius - i - 1;
int16_t BR_X = TR_X;
int16_t BR_Y = BL_Y;
if(X_Step > 1) {
Display_Shapes_Draw_HLine(TL_X, TL_Y, X_Step, 1, color);
Display_Shapes_Draw_HLine(BL_X, BL_Y, X_Step, 1, color);
Display_Shapes_Draw_HLine(TR_X - X_Step + 1, TR_Y, X_Step, 1, color);
Display_Shapes_Draw_HLine(BR_X - X_Step + 1, BR_Y, X_Step, 1, color);
}
else {
Display_Shapes_Draw_Pixel_Safe(TL_X, TL_Y, color);
Display_Shapes_Draw_Pixel_Safe(BL_X, BL_Y, color);
Display_Shapes_Draw_Pixel_Safe(TR_X, TR_Y, color);
Display_Shapes_Draw_Pixel_Safe(BR_X, BR_Y, color);
}
Last_X = Data[i];
}
}
/**************************************************************************/
/*!
@brief Quarter-circle drawer, used to do circles and roundrects