better fonts part 1

This commit is contained in:
Vicente Ferrari Smith 2026-01-16 00:19:50 +01:00
parent 818382f37b
commit 52ea7dcfa5
2 changed files with 77 additions and 119 deletions

View File

@ -32,8 +32,6 @@ const Glyph = struct {
st0 : rl.Vector2, st0 : rl.Vector2,
st1 : rl.Vector2, st1 : rl.Vector2,
bitmap : []u8,
}; };
pub const Font = struct { pub const Font = struct {
@ -49,25 +47,9 @@ pub const Font = struct {
const font_data = try std.fs.cwd().readFileAlloc(allocator, filename, 10 * 1024 * 1024); const font_data = try std.fs.cwd().readFileAlloc(allocator, filename, 10 * 1024 * 1024);
// if !success {
// log("[Error] Could not read the font file.");
// return;
// }
var face = try ft_lib.createFaceMemory(font_data, 0); var face = try ft_lib.createFaceMemory(font_data, 0);
var glyphs = std.AutoHashMap(u32, Glyph).init(allocator); var glyphs = std.AutoHashMap(u32, Glyph).init(allocator);
// .FT_New_Memory_Face(ftlib, font_data.data, xx font_data.count, 0, *face);
// if error {
// log("[Error] %", to_string(FT_Error_String(error)));
// }
//FT_Set_Pixel_Sizes(face, 0, size);
try face.setCharSize(0, size * 64, 0, 96); try face.setCharSize(0, size * 64, 0, 96);
// error = FT_Set_Char_Size(face, 0, size * 64, 0, 96);
// if error {
// log("[Error] %", to_string(FT_Error_String(error)));
// }
var rects = try std.ArrayList(rp.stbrp_rect).initCapacity(allocator, 1024); var rects = try std.ArrayList(rp.stbrp_rect).initCapacity(allocator, 1024);
defer rects.deinit(allocator); defer rects.deinit(allocator);
@ -79,8 +61,9 @@ pub const Font = struct {
const _atlas : []u8 = try allocator.alloc(u8, ATLAS_SIZE * ATLAS_SIZE); const _atlas : []u8 = try allocator.alloc(u8, ATLAS_SIZE * ATLAS_SIZE);
defer allocator.free(_atlas); defer allocator.free(_atlas);
@memset(_atlas, 0);
var atlas : rl.Image = .{ const atlas : rl.Image = .{
.data = _atlas.ptr, .data = _atlas.ptr,
.width = ATLAS_SIZE, .width = ATLAS_SIZE,
.height = ATLAS_SIZE, .height = ATLAS_SIZE,
@ -88,35 +71,67 @@ pub const Font = struct {
.format = .uncompressed_grayscale .format = .uncompressed_grayscale
}; };
std.log.info("We have {} glyphs", .{face.numGlyphs()});
// for (0..1500) |i| {
for (0..face.numGlyphs()) |i| { for (0..face.numGlyphs()) |i| {
//for 65..90 { try face.loadGlyph(@intCast(i), .{});
//index := FT_Get_Char_Index(face, xx it); const bmp = face.glyph().bitmap();
try face.loadGlyph(@intCast(i), .{ .render = true });
// array_add(*rects, stbrp_rect.{cast(s32) face.glyph.glyph_index, cast(s32) face.glyph.bitmap.width, std.log.info("got size {}, {}", .{bmp.width(), bmp.rows()});
// cast(s32) face.glyph.bitmap.rows, 0, 0, 0});
try rects.append(allocator, try rects.append(allocator, .{
rp.stbrp_rect{
.id = @intCast(face.glyph().glyphIndex()), .id = @intCast(face.glyph().glyphIndex()),
.w = @intCast(face.glyph().bitmap().width()), .w = @intCast(bmp.width()),
.h = @intCast(face.glyph().bitmap().rows()), .h = @intCast(bmp.rows()),
.x = 0, .x = 0,
.y = 0, .y = 0,
.was_packed = 0, .was_packed = 0,
});
} }
);
const width = face.glyph().bitmap().width(); _ = rp.stbrp_pack_rects(&stbrpcontext, rects.items.ptr, @intCast(rects.items.len));
const height = face.glyph().bitmap().rows();
for (rects.items) |rect| {
if (rect.was_packed == 0) continue;
const i : u32 = @intCast(rect.id);
//index := FT_Get_Char_Index(face, xx it);
try face.loadGlyph(i, .{ .render = true });
const bmp = face.glyph().bitmap();
const buf = bmp.buffer() orelse continue;
const pitch: usize = @intCast(bmp.pitch());
const w: usize = @intCast(bmp.width());
const h: usize = @intCast(bmp.rows());
const dst_x: usize = @intCast(rect.x);
const dst_y: usize = @intCast(rect.y);
for (0..h) |y| {
const src = buf[y * pitch .. y * pitch + w];
const dst_index = (dst_y + y) * ATLAS_SIZE + dst_x;
const dst = _atlas[dst_index .. dst_index + w];
@memcpy(dst, src);
}
const width = bmp.width();
const height = bmp.rows();
const bearing_y = face.glyph().bitmapTop(); const bearing_y = face.glyph().bitmapTop();
const descent = @as(i32, @intCast(height)) - bearing_y; const descent = @as(i32, @intCast(height)) - bearing_y;
const ascent = @as(i32, @intCast(height)) - descent; const ascent = @as(i32, @intCast(height)) - descent;
const x : i16 = @intCast(rect.x);
const y : i16 = @as(i16, @intCast(rect.y)) + @as(i16, @intCast(height));
const fx = @as(f32, @floatFromInt(x));
const fy = @as(f32, @floatFromInt(y));
const fw = @as(f32, @floatFromInt(width));
const fh = @as(f32, @floatFromInt(height));
const fs = @as(f32, ATLAS_SIZE);
const glyph = Glyph{ const glyph = Glyph{
.x = 0, .x = x,
.y = 0, .y = y,
.utf32 = @intCast(i), .utf32 = @intCast(i),
.index = face.glyph().glyphIndex(), .index = face.glyph().glyphIndex(),
.bearing_x = face.glyph().bitmapLeft(), .bearing_x = face.glyph().bitmapLeft(),
@ -128,77 +143,15 @@ pub const Font = struct {
.descent = @as(i32, @intCast(face.glyph().bitmap().rows())) - face.glyph().bitmapTop(), .descent = @as(i32, @intCast(face.glyph().bitmap().rows())) - face.glyph().bitmapTop(),
.ascent = ascent, .ascent = ascent,
.advance = @intCast(face.glyph().advance().x >> 6), .advance = @intCast(face.glyph().advance().x >> 6),
.st0 = rl.Vector2.zero(), .st0 = rl.Vector2.init((fx + 0.5) / fs, (fy - 0.5) / fs),
.st1 = rl.Vector2.zero(), .st1 = .{.x = (fx + fw) / fs, .y = (fy - fh) / fs},
.bitmap = try allocator.alloc(u8, width * height),
}; };
const s = glyph.height * glyph.width;
const buf = face.glyph().bitmap().buffer() orelse continue;
@memcpy(glyph.bitmap, buf[0..s]);
// table_set(*font.glyphs, glyph.index, glyph);
try glyphs.put(glyph.index, glyph); try glyphs.put(glyph.index, glyph);
// if (try glyphs.fetchPut(glyph.index, glyph)) |old| {
// allocator.free(old.value.bitmap);
// }
}
_ = rp.stbrp_pack_rects(&stbrpcontext, rects.items.ptr, @intCast(rects.items.len));
// stbrp_pack_rects(*stbrpcontext, rects.data, cast(s32) rects.count);
for (rects.items) |rect| {
// for rect : rects {
const glyph = glyphs.getPtr(@intCast(rect.id)) orelse continue;
if (glyph.bitmap.len != 0) {
glyph.x = @intCast(rect.x);
glyph.y = @as(i16, @intCast(rect.y)) + @as(i16, @intCast(glyph.height));
const fx = @as(f32, @floatFromInt(glyph.x));
const fy = @as(f32, @floatFromInt(glyph.y));
const fw = @as(f32, @floatFromInt(glyph.width));
const fh = @as(f32, @floatFromInt(glyph.height));
const fs = @as(f32, ATLAS_SIZE);
glyph.st0 = rl.Vector2.init((fx + 0.5) / fs, (fy - 0.5) / fs);
glyph.st1 = .{
.x = (fx + fw) / fs,
.y = (fy - fh) / fs,
};
const g = rl.Image{
.data = glyph.bitmap.ptr,
.width = @intCast(glyph.width),
.height = @intCast(glyph.height),
.mipmaps = 1,
.format = .uncompressed_grayscale
};
atlas.drawImage(g,
.{ .x = 0, .y = 0, .width = @floatFromInt(g.width), .height = @floatFromInt(g.height) },
.{ .x = @floatFromInt(rect.x), .y = @floatFromInt(rect.y), .width = @floatFromInt(g.width), .height = @floatFromInt(g.height)},
.white
);
// copyBitmapToAtlas(glyph.bitmap, glyph.width, glyph.height, atlas, @intCast(rect.x), @intCast(rect.y));
}
// glyph : *Glyph = table_find_pointer(*font.glyphs, cast(u32) rect.id);
// if (glyph.bitmap) {
// glyph.x = cast,trunc(s16, rect.x);
// glyph.y = cast,trunc(s16, rect.y) + xx glyph.height;
// glyph.st0 = .{cast(float, glyph.x + 0.5) / ATLAS_SIZE, cast(float, glyph.y - 0.5) / ATLAS_SIZE};
// //glyph.st1 = glyph.st0 + .{cast(float, glyph.width) / ATLAS_SIZE, -cast(float, glyph.height) / ATLAS_SIZE};
// glyph.st1 = .{(cast(float, glyph.x + glyph.width )) / cast(float, ATLAS_SIZE), (cast(float, glyph.y - glyph.height )) / cast(float, ATLAS_SIZE)};
// copyBitmapToAtlas(glyph.bitmap, cast(s32) glyph.width, cast(s32) glyph.height, atlas, cast(s32) rect.x, cast(s32) rect.y);
// }
} }
const texture = try rl.Texture.fromImage(atlas); const texture = try rl.Texture.fromImage(atlas);
// image.flipVertical();
// texture = make_texture_from_data(atlas, ATLAS_SIZE, ATLAS_SIZE);
// const kb = kbts_FontFromMemory(font_data.data, xx font_data.count, 0, null, null);
var _kb = kb.kbts_FontFromMemory(font_data.ptr, @intCast(font_data.len), 0, null, null); var _kb = kb.kbts_FontFromMemory(font_data.ptr, @intCast(font_data.len), 0, null, null);
if (kb.kbts_FontIsValid(&_kb) != 0) { if (kb.kbts_FontIsValid(&_kb) != 0) {
@ -215,10 +168,10 @@ pub const Font = struct {
} }
pub fn deinit(self: *Font, allocator: std.mem.Allocator) void { pub fn deinit(self: *Font, allocator: std.mem.Allocator) void {
var it = self.glyphs.valueIterator(); // var it = self.glyphs.valueIterator();
while (it.next()) |g| { // while (it.next()) |g| {
allocator.free(g.bitmap); // allocator.free(g.bitmap);
} // }
self.glyphs.deinit(); self.glyphs.deinit();
self.face.deinit(); self.face.deinit();
kb.kbts_FreeFont(&self.kb); kb.kbts_FreeFont(&self.kb);
@ -314,9 +267,9 @@ pub const Font = struct {
rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st1.x, st1.y); rl.gl.rlVertex2f(p1.x, p1.y); rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st1.x, st1.y); rl.gl.rlVertex2f(p1.x, p1.y);
rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st0.x, st1.y); rl.gl.rlVertex2f(p0.x, p1.y); rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st0.x, st1.y); rl.gl.rlVertex2f(p0.x, p1.y);
rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st1.x, st0.y); rl.gl.rlVertex2f(p1.x, p0.y); // rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st1.x, st0.y); rl.gl.rlVertex2f(p1.x, p0.y);
rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st1.x, st1.y); rl.gl.rlVertex2f(p1.x, p1.y); // rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st1.x, st1.y); rl.gl.rlVertex2f(p1.x, p1.y);
rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st0.x, st0.y); rl.gl.rlVertex2f(p0.x, p0.y); // rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st0.x, st0.y); rl.gl.rlVertex2f(p0.x, p0.y);
} else { } else {
continue; continue;
} }

View File

@ -180,17 +180,22 @@ pub fn main() !void {
rl.beginDrawing(); rl.beginDrawing();
f.texture.drawPro(
.{.x = 0, .y = 0, .width = 4096, .height = 4096},
.{.x = 0, .y = 0, .width = 512, .height = 512 },
.zero(), 0, .white);
rl.beginShaderMode(shader); rl.beginShaderMode(shader);
f.render_text( // f.render_text(
"H", // "H",
rl.Vector2.init(400, 400), // rl.Vector2.init(400, 400),
true, // true,
rl.Color.white, // rl.Color.white,
rl.Color.blank, // rl.Color.blank,
true, // true,
true // true
); // );
// rl.gl.rlBegin(rl.gl.rl_triangles); // rl.gl.rlBegin(rl.gl.rl_triangles);