update and cleanup gui code

This commit is contained in:
Shuanglei Tao
2025-12-31 18:16:33 +08:00
parent 5685e155d8
commit 07e43372da
4 changed files with 205 additions and 53 deletions

View File

@@ -467,7 +467,20 @@ void GFX_drawDottedLine(Adafruit_GFX* gfx, int16_t x0, int16_t y0, int16_t x1, i
*/
/**************************************************************************/
void GFX_drawFastVLine(Adafruit_GFX* gfx, int16_t x, int16_t y, int16_t h, uint16_t color) {
GFX_drawLine(gfx, x, y, x, y + h - 1, color);
if (h <= 0) return;
// Boundary check
if (x < 0 || x >= gfx->_width) return;
if (y < 0) {
h += y;
y = 0;
}
if (y + h > gfx->_height) h = gfx->_height - y;
if (h <= 0) return;
for (int16_t i = 0; i < h; i++) {
GFX_drawPixel(gfx, x, y + i, color);
}
}
/**************************************************************************/
@@ -480,7 +493,20 @@ void GFX_drawFastVLine(Adafruit_GFX* gfx, int16_t x, int16_t y, int16_t h, uint1
*/
/**************************************************************************/
void GFX_drawFastHLine(Adafruit_GFX* gfx, int16_t x, int16_t y, int16_t w, uint16_t color) {
GFX_drawLine(gfx, x, y, x + w - 1, y, color);
if (w <= 0) return;
// Boundary check
if (y < 0 || y >= gfx->_height) return;
if (x < 0) {
w += x;
x = 0;
}
if (x + w > gfx->_width) w = gfx->_width - x;
if (w <= 0) return;
for (int16_t i = 0; i < w; i++) {
GFX_drawPixel(gfx, x + i, y, color);
}
}
/**************************************************************************/
@@ -564,8 +590,8 @@ void GFX_drawCircle(Adafruit_GFX* gfx, int16_t x0, int16_t y0, int16_t r, uint16
@param x0 Center-point x coordinate
@param y0 Center-point y coordinate
@param r Radius of circle
@param cornername Mask bit #1 or bit #2 to indicate which quarters of
the circle we're doing
@param cornername Mask bit #1, #2, #4, and #8 to indicate which quarters
of the circle we're doing
@param color 16-bit 5-6-5 Color to draw with
*/
/**************************************************************************/
@@ -620,11 +646,12 @@ void GFX_fillCircle(Adafruit_GFX* gfx, int16_t x0, int16_t y0, int16_t r, uint16
/**************************************************************************/
/*!
@brief Quarter-circle drawer with fill, used for circles and roundrects
@brief Half-circle drawer with fill, used for circles and roundrects
@param x0 Center-point x coordinate
@param y0 Center-point y coordinate
@param r Radius of circle
@param corners Mask bits indicating which quarters we're doing
@param corners Mask bits indicating which sides of the circle we are
doing, left (1) and/or right (2)
@param delta Offset from center-point, used for round-rects
@param color 16-bit 5-6-5 Color to fill with
*/
@@ -665,6 +692,102 @@ void GFX_fillCircleHelper(Adafruit_GFX* gfx, int16_t x0, int16_t y0, int16_t r,
}
}
/**************************************************************************/
/*!
@brief Draw an ellipse outline
@param x0 Center-point x coordinate
@param y0 Center-point y coordinate
@param rw Horizontal radius of ellipse
@param rh Vertical radius of ellipse
@param color 16-bit 5-6-5 Color to draw with
*/
/**************************************************************************/
void GFX_drawEllipse(Adafruit_GFX* gfx, int16_t x0, int16_t y0, int16_t rw, int16_t rh, uint16_t color) {
// Bresenham's ellipse algorithm
int16_t x = 0, y = rh;
int32_t rw2 = rw * rw, rh2 = rh * rh;
int32_t twoRw2 = 2 * rw2, twoRh2 = 2 * rh2;
int32_t decision = rh2 - (rw2 * rh) + (rw2 / 4);
// region 1
while ((twoRh2 * x) < (twoRw2 * y)) {
GFX_drawPixel(gfx, x0 + x, y0 + y, color);
GFX_drawPixel(gfx, x0 - x, y0 + y, color);
GFX_drawPixel(gfx, x0 + x, y0 - y, color);
GFX_drawPixel(gfx, x0 - x, y0 - y, color);
x++;
if (decision < 0) {
decision += rh2 + (twoRh2 * x);
} else {
decision += rh2 + (twoRh2 * x) - (twoRw2 * y);
y--;
}
}
// region 2
decision = ((rh2 * (2 * x + 1) * (2 * x + 1)) >> 2) + (rw2 * (y - 1) * (y - 1)) - (rw2 * rh2);
while (y >= 0) {
GFX_drawPixel(gfx, x0 + x, y0 + y, color);
GFX_drawPixel(gfx, x0 - x, y0 + y, color);
GFX_drawPixel(gfx, x0 + x, y0 - y, color);
GFX_drawPixel(gfx, x0 - x, y0 - y, color);
y--;
if (decision > 0) {
decision += rw2 - (twoRw2 * y);
} else {
decision += rw2 + (twoRh2 * x) - (twoRw2 * y);
x++;
}
}
}
/**************************************************************************/
/*!
@brief Draw an ellipse with filled colour
@param x0 Center-point x coordinate
@param y0 Center-point y coordinate
@param rw Horizontal radius of ellipse
@param rh Vertical radius of ellipse
@param color 16-bit 5-6-5 Color to draw with
*/
/**************************************************************************/
void GFX_fillEllipse(Adafruit_GFX* gfx, int16_t x0, int16_t y0, int16_t rw, int16_t rh, uint16_t color) {
// Bresenham's ellipse algorithm
int16_t x = 0, y = rh;
int32_t rw2 = rw * rw, rh2 = rh * rh;
int32_t twoRw2 = 2 * rw2, twoRh2 = 2 * rh2;
int32_t decision = rh2 - (rw2 * rh) + (rw2 / 4);
// region 1
while ((twoRh2 * x) < (twoRw2 * y)) {
x++;
if (decision < 0) {
decision += rh2 + (twoRh2 * x);
} else {
decision += rh2 + (twoRh2 * x) - (twoRw2 * y);
GFX_drawFastHLine(gfx, x0 - (x - 1), y0 + y, 2 * (x - 1) + 1, color);
GFX_drawFastHLine(gfx, x0 - (x - 1), y0 - y, 2 * (x - 1) + 1, color);
y--;
}
}
// region 2
decision = ((rh2 * (2 * x + 1) * (2 * x + 1)) >> 2) + (rw2 * (y - 1) * (y - 1)) - (rw2 * rh2);
while (y >= 0) {
GFX_drawFastHLine(gfx, x0 - x, y0 + y, 2 * x + 1, color);
GFX_drawFastHLine(gfx, x0 - x, y0 - y, 2 * x + 1, color);
y--;
if (decision > 0) {
decision += rw2 - (twoRw2 * y);
} else {
decision += rw2 + (twoRh2 * x) - (twoRw2 * y);
x++;
}
}
}
/**************************************************************************/
/*!
@brief Draw a rectangle with no fill color
@@ -1033,31 +1156,6 @@ int16_t GFX_getUTF8Width(Adafruit_GFX* gfx, const char* str) {
return w;
}
int16_t GFX_getUTF8Widthf(Adafruit_GFX* gfx, const char* format, ...) {
char buf[64] = {0};
char* str = buf;
size_t len;
va_list va;
va_start(va, format);
len = vsnprintf(buf, sizeof(buf), format, va);
va_end(va);
if (len > sizeof(buf) - 1) {
str = malloc(len + 1);
if (str == NULL) return 0;
va_start(va, format);
vsnprintf(str, len + 1, format, va);
va_end(va);
}
int16_t w = GFX_getUTF8Width(gfx, str);
if (str != buf) free(str);
return w;
}
size_t GFX_print(Adafruit_GFX* gfx, const char c) {
int16_t delta;
uint16_t e = utf8_next(gfx, (uint8_t)c);

View File

@@ -72,6 +72,8 @@ void GFX_drawCircleHelper(Adafruit_GFX* gfx, int16_t x0, int16_t y0, int16_t r,
void GFX_fillCircle(Adafruit_GFX* gfx, int16_t x0, int16_t y0, int16_t r, uint16_t color);
void GFX_fillCircleHelper(Adafruit_GFX* gfx, int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta,
uint16_t color);
void GFX_drawEllipse(Adafruit_GFX* gfx, int16_t x0, int16_t y0, int16_t rw, int16_t rh, uint16_t color);
void GFX_fillEllipse(Adafruit_GFX* gfx, int16_t x0, int16_t y0, int16_t rw, int16_t rh, uint16_t color);
void GFX_drawTriangle(Adafruit_GFX* gfx, int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2,
uint16_t color);
void GFX_fillTriangle(Adafruit_GFX* gfx, int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2,
@@ -94,7 +96,6 @@ int16_t GFX_drawGlyph(Adafruit_GFX* gfx, int16_t x, int16_t y, uint16_t e);
int16_t GFX_drawStr(Adafruit_GFX* gfx, int16_t x, int16_t y, const char* s);
int16_t GFX_drawUTF8(Adafruit_GFX* gfx, int16_t x, int16_t y, const char* str);
int16_t GFX_getUTF8Width(Adafruit_GFX* gfx, const char* str);
int16_t GFX_getUTF8Widthf(Adafruit_GFX* gfx, const char* format, ...);
size_t GFX_print(Adafruit_GFX* gfx, const char c);
size_t GFX_write(Adafruit_GFX* gfx, const char* buffer, size_t size);
size_t GFX_printf(Adafruit_GFX* gfx, const char* format, ...);

View File

@@ -12,6 +12,9 @@
GFX_setFont(gfx, font); \
GFX_printf(gfx, __VA_ARGS__);
// height to use larger layout
#define large_layout(data) ((data)->height >= 400)
typedef struct {
uint8_t month;
uint8_t day;
@@ -194,15 +197,15 @@ static void DrawDateHeader(Adafruit_GFX* gfx, int16_t x, int16_t y, tm_t* tm, st
GFX_printf(gfx, " [%s]", Lunar_ZodiacString[LUNAR_GetZodiac(Lunar)]);
GFX_setTextColor(gfx, GFX_BLACK, GFX_WHITE);
DrawBattery(gfx, data->width - 10 - 2, data->height > 300 ? 16 : 6, 20, data->voltage);
DrawBattery(gfx, data->width - 10 - 2, large_layout(data) ? 16 : 6, 20, data->voltage);
GFX_setCursor(gfx, data->width - GFX_getUTF8Width(gfx, data->ssid) - 10, y);
GFX_printf(gfx, "%s", data->ssid);
}
static void DrawWeekHeader(Adafruit_GFX* gfx, int16_t x, int16_t y, gui_data_t* data) {
GFX_setFont(gfx, data->height > 300 ? u8g2_font_wqy12_t_lunar : u8g2_font_wqy9_t_lunar);
GFX_setFont(gfx, large_layout(data) ? u8g2_font_wqy12_t_lunar : u8g2_font_wqy9_t_lunar);
uint8_t w = (data->width - 2 * x) / 7;
uint8_t h = data->height > 300 ? 32 : 24;
uint8_t h = large_layout(data) ? 32 : 24;
uint8_t r = (data->width - 2 * x) % 7;
uint8_t fh = (h - GFX_getFontHeight(gfx)) / 2 + GFX_getFontAscent(gfx) + 1;
int16_t cw = GFX_getUTF8Width(gfx, Lunar_DayString[0]);
@@ -225,7 +228,7 @@ static void DrawMonthDays(Adafruit_GFX* gfx, int16_t x, int16_t y, tm_t* tm, str
int16_t bw = (data->width - x - 10) / 7;
int16_t bh = (data->height - y - 10) / monthDayRows;
bool large = data->height > 300;
bool large = large_layout(data);
if (large) {
for (uint8_t i = 1; i < monthDayRows; i++)
@@ -245,7 +248,8 @@ static void DrawMonthDays(Adafruit_GFX* gfx, int16_t x, int16_t y, tm_t* tm, str
LUNAR_SolarToLunar(Lunar, year, month, day);
int16_t cr = large ? 13 : 10;
int16_t cr = large ? 15 : 11;
if (monthDayRows > 5) cr -= 1; // reduce circle height for 6 week rows
int16_t bx = x + (bw - 2 * cr) / 2 + displayWeek * bw;
int16_t by = y + (bh - 2 * cr) / 2 + (i + adjustedFirstDay) / 7 * bh + 3;
@@ -256,24 +260,26 @@ static void DrawMonthDays(Adafruit_GFX* gfx, int16_t x, int16_t y, tm_t* tm, str
GFX_setTextColor(gfx, weekend ? GFX_RED : GFX_BLACK, GFX_WHITE);
}
char buf[10] = {0};
snprintf(buf, sizeof(buf), "%d", day);
GFX_setFont(gfx, large ? u8g2_font_helvB18_tn : u8g2_font_helvB14_tn);
GFX_setCursor(gfx, bx + (2 * cr - GFX_getUTF8Widthf(gfx, "%d", day)) / 2, by - (cr - GFX_getFontHeight(gfx)));
GFX_printf(gfx, "%d", day);
GFX_setCursor(gfx, bx + (2 * cr - GFX_getUTF8Width(gfx, buf)) / 2, by - (cr - GFX_getFontHeight(gfx)) - 1);
GFX_printf(gfx, "%s", buf);
char festival[10] = {0};
GFX_setFont(gfx, large ? u8g2_font_wqy12_t_lunar : u8g2_font_wqy9_t_lunar);
GFX_setFontMode(gfx, 1); // transparent
if (GetFestival(year, month, day, actualWeek, Lunar, festival)) {
if (GetFestival(year, month, day, actualWeek, Lunar, buf)) {
if (day != tm->tm_mday) GFX_setTextColor(gfx, GFX_RED, GFX_WHITE);
} else {
if (Lunar->Date == 1)
snprintf(festival, sizeof(festival), "%s%s", Lunar_MonthLeapString[Lunar->IsLeap],
snprintf(buf, sizeof(buf), "%s%s", Lunar_MonthLeapString[Lunar->IsLeap],
Lunar_MonthString[Lunar->Month]);
else
snprintf(festival, sizeof(festival), "%s", Lunar_DateString[Lunar->Date]);
snprintf(buf, sizeof(buf), "%s", Lunar_DateString[Lunar->Date]);
}
GFX_setCursor(gfx, bx + (2 * cr - GFX_getUTF8Width(gfx, festival)) / 2, gfx->ty + GFX_getFontHeight(gfx) + 3);
GFX_printf(gfx, "%s", festival);
GFX_setCursor(gfx, bx + (2 * cr - GFX_getUTF8Width(gfx, buf)) / 2 + 1,
gfx->ty + GFX_getFontHeight(gfx) + (large ? 5 : 3));
GFX_printf(gfx, "%s", buf);
bool work = false;
if (year == HOLIDAY_YEAR && GetHoliday(month, day, &work)) {
@@ -293,7 +299,7 @@ static void DrawMonthDays(Adafruit_GFX* gfx, int16_t x, int16_t y, tm_t* tm, str
}
static void DrawCalendar(Adafruit_GFX* gfx, tm_t* tm, struct Lunar_Date* Lunar, gui_data_t* data) {
bool large = data->height > 300;
bool large = large_layout(data);
DrawDateHeader(gfx, 10, large ? 38 : 28, tm, Lunar, data);
DrawWeekHeader(gfx, 10, large ? 44 : 32, data);
DrawMonthDays(gfx, 10, large ? 84 : 64, tm, Lunar, data);
@@ -316,10 +322,10 @@ static void DrawCalendar(Adafruit_GFX* gfx, tm_t* tm, struct Lunar_Date* Lunar,
https://forum.arduino.cc/t/fast-7-segment-number-display-for-tft/296619/4
*/
static void Draw7Number(Adafruit_GFX *gfx, int n, unsigned int xLoc, unsigned int yLoc, char cS, unsigned int fC, unsigned int bC, int nD) {
unsigned int num=abs(n),i,t,w,col,h,a,b,j=1,d=0,S2=5*cS,S3=2*cS,S4=7*cS,x1=cS+1,x2=S3+S2+1,y1=yLoc+x1,y3=yLoc+S3+S4+1;
unsigned int seg[7][3]={{x1,yLoc,1},{x2,y1,0},{x2,y3+x1,0},{x1,(2*y3)-yLoc,1},{0,y3+x1,0},{0,y1,0},{x1,y3,1}};
unsigned char nums[12]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40},c=(c=abs(cS))>10?10:(c<1)?1:c,cnt=(cnt=abs(nD))>10?10:(cnt<1)?1:cnt;
static void Draw7Number(Adafruit_GFX *gfx, int16_t n, uint16_t xLoc, uint16_t yLoc, int16_t cS, uint16_t fC, uint16_t bC, int16_t nD) {
uint16_t num=abs(n),i,t,w,col,h,a,b,j=1,d=0,S2=5*cS,S3=2*cS,S4=7*cS,x1=cS+1,x2=S3+S2+1,y1=yLoc+x1,y3=yLoc+S3+S4+1;
uint16_t seg[7][3]={{x1,yLoc,1},{x2,y1,0},{x2,y3+x1,0},{x1,(2*y3)-yLoc,1},{0,y3+x1,0},{0,y1,0},{x1,y3,1}};
uint8_t nums[12]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40},c=(c=abs(cS))>10?10:(c<1)?1:c,cnt=(cnt=abs(nD))>10?10:(cnt<1)?1:cnt;
for (xLoc+=cnt*(d=S2+(3*S3)+2);cnt>0;cnt--){
for (i=(num>9)?num%10:((!cnt)&&(n<0))?11:((nD<0)&&(!num))?10:num,xLoc-=d,num/=10,j=0;j<7;++j){
col=(nums[i]&(1<<j))?fC:bC;
@@ -341,7 +347,7 @@ static void DrawTime(Adafruit_GFX* gfx, tm_t* tm, int16_t x, int16_t y, uint16_t
}
static void DrawClock(Adafruit_GFX* gfx, tm_t* tm, struct Lunar_Date* Lunar, gui_data_t* data) {
uint8_t padding = data->height > 300 ? 100 : 40;
uint8_t padding = large_layout(data) ? 100 : 40;
GFX_setCursor(gfx, padding, 36);
GFX_printf_styled(gfx, GFX_RED, GFX_WHITE, u8g2_font_helvB18_tn, "%d", tm->tm_year + YEAR0);
GFX_printf_styled(gfx, GFX_BLACK, GFX_WHITE, u8g2_font_wqy12_t_lunar, "");

View File

@@ -415,14 +415,19 @@ static int16_t u8g2_font_draw_glyph(u8g2_font_t* u8g2, int16_t x, int16_t y, uin
//========================================================
/**
* Check, whether the font contains a glyph for the requested encoding.
*/
uint8_t u8g2_IsGlyph(u8g2_font_t* u8g2, uint16_t requested_encoding) {
/* updated to new code */
if (u8g2_font_get_glyph_data(u8g2, requested_encoding) != NULL) return 1;
return 0;
}
/* side effect: updates u8g2->font_decode and u8g2->glyph_x_offset */
/* actually u8g2_GetGlyphWidth returns the glyph delta x and glyph width itself is set as side effect */
/**
* side effect: updates u8g2->font_decode and u8g2->glyph_x_offset.
* actually u8g2_GetGlyphWidth returns the glyph delta x and glyph width itself is set as side effect
*/
int8_t u8g2_GetGlyphWidth(u8g2_font_t* u8g2, uint16_t requested_encoding) {
const uint8_t* glyph_data = u8g2_font_get_glyph_data(u8g2, requested_encoding);
if (glyph_data == NULL) return 0;
@@ -435,16 +440,49 @@ int8_t u8g2_GetGlyphWidth(u8g2_font_t* u8g2, uint16_t requested_encoding) {
return u8g2_font_decode_get_signed_bits(&(u8g2->font_decode), u8g2->font_info.bits_per_delta_x);
}
/**
* Defines, whether the glyph and string drawing functions will write the background color (mode 0/solid, is_transparent
* = 0) or not (mode 1/transparent, is_transparent = 1).
* Default mode is 0 (background color of the characters is overwritten).
*/
void u8g2_SetFontMode(u8g2_font_t* u8g2, uint8_t is_transparent) {
u8g2->font_decode.is_transparent = is_transparent; // new font procedures
}
/**
* The arguments defines the drawing direction of all strings or glyphs.
*
* Argument String Rotation Description
* 0 0 degree Left to right
* 1 90 degree Top to down
* 2 180 degree Right to left
* 3 270 degree Down to top
*/
void u8g2_SetFontDirection(u8g2_font_t* u8g2, uint8_t dir) { u8g2->font_decode.dir = dir; }
/**
* Draw a single character.
*
* The character is placed at the specified pixel posion x and y.
* U8g2 supports the lower 16 bit of the unicode character range (plane 0/Basic Multilingual Plane):
* The encoding can be any value from 0 to 65535.
* The glyph can be drawn only, if the encoding exists in the active font.
*/
int16_t u8g2_DrawGlyph(u8g2_font_t* u8g2, int16_t x, int16_t y, uint16_t encoding) {
return u8g2_font_draw_glyph(u8g2, x, y, encoding);
}
/**
* Draw a string.
*
* The first character is placed at position x and y.
* Use setFont to assign a font before drawing a string on the display.
* To draw a character with encoding 127 to 255, use the escape sequence "\xab" (hex value ab) or "\xyz"
* (octal value xyz).
* This function can not draw any glyph with encoding greater or equal to 256. Use drawUTF8 or drawGlyph to access
* glyphs with encoding greater or equal to 256.
* The X2 variant will double the size of the sting but will ignore the font direction setting.
*/
int16_t u8g2_DrawStr(u8g2_font_t* u8g2, int16_t x, int16_t y, const char* s) {
int16_t sum, delta;
sum = 0;
@@ -471,6 +509,9 @@ int16_t u8g2_DrawStr(u8g2_font_t* u8g2, int16_t x, int16_t y, const char* s) {
return sum;
}
/**
* Define a u8g2 font for the glyph and string drawing functions.
*/
void u8g2_SetFont(u8g2_font_t* u8g2, const uint8_t* font) {
if (u8g2->font != font) {
u8g2->font = font;
@@ -480,6 +521,12 @@ void u8g2_SetFont(u8g2_font_t* u8g2, const uint8_t* font) {
}
}
/**
* Set foreground color for font drawing
*/
void u8g2_SetForegroundColor(u8g2_font_t* u8g2, uint16_t fg) { u8g2->font_decode.fg_color = fg; }
/**
* Set background color for font drawing
*/
void u8g2_SetBackgroundColor(u8g2_font_t* u8g2, uint16_t bg) { u8g2->font_decode.bg_color = bg; }