430800c8
Improve glyph positioning
a73x 2026-04-08 11:58
Commit message
src/font.zig
| Old | New | ||
|---|---|---|---|
| @@ -138,6 +138,11 @@ pub const Face = struct { | |||
| 138 | const metrics = self.face.*.size.*.metrics; | 138 | const metrics = self.face.*.size.*.metrics; |
| 139 | return @intCast((metrics.ascender - metrics.descender) >> 6); | 139 | return @intCast((metrics.ascender - metrics.descender) >> 6); |
| 140 | } | 140 | } |
| 141 | |||
| 142 | pub fn baseline(self: *Face) u32 { | ||
| 143 | const metrics = self.face.*.size.*.metrics; | ||
| 144 | return @intCast(metrics.ascender >> 6); | ||
| 145 | } | ||
| 141 | }; | 146 | }; |
| 142 | 147 | ||
| 143 | pub const GlyphUV = struct { | 148 | pub const GlyphUV = struct { |
src/main.zig
| Old | New | ||
|---|---|---|---|
| @@ -56,6 +56,7 @@ fn runTerminal(alloc: std.mem.Allocator) !void { | |||
| 56 | 56 | ||
| 57 | const cell_w = face.cellWidth(); | 57 | const cell_w = face.cellWidth(); |
| 58 | const cell_h = face.cellHeight(); | 58 | const cell_h = face.cellHeight(); |
| 59 | const baseline = face.baseline(); | ||
| 59 | 60 | ||
| 60 | // === grid size === | 61 | // === grid size === |
| 61 | const initial_grid: GridSize = .{ .cols = 80, .rows = 24 }; | 62 | const initial_grid: GridSize = .{ .cols = 80, .rows = 24 }; |
| @@ -218,6 +219,7 @@ fn runTerminal(alloc: std.mem.Allocator) !void { | |||
| 218 | col_idx, | 219 | col_idx, |
| 219 | cell_w, | 220 | cell_w, |
| 220 | cell_h, | 221 | cell_h, |
| 222 | baseline, | ||
| 221 | glyph_uv, | 223 | glyph_uv, |
| 222 | bg_uv, | 224 | bg_uv, |
| 223 | colors, | 225 | colors, |
| @@ -286,6 +288,7 @@ fn appendCellInstances( | |||
| 286 | col_idx: u32, | 288 | col_idx: u32, |
| 287 | cell_w: u32, | 289 | cell_w: u32, |
| 288 | cell_h: u32, | 290 | cell_h: u32, |
| 291 | baseline: u32, | ||
| 289 | glyph_uv: ?font.GlyphUV, | 292 | glyph_uv: ?font.GlyphUV, |
| 290 | bg_uv: font.GlyphUV, | 293 | bg_uv: font.GlyphUV, |
| 291 | colors: vt.CellColors, | 294 | colors: vt.CellColors, |
| @@ -308,7 +311,7 @@ fn appendCellInstances( | |||
| 308 | .glyph_size = .{ @floatFromInt(uv.width), @floatFromInt(uv.height) }, | 311 | .glyph_size = .{ @floatFromInt(uv.width), @floatFromInt(uv.height) }, |
| 309 | .glyph_bearing = .{ | 312 | .glyph_bearing = .{ |
| 310 | @floatFromInt(uv.bearing_x), | 313 | @floatFromInt(uv.bearing_x), |
| 311 | @as(f32, @floatFromInt(cell_h)) - @as(f32, @floatFromInt(uv.bearing_y)), | 314 | glyphTopOffset(baseline, uv.bearing_y), |
| 312 | }, | 315 | }, |
| 313 | .uv_rect = .{ uv.u0, uv.v0, uv.u1, uv.v1 }, | 316 | .uv_rect = .{ uv.u0, uv.v0, uv.u1, uv.v1 }, |
| 314 | .fg = colors.fg, | 317 | .fg = colors.fg, |
| @@ -316,6 +319,10 @@ fn appendCellInstances( | |||
| 316 | }); | 319 | }); |
| 317 | } | 320 | } |
| 318 | 321 | ||
| 322 | fn glyphTopOffset(baseline: u32, bearing_y: i32) f32 { | ||
| 323 | return @as(f32, @floatFromInt(baseline)) - @as(f32, @floatFromInt(bearing_y)); | ||
| 324 | } | ||
| 325 | |||
| 319 | fn encodeKeyboardEvent( | 326 | fn encodeKeyboardEvent( |
| 320 | term: *const vt.Terminal, | 327 | term: *const vt.Terminal, |
| 321 | ev: wayland_client.KeyboardEvent, | 328 | ev: wayland_client.KeyboardEvent, |
| @@ -416,6 +423,7 @@ fn runDrawSmokeTest(alloc: std.mem.Allocator) !void { | |||
| 416 | // Cell size from font metrics | 423 | // Cell size from font metrics |
| 417 | const cell_w: f32 = @floatFromInt(face.cellWidth()); | 424 | const cell_w: f32 = @floatFromInt(face.cellWidth()); |
| 418 | const cell_h: f32 = @floatFromInt(face.cellHeight()); | 425 | const cell_h: f32 = @floatFromInt(face.cellHeight()); |
| 426 | const baseline = face.baseline(); | ||
| 419 | std.debug.print("cell size: {d}x{d}\n", .{ cell_w, cell_h }); | 427 | std.debug.print("cell size: {d}x{d}\n", .{ cell_w, cell_h }); |
| 420 | 428 | ||
| 421 | // One Instance: 'M' at cell position (40, 12) — near center of 80x24 grid | 429 | // One Instance: 'M' at cell position (40, 12) — near center of 80x24 grid |
| @@ -425,7 +433,7 @@ fn runDrawSmokeTest(alloc: std.mem.Allocator) !void { | |||
| 425 | .glyph_size = .{ @floatFromInt(glyph_uv.width), @floatFromInt(glyph_uv.height) }, | 433 | .glyph_size = .{ @floatFromInt(glyph_uv.width), @floatFromInt(glyph_uv.height) }, |
| 426 | .glyph_bearing = .{ | 434 | .glyph_bearing = .{ |
| 427 | @floatFromInt(glyph_uv.bearing_x), | 435 | @floatFromInt(glyph_uv.bearing_x), |
| 428 | @as(f32, @floatFromInt(face.cellHeight())) - @as(f32, @floatFromInt(glyph_uv.bearing_y)), | 436 | glyphTopOffset(baseline, glyph_uv.bearing_y), |
| 429 | }, | 437 | }, |
| 430 | .uv_rect = .{ glyph_uv.u0, glyph_uv.v0, glyph_uv.u1, glyph_uv.v1 }, | 438 | .uv_rect = .{ glyph_uv.u0, glyph_uv.v0, glyph_uv.u1, glyph_uv.v1 }, |
| 431 | .fg = .{ 1.0, 1.0, 1.0, 1.0 }, | 439 | .fg = .{ 1.0, 1.0, 1.0, 1.0 }, |
| @@ -503,6 +511,7 @@ test "appendCellInstances emits a background quad for colored space" { | |||
| 503 | 3, | 511 | 3, |
| 504 | 8, | 512 | 8, |
| 505 | 16, | 513 | 16, |
| 514 | 12, | ||
| 506 | null, | 515 | null, |
| 507 | bg_uv, | 516 | bg_uv, |
| 508 | .{ | 517 | .{ |
| @@ -552,6 +561,7 @@ test "appendCellInstances emits background before glyph" { | |||
| 552 | 1, | 561 | 1, |
| 553 | 8, | 562 | 8, |
| 554 | 16, | 563 | 16, |
| 564 | 12, | ||
| 555 | glyph_uv, | 565 | glyph_uv, |
| 556 | bg_uv, | 566 | bg_uv, |
| 557 | .{ | 567 | .{ |
| @@ -566,6 +576,11 @@ test "appendCellInstances emits background before glyph" { | |||
| 566 | try std.testing.expectEqualDeep([2]f32{ 7.0, 11.0 }, instances.items[1].glyph_size); | 576 | try std.testing.expectEqualDeep([2]f32{ 7.0, 11.0 }, instances.items[1].glyph_size); |
| 567 | try std.testing.expectEqualDeep([4]f32{ 0.1, 0.2, 0.3, 1.0 }, instances.items[0].fg); | 577 | try std.testing.expectEqualDeep([4]f32{ 0.1, 0.2, 0.3, 1.0 }, instances.items[0].fg); |
| 568 | try std.testing.expectEqualDeep([4]f32{ 0.9, 0.8, 0.7, 1.0 }, instances.items[1].fg); | 578 | try std.testing.expectEqualDeep([4]f32{ 0.9, 0.8, 0.7, 1.0 }, instances.items[1].fg); |
| 579 | try std.testing.expectEqualDeep([2]f32{ 1.0, -1.0 }, instances.items[1].glyph_bearing); | ||
| 580 | } | ||
| 581 | |||
| 582 | test "glyphTopOffset uses baseline rather than cell height" { | ||
| 583 | try std.testing.expectEqual(@as(f32, -3.0), glyphTopOffset(9, 12)); | ||
| 569 | } | 584 | } |
| 570 | 585 | ||
| 571 | fn runRenderSmokeTest(alloc: std.mem.Allocator) !void { | 586 | fn runRenderSmokeTest(alloc: std.mem.Allocator) !void { |