summaryrefslogtreecommitdiff
path: root/src/gui/font.c
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-24 12:22:51 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commit39cdba439c1ae47dbc7c3243a9273aa011c6affa (patch)
treec9408bae952e350892f073c8f9bd3661f5e90943 /src/gui/font.c
parente5f764bd201df6554272427fc8cedffece18066f (diff)
downloadpardes-39cdba439c1ae47dbc7c3243a9273aa011c6affa.tar.gz
pardes-39cdba439c1ae47dbc7c3243a9273aa011c6affa.zip
Allocate FreeType's and HarfBuzz's memory from the gui's Zig allocator
FreeType and HarfBuzz allocated with libc malloc, out of sight of the Zig allocator the rest of the gui uses, so its Debug leak report and a test's std.testing.allocator never saw them. Now every allocation the font code makes goes through ui_malloc, ui_calloc, ui_realloc and ui_free, exported from gui.zig over `font_allocator`: - HarfBuzz is compiled with hb_malloc_impl and friends pointing at them. - Each UIFont gives its FreeType library its own memory manager (FT_New_Library with an FT_MemoryRec_ over the same functions, then FT_Add_Default_Modules and FT_Set_Default_Properties: exactly what FT_Init_FreeType does over its malloc manager), torn down with FT_Done_Library. - The shim's own UIFont and scratch arrays use them too. C's free and realloc pass no size, so a block starts with a 32-byte header (the caller's pointer stays 16-byte, max_align_t, aligned) holding its size and the allocator that made it, so a block goes back where it came from even after font_allocator is repointed. runNative points font_allocator at the gui's gpa. HarfBuzz makes a few process-wide objects on first use and never frees them (the default Unicode functions, the locale's language, hb-ft's font functions); ui_font_prime makes them from the default heap before that, so a Debug build's leak report stays about fonts. Tests do the same: the shaping tests, and a new test that creates, rasterizes and shapes fonts under std.testing.allocator, report any leak. Only the render thread calls into fonts, and every allocator used is thread-safe. It costs nothing measurable: against the previous change, 10 interleaved rounds with an A/A pair show every scenario as fast or faster with Adwaita Mono and Maple Mono alike, and first paint over 30 launches each matches main. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Diffstat (limited to 'src/gui/font.c')
-rw-r--r--src/gui/font.c59
1 files changed, 47 insertions, 12 deletions
diff --git a/src/gui/font.c b/src/gui/font.c
index 5cb98e1a..aebe26e6 100644
--- a/src/gui/font.c
+++ b/src/gui/font.c
@@ -6,14 +6,17 @@
#include "font.h"
#include <freetype-zig.h>
#include <freetype/ftbitmap.h>
+#include <freetype/ftmodapi.h>
#include <hb-ft.h>
#include <hb-ot.h>
#include <hb.h>
-#include <stdlib.h>
#include <string.h>
struct UIFont {
+ // FreeType allocates through ui_malloc and friends: the library holds a
+ // pointer to this for its lifetime, which the UIFont outlives.
+ struct FT_MemoryRec_ memory;
FT_Library library;
FT_Face face;
FT_F26Dot6 size;
@@ -60,20 +63,42 @@ static FT_Int32 load_flags(void) {
return FT_LOAD_DEFAULT | FT_LOAD_TARGET_LIGHT;
}
+static void *ft_alloc(FT_Memory memory, long size) {
+ (void)memory;
+ return ui_malloc((size_t)size);
+}
+
+static void ft_free(FT_Memory memory, void *block) {
+ (void)memory;
+ ui_free(block);
+}
+
+static void *ft_realloc(FT_Memory memory, long cur_size, long new_size,
+ void *block) {
+ (void)memory;
+ (void)cur_size;
+ return ui_realloc(block, (size_t)new_size);
+}
+
UIFont *ui_font_new(const uint8_t *data, int32_t len) {
if (!data || len <= 0)
return NULL;
- UIFont *f = (UIFont *)calloc(1, sizeof(UIFont));
+ UIFont *f = (UIFont *)ui_calloc(1, sizeof(UIFont));
if (!f)
return NULL;
- if (FT_Init_FreeType(&f->library) != 0) {
- free(f);
+ // FT_Init_FreeType is exactly FT_New_Library over FT_New_Memory's malloc
+ // manager, then the default modules and properties: the same, over ours.
+ f->memory = (struct FT_MemoryRec_){NULL, ft_alloc, ft_free, ft_realloc};
+ if (FT_New_Library(&f->memory, &f->library) != 0) {
+ ui_free(f);
return NULL;
}
+ FT_Add_Default_Modules(f->library);
+ FT_Set_Default_Properties(f->library);
if (FT_New_Memory_Face(f->library, data, (FT_Long)len, 0, &f->face) != 0) {
- FT_Done_FreeType(f->library);
- free(f);
+ FT_Done_Library(f->library);
+ ui_free(f);
return NULL;
}
// Some symbol fonts do not expose a Unicode charmap. FreeType already
@@ -136,10 +161,20 @@ void ui_font_free(UIFont *f) {
if (f->face)
FT_Done_Face(f->face);
if (f->library)
- FT_Done_FreeType(f->library);
- free(f->own);
- free(f->state);
- free(f);
+ FT_Done_Library(f->library);
+ ui_free(f->own);
+ ui_free(f->state);
+ ui_free(f);
+}
+
+void ui_font_prime(UIFont *f) {
+ if (!f)
+ return;
+ hb_font_destroy(hb_ft_font_create_referenced(f->face));
+ hb_buffer_t *buffer = hb_buffer_create();
+ hb_buffer_add_utf8(buffer, "a", 1, 0, 1);
+ hb_buffer_guess_segment_properties(buffer);
+ hb_buffer_destroy(buffer);
}
void ui_font_cell_metrics(UIFont *f, float px, int32_t *cell_w, int32_t *cell_h,
@@ -325,11 +360,11 @@ void ui_font_shape(UIFont *f, float px, int32_t cell_w, const uint32_t *cps,
if (!substitutable || cell_w <= 0 || !set_size(f, px))
return;
if (n > f->scratch) {
- uint32_t *own = (uint32_t *)realloc(f->own, (size_t)n * sizeof *own);
+ uint32_t *own = (uint32_t *)ui_realloc(f->own, (size_t)n * sizeof *own);
if (!own)
return;
f->own = own;
- uint8_t *state = (uint8_t *)realloc(f->state, (size_t)n);
+ uint8_t *state = (uint8_t *)ui_realloc(f->state, (size_t)n);
if (!state)
return;
f->state = state;