2025-09-17 01:47:49 +02:00

341 lines
9.1 KiB
C

#include "font.h"
#include "LCD_driver.h"
uint16_t GFX_DrawChar(
uint16_t x,
uint16_t y,
char c,
const GFXfont *font,
uint16_t fg_color,
uint16_t bg_color) {
if (c < font->firstChar || c > font->lastChar) {
return x;
}
uint16_t glyphIndex = c - font->firstChar;
const GFXglyph *glyph = &font->glyphs[glyphIndex];
int16_t startX = x + glyph->xOffset;
int16_t startY = y + glyph->yOffset;
if (startX + glyph->width <= 0 || startX >= LCD_WIDTH) return x;
if (startY + glyph->height <= 0 || startY >= LCD_HEIGHT) return x;
LCD_Set_Address(startX, startY, startX + glyph->width - 1,
startY + glyph->height - 1);
uint16_t pendingPixelCount = 0;
uint8_t pendingPixelBit = 0;
uint16_t bitPos = 0;
for (uint8_t row = 0; row < glyph->height; row++) {
for (uint8_t col = 0; col < glyph->width; col++) {
uint16_t byteIndex = glyph->bitmapOffset + (bitPos / 8);
uint8_t bitIndex = bitPos % 8;
uint8_t pixelBit =
(font->bitmaps[byteIndex] & (0x80 >> bitIndex)) ? 1 : 0;
if (pixelBit == pendingPixelBit) {
pendingPixelCount++;
} else {
if (pendingPixelCount != 0) {
LCD_Draw_Colour_Burst(pendingPixelBit ? fg_color : bg_color,
pendingPixelCount);
pendingPixelCount = 1;
}
pendingPixelBit = pixelBit;
}
// Per pixel way: slower
// uint16_t pixelColor = pixelBit ? fg_color : bg_color;
//
// int16_t absX = startX + col;
// int16_t absY = startY + row;
//
// if (absX >= 0 && absX < LCD_WIDTH && absY >= 0 && absY < LCD_HEIGHT) {
// LCD_Draw_Pixel(absX, absY, pixelColor);
// }
bitPos++;
}
}
if (pendingPixelCount != 0) {
LCD_Draw_Colour_Burst(pendingPixelBit ? fg_color : bg_color,
pendingPixelCount);
}
return x + glyph->advance;
}
uint16_t GFX_ClearChar(
uint16_t x,
uint16_t y,
char c,
const GFXfont *font,
uint16_t bg_color) {
if (c < font->firstChar || c > font->lastChar) {
return x;
}
uint16_t glyphIndex = c - font->firstChar;
const GFXglyph *glyph = &font->glyphs[glyphIndex];
int16_t startX = x + glyph->xOffset;
int16_t startY = y + glyph->yOffset;
if (startX + glyph->width <= 0 || startX >= LCD_WIDTH) return x;
if (startY + glyph->height <= 0 || startY >= LCD_HEIGHT) return x;
LCD_Set_Address(startX, startY, startX + glyph->width - 1,
startY + glyph->height - 1);
LCD_Draw_Colour_Burst(bg_color, glyph->height * glyph->width);
return x + glyph->advance;
}
uint16_t GFX_DrawCharScaled(
uint16_t x,
uint16_t y,
char c,
const GFXfont *font,
uint16_t fg_color,
uint16_t bg_color,
uint8_t scale) {
if (c < font->firstChar || c > font->lastChar || scale == 0) {
return x;
}
uint16_t glyphIndex = c - font->firstChar;
const GFXglyph *glyph = &font->glyphs[glyphIndex];
// Calcul des dimensions cibles après mise à l'échelle
uint16_t target_width = (glyph->width + scale - 1) / scale;
uint16_t target_height = (glyph->height + scale - 1) / scale;
int16_t startX = x + (glyph->xOffset / scale);
int16_t startY = y + (glyph->yOffset / scale);
// Vérification des limites avec les dimensions cibles
if (startX + target_width <= 0 || startX >= LCD_WIDTH) return x;
if (startY + target_height <= 0 || startY >= LCD_HEIGHT) return x;
// Mode standard (sans anti-crénelage)
if (scale == 1) {
LCD_Set_Address(startX, startY, startX + glyph->width - 1,
startY + glyph->height - 1);
uint16_t pendingPixelCount = 0;
uint8_t pendingPixelBit = 0;
uint16_t bitPos = 0;
for (uint8_t row = 0; row < glyph->height; row++) {
for (uint8_t col = 0; col < glyph->width; col++) {
uint16_t byteIndex = glyph->bitmapOffset + (bitPos / 8);
uint8_t bitIndex = bitPos % 8;
uint8_t pixelBit =
(font->bitmaps[byteIndex] & (0x80 >> bitIndex)) ? 1 : 0;
if (pixelBit == pendingPixelBit) {
pendingPixelCount++;
} else {
if (pendingPixelCount != 0) {
LCD_Draw_Colour_Burst(pendingPixelBit ? fg_color : bg_color,
pendingPixelCount);
pendingPixelCount = 1;
}
pendingPixelBit = pixelBit;
}
bitPos++;
}
}
if (pendingPixelCount != 0) {
LCD_Draw_Colour_Burst(pendingPixelBit ? fg_color : bg_color,
pendingPixelCount);
}
return x + glyph->advance;
}
// Mode anti-crénelage (scale > 1)
const uint16_t scale_sq = scale * scale;
const uint8_t bg_R = (bg_color >> 11) & 0x1F;
const uint8_t bg_G = (bg_color >> 5) & 0x3F;
const uint8_t bg_B = bg_color & 0x1F;
const uint8_t fg_R = (fg_color >> 11) & 0x1F;
const uint8_t fg_G = (fg_color >> 5) & 0x3F;
const uint8_t fg_B = fg_color & 0x1F;
for (uint16_t tr = 0; tr < target_height; tr++) {
for (uint16_t tc = 0; tc < target_width; tc++) {
uint16_t count = 0;
const uint16_t src_row_start = tr * scale;
const uint16_t src_col_start = tc * scale;
// Comptage des pixels actifs dans le bloc source
for (uint8_t sr = 0; sr < scale; sr++) {
for (uint8_t sc = 0; sc < scale; sc++) {
const uint16_t src_row = src_row_start + sr;
const uint16_t src_col = src_col_start + sc;
if (src_row >= glyph->height || src_col >= glyph->width) continue;
const uint32_t bitPos = (src_row * glyph->width) + src_col;
const uint16_t byteIndex = glyph->bitmapOffset + (bitPos / 8);
const uint8_t bitIndex = bitPos % 8;
if (font->bitmaps[byteIndex] & (0x80 >> bitIndex)) {
count++;
}
}
}
// Calcul de la couleur interpolée (RGB565)
const uint8_t coverage = (count * 100) / scale_sq;// Pour éviter la division flottante
const uint8_t R = (bg_R * (100 - coverage) + fg_R * coverage) / 100;
const uint8_t G = (bg_G * (100 - coverage) + fg_G * coverage) / 100;
const uint8_t B = (bg_B * (100 - coverage) + fg_B * coverage) / 100;
const uint16_t blended_color = (R << 11) | (G << 5) | B;
// Dessin du pixel cible si dans les limites
const int16_t absX = startX + tc;
const int16_t absY = startY + tr;
if (absX >= 0 && absX < LCD_WIDTH && absY >= 0 && absY < LCD_HEIGHT) {
LCD_Draw_Pixel(absX, absY, blended_color);
}
}
}
return x + (glyph->advance / scale);
}
uint16_t GFX_GetTextWidth(
const char *text,
const GFXfont *font,
int8_t letter_spacing) {
uint16_t width = 0;
while (*text != '\0') {
if (*text >= font->firstChar && *text <= font->lastChar) {
uint16_t glyphIndex = *text - font->firstChar;
width += font->glyphs[glyphIndex].advance;
width += letter_spacing;
} else {
// Default advance for unsupported characters
width += 10;
}
text++;
}
return width;
}
uint16_t GFX_DrawText(
uint16_t x,
uint16_t y,
const char *text,
const GFXfont *font,
uint16_t fg_color,
uint16_t bg_color,
uint8_t alignment,
int8_t letter_spacing) {
// Adjust x position based on alignment
switch (alignment) {
case 1:// Center
x -= GFX_GetTextWidth(text, font, letter_spacing) / 2;
break;
case 2:// Right
x -= GFX_GetTextWidth(text, font, letter_spacing);
break;
// Default: Left alignment (no adjustment)
}
// Draw each character
uint16_t cursorX = x;
uint16_t cursorY = y;
while (*text != '\0') {
cursorX = GFX_DrawChar(cursorX, cursorY, *text, font, fg_color, bg_color);
cursorX += letter_spacing;
text++;
}
if (alignment == 2) {
return x;
}
return cursorX;
}
uint16_t GFX_ClearText(
uint16_t x,
uint16_t y,
const char *text,
const GFXfont *font,
uint16_t bg_color,
uint8_t alignment,
int8_t letter_spacing) {
// Adjust x position based on alignment
switch (alignment) {
case 1:// Center
x -= GFX_GetTextWidth(text, font, letter_spacing) / 2;
break;
case 2:// Right
x -= GFX_GetTextWidth(text, font, letter_spacing);
break;
// Default: Left alignment (no adjustment)
}
// Draw each character
uint16_t cursorX = x;
uint16_t cursorY = y;
while (*text != '\0') {
cursorX = GFX_ClearChar(cursorX, cursorY, *text, font, bg_color);
cursorX += letter_spacing;
text++;
}
if (alignment == 2) {
return x;
}
return cursorX;
}
uint16_t GFX_DrawTextScaled(
uint16_t x,
uint16_t y,
const char *text,
const GFXfont *font,
uint16_t fg_color,
uint16_t bg_color,
uint8_t alignment,
int8_t letter_spacing,
uint8_t scale) {
uint16_t text_width = GFX_GetTextWidth(text, font, letter_spacing * scale);
if (scale != 1) {
text_width /= scale;
}
// Adjust x position based on alignment
switch (alignment) {
case 1:// Center
x -= text_width / 2;
break;
case 2:// Right
x -= text_width;
break;
// Default: Left alignment (no adjustment)
}
// Draw each character
uint16_t cursorX = x;
uint16_t cursorY = y;
while (*text != '\0') {
cursorX = GFX_DrawCharScaled(cursorX, cursorY, *text, font, fg_color, bg_color,
scale);
cursorX += letter_spacing;
text++;
}
if (alignment == 2) {
return x;
}
return cursorX;
}