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

270 lines
5.6 KiB
C

#include <vhex/display/font.h>
#include <vhex/display/draw/pixel.h>
#include <vhex/display/color.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;
}
#if 0
/* 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;
}
#endif
/* dfont_glyph_index(): Obtain the glyph index of a Unicode code point */
//TODO: suport unicode block
int dfont_glyph_index(font_t const *f, uint32_t code_point)
{
code_point -= ' ';
if ((int)code_point < 0 || code_point >= f->glyph.count)
return (-1);
return code_point;
}
/* dfont_char_render() : */
void dfont_char_render(
struct dshader_surface *surface,
int glyph_idx,
uint32_t *arg
) {
uint32_t glyph_bitmap;
uint32_t glyph_shift;
int glyph_width;
int glyph_size;
font_t *font;
int counter;
int sx;
int x;
int y;
x = arg[0];
y = arg[1];
/* generate font index / shift information */
font = dfont_get();
if (font->shape.prop == 1) {
glyph_width = font->glyph.prop[glyph_idx].width;
glyph_shift = font->glyph.prop[glyph_idx].shift;
glyph_idx = font->glyph.prop[glyph_idx].index;
glyph_size = glyph_width * font->glyph.height;
} else {
glyph_width = font->glyph.mono.width;
glyph_size = font->glyph.mono.storage_size;
glyph_shift = glyph_size * glyph_idx;
glyph_idx = glyph_shift >> 8;
glyph_shift = glyph_shift & 0xff;
}
/* drawing algo */
//TODO: cache surface data (limit read/write)
sx = x;
counter = 0;
glyph_shift = 0x80000000 >> glyph_shift;
glyph_bitmap = font->glyph.data[glyph_idx];
for (int i = 0; i < glyph_size; ++i)
{
if (glyph_bitmap & glyph_shift) {
dpixel_render(surface, x, y, arg[4]);
} else {
dpixel_render(surface, x, y, arg[5]);
}
/* update font bitmap index / shift */
glyph_shift >>= 1;
if (glyph_shift == 0x00000000) {
glyph_shift = 0x80000000;
glyph_bitmap = font->glyph.data[++glyph_idx];
}
/* update bitmap position */
x += 1;
counter += 1;
if (counter >= glyph_width) {
counter = 0;
x = sx;
y += 1;
}
}
}
//---
// Kernel-level API
//---
/* dfont_render() : draw caracter in surface */
void dfont_text_render(struct dshader_surface *surface, uint32_t *arg)
{
uint32_t code_point;
uint8_t const * str;
font_t *font;
int glyph_idx;
int glyph_width;
int counter;
int sx;
int sy;
/* check culling */
if (((int)arg[3] < surface->y1 && (int)arg[1] > surface->y2)
|| ((int)arg[2] < surface->x1 && (int)arg[0] > surface->x2)) {
return;
}
/* draw algo (update me) */
sx = arg[0];
sy = arg[1];
counter = -1;
font = dfont_get();
str = (void*)(uintptr_t)arg[6];
while (++counter < (int)arg[7])
{
code_point = dfont_utf8_next(&str);
if (code_point == '\n') {
arg[1] += font->glyph.height;
arg[0] = sx;
continue;
}
glyph_idx = dfont_glyph_index(font, code_point);
glyph_width = font->glyph.mono.width;
if (font->shape.prop == 1)
glyph_width = font->glyph.prop[glyph_idx].width;
dfont_char_render(surface, glyph_idx, arg);
arg[0] += glyph_width + font->char_spacing;
}
arg[1] = sy;
arg[0] = sx;
}
//---
// 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 length = 0;
size_t mx = 0;
size_t x = 0;
size_t y = 0;
size_t glyph_width;
int glyph_idx;
int limit;
/* 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;
/* line discipline */
length++;
if (code_point == '\n') {
y += font->glyph.height;
x = 0;
continue;
}
glyph_width = font->glyph.mono.width;
if (font->shape.prop == 1) {
glyph_idx = dfont_glyph_index(font, code_point);
if (glyph_idx < 0) {
if (w != NULL) *w = 0;
if (h != NULL) *h = 0;
if (size != NULL) *size = length;
return (-1);
}
glyph_width = font->glyph.prop[glyph_idx].width;
}
x += glyph_width + font->char_spacing;
if (x > mx) mx = x;
}
/* set geometry information */
if (w != NULL) *w = mx - font->char_spacing;
if (h != NULL) *h = y;
if (size != NULL) *size = length;
return (0);
}