Skip to content

Commit

Permalink
fonts: don't leak memory for dynamically allocated fonts
Browse files Browse the repository at this point in the history
  • Loading branch information
david-vanderson committed Jan 16, 2025
1 parent afb8560 commit f16d04c
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/Examples.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1177,7 +1177,7 @@ pub fn textEntryWidgets(demo_win_id: u32) !void {
}

if (bytes) |b| {
try dvui.currentWindow().font_bytes.put(name, b);
try dvui.currentWindow().font_bytes.put(name, dvui.FontBytesEntry{ .ttf_bytes = b, .alloced = true });

_ = dvui.fontCacheGet(.{ .name = name, .size = 14 }) catch {
_ = dvui.currentWindow().font_bytes.remove(name);
Expand Down
8 changes: 4 additions & 4 deletions src/Font.zig
Original file line number Diff line number Diff line change
Expand Up @@ -129,14 +129,14 @@ pub const TTFBytes = struct {
//pub const OpenDyslexicBdIt = @embedFile("fonts/OpenDyslexic/compiled/OpenDyslexic-Bold-Italic.otf");
};

pub fn initTTFBytesDatabase(allocator: std.mem.Allocator) !std.StringHashMap([]const u8) {
var result = std.StringHashMap([]const u8).init(allocator);
pub fn initTTFBytesDatabase(allocator: std.mem.Allocator) !std.StringHashMap(dvui.FontBytesEntry) {
var result = std.StringHashMap(dvui.FontBytesEntry).init(allocator);
inline for (@typeInfo(TTFBytes).Struct.decls) |decl| {
try result.put(decl.name, @field(TTFBytes, decl.name));
try result.put(decl.name, dvui.FontBytesEntry{ .ttf_bytes = @field(TTFBytes, decl.name), .alloced = false });
}

if (!dvui.wasm) {
try result.put("Noto", @embedFile("fonts/NotoSansKR-Regular.ttf"));
try result.put("Noto", dvui.FontBytesEntry{ .ttf_bytes = @embedFile("fonts/NotoSansKR-Regular.ttf"), .alloced = false });
}

return result;
Expand Down
28 changes: 23 additions & 5 deletions src/dvui.zig
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,14 @@ pub fn frameTimeNS() i128 {
return currentWindow().frame_time_ns;
}

/// The bytes of a truetype font file and whether to free it.
pub const FontBytesEntry = struct {
ttf_bytes: []const u8,

/// If true, ttf_bytes was allocated by Window.gpa and will be freed by it.
alloced: bool,
};

const GlyphInfo = struct {
advance: f32, // horizontal distance to move the pen
leftBearing: f32, // horizontal distance from pen to bounding box left edge
Expand Down Expand Up @@ -387,8 +395,8 @@ const FontCacheEntry = struct {
pub fn hash(font: Font) u32 {
var h = fnv.init();
var bytes: []const u8 = undefined;
if (currentWindow().font_bytes.get(font.name)) |ttf_bytes| {
bytes = ttf_bytes;
if (currentWindow().font_bytes.get(font.name)) |fbe| {
bytes = fbe.ttf_bytes;
} else {
bytes = Font.default_ttf_bytes;
}
Expand Down Expand Up @@ -539,8 +547,8 @@ pub fn fontCacheGet(font: Font) !*FontCacheEntry {

//ttf bytes
const bytes = blk: {
if (currentWindow().font_bytes.get(font.name)) |ttf_bytes| {
break :blk ttf_bytes;
if (currentWindow().font_bytes.get(font.name)) |fbe| {
break :blk fbe.ttf_bytes;
} else {
log.warn("Font \"{s}\" not in dvui database, using default", .{font.name});
break :blk Font.default_ttf_bytes;
Expand Down Expand Up @@ -2576,7 +2584,7 @@ pub const Window = struct {
tab_index_prev: std.ArrayList(TabIndex),
tab_index: std.ArrayList(TabIndex),
font_cache: std.AutoHashMap(u32, FontCacheEntry),
font_bytes: std.StringHashMap([]const u8),
font_bytes: std.StringHashMap(FontBytesEntry),
texture_cache: std.AutoHashMap(u32, TextureCacheEntry),
dialog_mutex: std.Thread.Mutex,
dialogs: std.ArrayList(Dialog),
Expand Down Expand Up @@ -2850,7 +2858,17 @@ pub const Window = struct {
self.toasts.deinit();
self.keybinds.deinit();
self._arena.deinit();

{
var it = self.font_bytes.valueIterator();
while (it.next()) |fbe| {
if (fbe.alloced) {
self.gpa.free(fbe.ttf_bytes);
}
}
}
self.font_bytes.deinit();

{
for (self.themes.values()) |*theme| {
theme.deinit(self.gpa);
Expand Down

0 comments on commit f16d04c

Please sign in to comment.