Skip to content

Commit

Permalink
GUI: LVGL Fonts through SDL_TTF with cache.
Browse files Browse the repository at this point in the history
  • Loading branch information
gabordemooij committed Feb 16, 2025
1 parent 8678795 commit ff4308d
Showing 1 changed file with 142 additions and 74 deletions.
216 changes: 142 additions & 74 deletions plugins/gui/gui.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>

#include "lvgl/lvgl.h"
#include "../../citrine.h"
#include <media.h>
Expand All @@ -16,20 +18,83 @@ ctr_object* packageObject;
ctr_object* CtrGUIAssetPackage;
ctr_object* colorObject;
ctr_object* fontObject;
lv_font_manager_t* g_font_manager;
lv_event_dsc_t* CtrEventHandler = NULL;

struct CtrGUIGlyphCacheEntry {
uint32_t index;
lv_font_glyph_dsc_t dsc;
uint8_t* data;
};
typedef struct CtrGUIGlyphCacheEntry CtrGUIGlyphCacheEntry;


#define CTR_GUI_MAX_FONTS 10
#define CTR_GUI_GLYPHCACHE_MAX 250
CtrGUIGlyphCacheEntry ctr_gui_glyph_cache[CTR_GUI_GLYPHCACHE_MAX * CTR_GUI_MAX_FONTS];

struct GUIFNT {
ctr_object* ref;
char* name;
char* path;
lv_font_t* font;
int num;
TTF_Font* font;
lv_font_t* lvfont;
};
typedef struct GUIFNT GUIFNT;
GUIFNT GuiFNT[10];
int maxFNT = 10;
GUIFNT GuiFNT[CTR_GUI_MAX_FONTS];
int maxFNT = CTR_GUI_MAX_FONTS;
int FNTCount = 0;


SDL_RWops* ctr_internal_gui_load_asset(char* asset_name, char asset_type) {
SDL_RWops* res = NULL;
// If we have no asset package, load from file instead
if (CtrGUIAssetPackage == NULL) {
res = SDL_RWFromFile(asset_name, "rb");
return res;
}
char* path = ctr_heap_allocate_cstring(ctr_internal_object_property(CtrGUIAssetPackage, "path", NULL));
SDL_RWops* asset_file = SDL_RWFromFile(path, "rb");
ctr_heap_free(path);
if (!asset_file) {
unsigned int blob_len;
void* blob;
//No asset file? then maybe embedded data blob?
blob = ctr_data_blob(&blob_len);
if (blob == NULL) {
return NULL;
}
asset_file = SDL_RWFromMem(blob, blob_len);
}
SDL_RWseek(asset_file, 0, RW_SEEK_SET);
char* buffer = ctr_heap_allocate(500);
while(1) {
uint64_t read_start = SDL_RWtell(asset_file);
int bytes_read = SDL_RWread(asset_file, buffer, 1, 500);
if (strncmp(asset_name, buffer, bytes_read) == 0) {
SDL_RWseek(asset_file, read_start + strlen(asset_name) + 1, RW_SEEK_SET);
uint64_t next_entry = 0;
SDL_RWread(asset_file, &next_entry, 8, 1);
uint64_t curpos = SDL_RWtell(asset_file);
uint64_t read_size = next_entry - curpos;
char* read_buffer = malloc(read_size);
SDL_RWread(asset_file, read_buffer, 1, read_size);
res = SDL_RWFromMem(read_buffer, read_size);
break;
} else {
char* boundary = strchr(buffer, 0);
uint64_t jmp_address = *((uint64_t*) (boundary+1));
if (jmp_address == 0) {
break;
}
SDL_RWseek(asset_file, jmp_address, SEEK_SET);
}
}
SDL_RWclose(asset_file);
ctr_heap_free(buffer);
return res;
}

ctr_object* ctr_color_new(ctr_object* myself, ctr_argument* argumentList) {
ctr_object* instance = ctr_internal_create_object(CTR_OBJECT_TYPE_OTOBJECT);
instance->link = myself;
Expand Down Expand Up @@ -109,6 +174,7 @@ ctr_object* ctr_font_new(ctr_object* myself, ctr_argument* argumentList) {
fnt->ref = instance;
fnt->name = ctr_heap_allocate(10);
fnt->path = NULL;
fnt->num = FNTCount - 1;
sprintf(fnt->name, "font%d", FNTCount-1);
ctr_resource* rs = ctr_heap_allocate( sizeof(ctr_resource) );
rs->ptr = fnt;
Expand All @@ -122,6 +188,55 @@ GUIFNT* ctr_internal_get_font_from_object(ctr_object* object) {
return (GUIFNT*) object->value.rvalue->ptr;
}


bool ctr_internal_gui_describe_glyph(const lv_font_t * f, lv_font_glyph_dsc_t * glyph_dsc, uint32_t unicode, uint32_t unicode_letter_next) {
CtrGUIGlyphCacheEntry* cache;
int cache_index = (((GUIFNT*)f->dsc)->num * CTR_GUI_GLYPHCACHE_MAX) + (unicode % CTR_GUI_GLYPHCACHE_MAX);
cache = &ctr_gui_glyph_cache[cache_index];
if (cache->index == unicode) {
*(glyph_dsc) = cache->dsc;
return true;
}
SDL_Color white = {255, 255, 255, 255};
SDL_Surface* glyph_surface = TTF_RenderGlyph_Blended(((GUIFNT*)f->dsc)->font, (uint16_t)unicode, white);
int w = glyph_surface->w;
int h = glyph_surface->h;
glyph_dsc->box_w = glyph_surface->w;
glyph_dsc->box_h = glyph_surface->h;
glyph_dsc->ofs_x = 0;
glyph_dsc->ofs_y = -TTF_FontAscent(((GUIFNT*)f->dsc)->font);
glyph_dsc->adv_w = glyph_surface->w;
glyph_dsc->format= LV_FONT_GLYPH_FORMAT_A8;
glyph_dsc->gid.index = unicode;
cache->dsc = *glyph_dsc;
cache->index = (uint32_t) unicode;
cache->data = ctr_heap_allocate(h * w * sizeof(uint8_t));
uint8_t* pixels = (uint8_t *)glyph_surface->pixels;
int pitch = glyph_surface->pitch; // Bytes per row
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
uint32_t pixel = *(Uint32 *)(pixels + y * pitch + x * 4); // Read 32-bit pixel
uint8_t alpha = (pixel >> 24) & 0xFF; // Extract alpha channel
cache->data[y * w + x] = alpha;
}
}
SDL_FreeSurface(glyph_surface);
return true;
}

const void* ctr_internal_gui_render_glyph(lv_font_glyph_dsc_t* d, lv_draw_buf_t * buf) {
GUIFNT* g = (GUIFNT*) d->resolved_font->dsc;
TTF_Font* f = g->font;
CtrGUIGlyphCacheEntry* cache;
int cache_index = (g->num * CTR_GUI_GLYPHCACHE_MAX) + (d->gid.index % CTR_GUI_GLYPHCACHE_MAX);
cache = &ctr_gui_glyph_cache[cache_index];
if (cache->index == d->gid.index) {
memcpy(buf->data, cache->data, (d->box_w * d->box_h * sizeof(uint8_t)));
return buf;
}
ctr_error("Unexpected code path.",0);
}

/**
* @def
* [ Font ] new
Expand All @@ -136,85 +251,30 @@ ctr_object* ctr_font_font(ctr_object* myself, ctr_argument* argumentList) {
GUIFNT* fnt = ctr_internal_get_font_from_object(myself);
if (fnt == NULL) return myself;
char* path = ctr_heap_allocate_cstring(ctr_internal_cast2string(argumentList->object));
char already_loaded = 0;
for (int i = 0; i < FNTCount; i++) {
if (fnt->path && strcmp(fnt->path, path) == 0) {
already_loaded = 1;
}
}
if (!already_loaded) {
lv_font_manager_add_path(g_font_manager, fnt->name, path);
SDL_RWops* res = ctr_internal_gui_load_asset(path, 1);
ctr_heap_free(path);
if (res == NULL) {
ctr_error("Unable to load font.", 0);
return myself;
}
fnt->path = ctr_heap_allocate(strlen(path)+1);
strcpy(fnt->path, path);
int size = ctr_tonum(argumentList->next->object);
fnt->font = lv_font_manager_create_font(
g_font_manager,
fnt->name,
LV_FREETYPE_FONT_RENDER_MODE_BITMAP,
size,
LV_FREETYPE_FONT_STYLE_NORMAL);
lv_xml_register_font(fnt->name, fnt->font);
fnt->font = TTF_OpenFontRW(res, 1, (int)ctr_internal_cast2number(argumentList->next->object)->value.nvalue);
fnt->lvfont = ctr_heap_allocate(sizeof(lv_font_t));
fnt->lvfont->get_glyph_dsc = ctr_internal_gui_describe_glyph;
fnt->lvfont->get_glyph_bitmap = ctr_internal_gui_render_glyph;
fnt->lvfont->line_height = TTF_FontHeight(fnt->font);
fnt->lvfont->base_line = TTF_FontAscent(fnt->font);
fnt->lvfont->dsc = fnt;
fnt->lvfont->user_data = NULL;
lv_xml_register_font(fnt->name, fnt->lvfont);
return myself;
}


ctr_object* ctr_font_name(ctr_object* myself, ctr_argument* argumentList) {
GUIFNT* fnt = ctr_internal_get_font_from_object(myself);
if (fnt == NULL) return myself;
return ctr_build_string( fnt->name, strlen(fnt->name) );
}


SDL_RWops* ctr_internal_gui_load_asset(char* asset_name, char asset_type) {
SDL_RWops* res = NULL;
// If we have no asset package, load from file instead
if (CtrGUIAssetPackage == NULL) {
res = SDL_RWFromFile(asset_name, "rb");
return res;
}
char* path = ctr_heap_allocate_cstring(ctr_internal_object_property(CtrGUIAssetPackage, "path", NULL));
SDL_RWops* asset_file = SDL_RWFromFile(path, "rb");
ctr_heap_free(path);
if (!asset_file) {
unsigned int blob_len;
void* blob;
//No asset file? then maybe embedded data blob?
blob = ctr_data_blob(&blob_len);
if (blob == NULL) {
return NULL;
}
asset_file = SDL_RWFromMem(blob, blob_len);
}
SDL_RWseek(asset_file, 0, RW_SEEK_SET);
char* buffer = ctr_heap_allocate(500);
while(1) {
uint64_t read_start = SDL_RWtell(asset_file);
int bytes_read = SDL_RWread(asset_file, buffer, 1, 500);
if (strncmp(asset_name, buffer, bytes_read) == 0) {
SDL_RWseek(asset_file, read_start + strlen(asset_name) + 1, RW_SEEK_SET);
uint64_t next_entry = 0;
SDL_RWread(asset_file, &next_entry, 8, 1);
uint64_t curpos = SDL_RWtell(asset_file);
uint64_t read_size = next_entry - curpos;
char* read_buffer = malloc(read_size);
SDL_RWread(asset_file, read_buffer, 1, read_size);
res = SDL_RWFromMem(read_buffer, read_size);
break;
} else {
char* boundary = strchr(buffer, 0);
uint64_t jmp_address = *((uint64_t*) (boundary+1));
if (jmp_address == 0) {
break;
}
SDL_RWseek(asset_file, jmp_address, SEEK_SET);
}
}
SDL_RWclose(asset_file);
ctr_heap_free(buffer);
return res;
}

void ctr_gui_internal_event_handler(lv_event_t* e) {
ctr_argument* arguments;
char* message;
Expand Down Expand Up @@ -295,7 +355,15 @@ void ctr_internal_gui_init(void) {
lv_indev_set_group(mousewheel, lv_group_get_default());
lv_indev_t * keyboard = lv_sdl_keyboard_create();
lv_indev_set_group(keyboard, lv_group_get_default());
g_font_manager = lv_font_manager_create(8);
//g_font_manager = lv_font_manager_create(8);
if (TTF_Init() < 0) {
printf("TTF INIT FAIL\n");
}
for(int i = 0; i < CTR_GUI_GLYPHCACHE_MAX * CTR_GUI_MAX_FONTS; i++) {
CtrGUIGlyphCacheEntry* cache = &ctr_gui_glyph_cache[i];
cache->data = NULL;
cache->index = 0;
}
}

ctr_object* ctr_gui_screen(ctr_object* myself, ctr_argument* argumentList) {
Expand Down

0 comments on commit ff4308d

Please sign in to comment.