vxKernel/src/modules/display/text/dfont.c

135 lines
2.7 KiB
C

#include <vhex/display/font.h>
//---
// Internal
//---
/* dfont_utf8_next(): Read the next UTF-8 code point of a string */
static uint32_t dfont_utf8_next(uint8_t const **str_pointer)
{
uint8_t const *str = *str_pointer;
uint8_t lead = *str++;
/* Skip non-leaders which are invalid as starting bytes */
while((lead >= 0x80 && lead <= 0xbf) ||
lead == 0xc0 || lead == 0xc1 || lead == 0xfe || lead == 0xff) {
lead = *str++;
}
/* This base case will handle the NUL terminator */
if(lead <= 0x7f) {
*str_pointer = str;
return lead;
}
uint8_t n2 = (*str++ & 0x3f);
if(lead <= 0xdf) {
*str_pointer = str;
return ((lead & 0x1f) << 6) | n2;
}
uint8_t n3 = (*str++ & 0x3f);
if(lead <= 0xef) {
*str_pointer = str;
return ((lead & 0x0f) << 12) | (n2 << 6) | n3;
}
uint8_t n4 = (*str++ & 0x3f);
if(lead <= 0xf7) {
*str_pointer = str;
return ((lead & 0x07) << 18) | (n2 << 12) | (n3 << 6) | n4;
}
/* It the string is too invalid, force a space and try to continue */
*str_pointer = str;
return 0x20;
}
/* dfont_glyph_index(): Obtain the glyph index of a Unicode code point */
int dfont_glyph_index(font_t const *f, uint32_t code_point)
{
int glyph_start = 0;
for(int i = 0; i < f->block_count; i++) {
int diff = code_point - f->blocks[i].start;
if(diff >= 0 && diff < f->blocks[i].length) {
return glyph_start + diff;
}
glyph_start += f->blocks[i].length;
}
return -1;
}
//---
// Public API
//---
/* dfont_get() : get the current font */
font_t *dfont_get(void)
{
extern font_t font8x9;
return &font8x9;
}
/* dfont_geometry() : get the char geometry */
int dfont_text_geometry(
font_t *font,
char const * const str_char,
int *size,
size_t *w,
size_t *h
) {
uint8_t const *str0 = (void *)str_char;
uint8_t const *str1 = (void *)str_char;
uint32_t code_point;
size_t char_width;
size_t length = 0;
size_t mx = 0;
size_t x = 0;
size_t y = 0;
int limit;
int glyph;
/* handle special behaviour */
if (font == NULL)
font = dfont_get();
limit = -1;
if (size != NULL && *size >= 0)
limit = *size;
/* generate geometry information */
while (1) {
code_point = dfont_utf8_next(&str0);
if (code_point == 0 || (limit > 0 && str0 - str1 >= limit))
break;
char_width = font->width;
if (font->shape.prop == 1) {
glyph = dfont_glyph_index(font, code_point);
if (glyph < 0)
return (-1);
char_width = font->glyph_width[glyph];
}
/* line discipline */
x += char_width;
if (code_point == '\n') {
y += font->data_height;
x = 0;
}
if (x > mx) mx = x;
length++;
}
/* set geometry information */
if (w != NULL) *w = mx;
if (h != NULL) *h = y;
if (size != NULL) *size = length;
return (0);
}