mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
update the font atlas if there are changes
This commit is contained in:
@ -123,10 +123,13 @@ pub fn addGlyph(self: *FontAtlas, alloc: Allocator, v: anytype) !*Glyph {
|
||||
errdefer _ = self.glyphs.remove(utf32);
|
||||
|
||||
const glyph_index = glyph_index: {
|
||||
// log.warn("glyph load: {x}", .{utf32});
|
||||
const idx = ftc.FT_Get_Char_Index(self.ft_face, utf32);
|
||||
if (idx > 0) break :glyph_index idx;
|
||||
|
||||
// Unknown glyph.
|
||||
log.warn("glyph not found: {x}", .{utf32});
|
||||
|
||||
// TODO: render something more identifiable than a space
|
||||
break :glyph_index ftc.FT_Get_Char_Index(self.ft_face, ' ');
|
||||
};
|
||||
@ -178,7 +181,7 @@ pub fn addGlyph(self: *FontAtlas, alloc: Allocator, v: anytype) !*Glyph {
|
||||
.advance_x = f26dot6ToFloat(glyph.*.advance.x),
|
||||
};
|
||||
|
||||
//log.debug("loaded glyph codepoint={} glyph={}", .{ utf32, gop.value_ptr.* });
|
||||
//log.debug("loaded glyph codepoint=U+{x} glyph={}", .{ utf32, gop.value_ptr.* });
|
||||
|
||||
return gop.value_ptr;
|
||||
}
|
||||
|
50
src/Grid.zig
50
src/Grid.zig
@ -34,6 +34,7 @@ texture: gl.Texture,
|
||||
|
||||
/// The font atlas.
|
||||
font_atlas: FontAtlas,
|
||||
atlas_dirty: bool,
|
||||
|
||||
/// Whether the cursor is visible or not. This is used to control cursor
|
||||
/// blinking.
|
||||
@ -220,6 +221,7 @@ pub fn init(alloc: Allocator) !Grid {
|
||||
.vbo = vbo,
|
||||
.texture = tex,
|
||||
.font_atlas = font,
|
||||
.atlas_dirty = false,
|
||||
.cursor_visible = true,
|
||||
.cursor_style = .box,
|
||||
.foreground = .{ .r = 255, .g = 255, .b = 255 },
|
||||
@ -238,27 +240,6 @@ pub fn deinit(self: *Grid) void {
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
/// TODO: remove, this is for testing
|
||||
pub fn demoCells(self: *Grid) !void {
|
||||
self.cells.clearRetainingCapacity();
|
||||
try self.cells.ensureUnusedCapacity(self.alloc, self.size.columns * self.size.rows);
|
||||
|
||||
var row: u32 = 0;
|
||||
while (row < self.size.rows) : (row += 1) {
|
||||
var col: u32 = 0;
|
||||
while (col < self.size.columns) : (col += 1) {
|
||||
self.cells.appendAssumeCapacity(.{
|
||||
.grid_col = @intCast(u16, col),
|
||||
.grid_row = @intCast(u16, row),
|
||||
.bg_r = @intCast(u8, @mod(col * row, 255)),
|
||||
.bg_g = @intCast(u8, @mod(col, 255)),
|
||||
.bg_b = @intCast(u8, 255 - @mod(col, 255)),
|
||||
.bg_a = 255,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// updateCells updates our GPU cells from the current terminal view.
|
||||
/// The updated cells will take effect on the next render.
|
||||
pub fn updateCells(self: *Grid, term: Terminal) !void {
|
||||
@ -307,7 +288,12 @@ pub fn updateCells(self: *Grid, term: Terminal) !void {
|
||||
|
||||
// Get our glyph
|
||||
// TODO: if we add a glyph, I think we need to rerender the texture.
|
||||
const glyph = try self.font_atlas.addGlyph(self.alloc, cell.char);
|
||||
const glyph = if (self.font_atlas.getGlyph(cell.char)) |glyph|
|
||||
glyph
|
||||
else glyph: {
|
||||
self.atlas_dirty = true;
|
||||
break :glyph try self.font_atlas.addGlyph(self.alloc, cell.char);
|
||||
};
|
||||
|
||||
const fg = cell.fg orelse self.foreground;
|
||||
self.cells.appendAssumeCapacity(.{
|
||||
@ -374,6 +360,26 @@ pub fn setScreenSize(self: *Grid, dim: ScreenSize) !void {
|
||||
log.debug("screen size screen={} grid={}, cell={}", .{ dim, self.size, self.cell_size });
|
||||
}
|
||||
|
||||
/// Updates the font texture atlas if it is dirty.
|
||||
pub fn flushAtlas(self: *Grid) !void {
|
||||
if (!self.atlas_dirty) return;
|
||||
|
||||
var texbind = try self.texture.bind(.@"2D");
|
||||
defer texbind.unbind();
|
||||
try texbind.subImage2D(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
@intCast(c_int, self.font_atlas.atlas.size),
|
||||
@intCast(c_int, self.font_atlas.atlas.size),
|
||||
.Red,
|
||||
.UnsignedByte,
|
||||
self.font_atlas.atlas.data.ptr,
|
||||
);
|
||||
|
||||
self.atlas_dirty = false;
|
||||
}
|
||||
|
||||
pub fn render(self: Grid) !void {
|
||||
const t = trace(@src());
|
||||
defer t.end();
|
||||
|
@ -499,6 +499,9 @@ fn renderTimerCallback(t: *libuv.Timer) void {
|
||||
// Update the cells for drawing
|
||||
win.grid.updateCells(win.terminal) catch unreachable;
|
||||
|
||||
// Update our texture if we have to
|
||||
win.grid.flushAtlas() catch unreachable;
|
||||
|
||||
// Set our background
|
||||
gl.clearColor(win.bg_r, win.bg_g, win.bg_b, win.bg_a);
|
||||
gl.clear(gl.c.GL_COLOR_BUFFER_BIT);
|
||||
|
@ -114,6 +114,30 @@ pub const Binding = struct {
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn subImage2D(
|
||||
b: Binding,
|
||||
level: c.GLint,
|
||||
xoffset: c.GLint,
|
||||
yoffset: c.GLint,
|
||||
width: c.GLsizei,
|
||||
height: c.GLsizei,
|
||||
format: Format,
|
||||
typ: DataType,
|
||||
data: ?*const anyopaque,
|
||||
) !void {
|
||||
c.glTexSubImage2D(
|
||||
@enumToInt(b.target),
|
||||
level,
|
||||
xoffset,
|
||||
yoffset,
|
||||
width,
|
||||
height,
|
||||
@enumToInt(format),
|
||||
@enumToInt(typ),
|
||||
data,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/// Create a single texture.
|
||||
|
Reference in New Issue
Block a user