gint/src/render/topti.c

190 lines
4.2 KiB
C

#include <gint/defs/types.h>
#include <gint/display.h>
#include "../render/render.h"
/* dfont(): Set the default font for text rendering */
font_t const *dfont(font_t const * font)
{
font_t const *old_font = topti_font;
topti_font = font ? font : gint_default_font;
return old_font;
}
/* dfont_default(): Get gint's default font */
font_t const *dfont_default(void)
{
return gint_default_font;
}
/* topti_glyph_index(): Obtain the glyph index of a Unicode code point */
int topti_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;
}
/* topti_offset(): Use a font index to find the location of a glyph */
int topti_offset(font_t const *f, uint glyph)
{
/* Non-proportional fonts don't need an index */
if(!f->prop) return glyph * f->storage_size;
uint8_t const *width = f->glyph_width;
/* The index gives us the position of all glyphs whose IDs are
multiples of 8. Start with a close one and iterate from there. */
uint g = glyph & ~0x7;
int offset = f->glyph_index[g >> 3];
/* Traverse the width array (which is in bits) while converting to
longword size */
while(g < glyph) offset += (width[g++] * f->data_height + 31) >> 5;
return offset;
}
/* topti_utf8_next(): Read the next UTF-8 code point of a string */
uint32_t topti_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;
}
/* dnsize(): Get the width and height of rendered text, with character limit */
void dnsize(char const *str_char, int size, font_t const *f, int *w, int *h)
{
uint8_t const *str = (void *)str_char;
uint8_t const *str0 = str;
uint32_t code_point;
if(!f) f = topti_font;
if(h) *h = f->line_height;
if(!w) return;
/* Width for monospaced fonts is easy, unfortunately we still need to
compute the length and group bytes into Unicode code points. */
if(!f->prop)
{
int length = 0;
while(1)
{
code_point = topti_utf8_next(&str);
if(!code_point || (size >= 0 && str - str0 > size))
break;
length++;
}
*w = (f->width + f->char_spacing) * length - f->char_spacing;
return;
}
/* For proportional fonts, fetch the width of each individual glyphs */
int width = 0;
while(1)
{
code_point = topti_utf8_next(&str);
if(!code_point || (size >= 0 && str - str0 > size)) break;
int glyph = topti_glyph_index(f, code_point);
if(glyph >= 0)
width += f->glyph_width[glyph] + f->char_spacing;
}
*w = width - f->char_spacing;
}
/* dsize(): Get the width and height of rendered text */
void dsize(char const *str_char, font_t const *f, int *w, int *h)
{
return dnsize(str_char, -1, f, w, h);
}
/* drsize(): Get width of rendered text with reverse size limit */
char const *drsize(char const *str_char, font_t const *f, int width, int *w)
{
uint8_t const *str = (void *)str_char;
uint32_t code_point;
int used_width = 0;
if(!f) f = topti_font;
while(used_width < width)
{
/* Record that last glyph considered fits */
str_char = (void *)str;
code_point = topti_utf8_next(&str);
if(!code_point)
{
break;
}
if(used_width > 0) used_width += f->char_spacing;
if(!f->prop)
{
used_width += f->width;
}
else
{
int glyph = topti_glyph_index(f, code_point);
if(glyph >= 0) used_width += f->glyph_width[glyph];
}
}
if(w) *w = used_width;
return str_char;
}