Files
ui-library-playground/Core/Src/graphics/fonts/font.c
Dylan Smith b48cb11197 Menu work
2026-01-15 15:53:02 -05:00

153 lines
7.6 KiB
C

#include "font.h"
#include "display.h"
#include "lvgl.h"
#include "stdbool.h"
#include <stdint.h>
static uint16_t find_glyph_id_in_cmap(const lv_font_fmt_txt_cmap_t * cmap, uint32_t character)
{
uint16_t cmap_last_character = cmap->range_start + cmap->range_length - 1;
bool character_exists_in_cmap = character >= cmap->range_start && character <= cmap_last_character;
if (character_exists_in_cmap)
{
uint16_t glyph_id = cmap->glyph_id_start - cmap->range_start + character;
return glyph_id;
}
return 0;
}
bool lv_font_get_glyph_dsc_fmt_txt(const lv_font_t * font, lv_font_fmt_txt_glyph_dsc_t * dsc_out, uint32_t character)
{
lv_font_fmt_txt_dsc_t *font_dsc = (lv_font_fmt_txt_dsc_t *) font->dsc;
for (uint16_t i = 0; i < font_dsc->cmap_num; i++)
{
uint16_t glyph_id = find_glyph_id_in_cmap(font_dsc->cmaps + i, character);
if (glyph_id == 0) continue;
*dsc_out = font_dsc->glyph_dsc[glyph_id];
return true;
}
return false;
}
// Left as a dummy definition for LVGL font compatibility.
// We're not processing the bitmaps with draw buffers like LVGL does.
const void * lv_font_get_bitmap_fmt_txt(lv_font_fmt_txt_glyph_dsc_t * g_dsc, lv_draw_buf_t * draw_buf)
{
}
/**
* @brief Draws a single character to the framebuffer using the specified font.
*
* This function renders a character glyph from an LVGL font to the framebuffer at the
* specified location. The character is drawn in the provided color using a 1-bit bitmap
* format where set bits are drawn as colored pixels and unset bits are left transparent.
*
* @param framebuffer Pointer to the framebuffer array where the character will be drawn.
* Must be large enough to accommodate the character at the specified
* location (typically DISPLAY_HEIGHT * DISPLAY_WIDTH pixels).
* @param font Pointer to the LVGL font structure containing glyph data and metrics.
* The font must be a valid LVGL formatted text font (lv_font_fmt_txt_dsc_t).
* @param x_loc X coordinate (horizontal position) where the top-left of the character
* bounding box will be drawn. Must be within framebuffer bounds.
* @param y_loc Y coordinate (vertical position) where the top-left of the character
* bounding box will be drawn. Must be within framebuffer bounds.
* @param character The ASCII character code (0-255) to be drawn.
* @param color The RGB565 color value to use when drawing the character pixels.
* Pixels corresponding to set bits in the glyph bitmap will be drawn
* in this color.
*
* @return The advance width of the drawn glyph in pixels (lower 16 bits of glyph_dsc.adv_w).
* This value can be used to calculate the x position for the next character when
* drawing text. Returns 0 if the character glyph was not found in the font.
*
* @note The function performs no bounds checking. Ensure x_loc and y_loc are within
* the framebuffer dimensions to prevent buffer overruns.
* @note The glyph bitmap is stored in a packed 1-bit format where each bit represents
* one pixel (1 = draw pixel, 0 = skip pixel).
* @note If the character is not found in the font's character map, the function returns
* 0 without drawing anything.
*/
uint16_t draw_character(pixel_t *framebuffer, const lv_font_t *font, const uint16_t x_loc, const uint16_t y_loc, const char character, pixel_t color)
{
const lv_font_fmt_txt_dsc_t *font_dsc = (lv_font_fmt_txt_dsc_t *)font->dsc;
lv_font_fmt_txt_glyph_dsc_t glyph_dsc;
bool glyph_found = lv_font_get_glyph_dsc_fmt_txt(font, &glyph_dsc, character);
if (!glyph_found) return 0;
const uint16_t glyph_width = glyph_dsc.box_w;
const uint16_t glyph_height = glyph_dsc.box_h;
const int8_t glyph_ofs_x = glyph_dsc.ofs_x;
const int8_t glyph_ofs_y = glyph_dsc.ofs_y;
const uint32_t num_bitmap_bits = glyph_height * glyph_width;
for (uint32_t bitmap_pixel_index = 0; bitmap_pixel_index < num_bitmap_bits; bitmap_pixel_index++)
{
const uint8_t current_bit_in_byte = bitmap_pixel_index % 8;
const uint16_t bitmap_byte_index = glyph_dsc.bitmap_index + (bitmap_pixel_index / 8);
const int32_t fb_pixel_x = x_loc + (bitmap_pixel_index % glyph_width) + glyph_ofs_x;
const int32_t fb_pixel_y = y_loc + font->line_height - glyph_height + (bitmap_pixel_index / glyph_width) - glyph_ofs_y - font->base_line;
const uint32_t fb_pixel_index = (fb_pixel_y * DISPLAY_WIDTH) + fb_pixel_x;
const bool bit_on = font_dsc->glyph_bitmap[bitmap_byte_index] & (0x80 >> current_bit_in_byte);
if (bit_on)
{
if (fb_pixel_x >= 0 && fb_pixel_x < DISPLAY_WIDTH && fb_pixel_y >= 0 && fb_pixel_y < DISPLAY_HEIGHT)
{
framebuffer[fb_pixel_index] = color;
}
}
}
return (glyph_dsc.adv_w & 0x0000FFFF);
}
/**
* @brief Draws a null-terminated string to the framebuffer using the specified font.
*
* This function renders a complete string of characters to the framebuffer by iteratively
* calling draw_character() for each character in the string. Characters are drawn horizontally
* from left to right, with each character's advance width determining the spacing for the
* next character. All characters are drawn on the same baseline (y_loc remains constant).
*
* @param framebuffer Pointer to the framebuffer array where the string will be drawn.
* Must be large enough to accommodate the entire string at the specified
* location (typically DISPLAY_HEIGHT * DISPLAY_WIDTH pixels).
* @param font Pointer to the LVGL font structure containing glyph data and metrics.
* The font must be a valid LVGL formatted text font (lv_font_fmt_txt_dsc_t).
* @param x_loc X coordinate (horizontal position) where the first character's top-left
* bounding box will be drawn. Must be within framebuffer bounds.
* @param y_loc Y coordinate (vertical position) where all characters' top-left bounding
* boxes will be drawn. Must be within framebuffer bounds.
* @param string Pointer to a null-terminated C string (array of uint8_t) containing
* the characters to be drawn. The function will stop drawing when it
* encounters a null terminator ('\0').
* @param color The RGB565 color value to use when drawing all character pixels.
* Pixels corresponding to set bits in each glyph bitmap will be drawn
* in this color.
*
* @note The function performs no bounds checking. Ensure x_loc and y_loc are within
* the framebuffer dimensions, and that the entire string will fit within the
* framebuffer bounds to prevent buffer overruns.
* @note Characters not found in the font (draw_character returns 0) will be skipped
* without advancing the x position, which may cause subsequent characters to
* overlap with missing characters.
* @note The string must be null-terminated. The function will continue drawing until
* it encounters a '\0' character.
* @note All characters in the string are drawn on the same horizontal baseline (y_loc
* remains constant for all characters).
*/
void draw_string(pixel_t *framebuffer, const lv_font_t *font, const uint16_t x_loc, const uint16_t y_loc, const char *string, pixel_t color)
{
uint32_t current_x = x_loc << 4;
for (const char *char_ptr = string; *char_ptr != '\0'; char_ptr++)
{
uint16_t advance_width = draw_character(framebuffer, font, current_x >> 4, y_loc, *char_ptr, color);
current_x += advance_width;
}
}