update the font atlas if there are changes

This commit is contained in:
Mitchell Hashimoto
2022-05-19 20:24:28 -07:00
parent 58b18a26f5
commit 6e86afba17
4 changed files with 59 additions and 23 deletions

View File

@ -123,10 +123,13 @@ pub fn addGlyph(self: *FontAtlas, alloc: Allocator, v: anytype) !*Glyph {
errdefer _ = self.glyphs.remove(utf32); errdefer _ = self.glyphs.remove(utf32);
const glyph_index = glyph_index: { const glyph_index = glyph_index: {
// log.warn("glyph load: {x}", .{utf32});
const idx = ftc.FT_Get_Char_Index(self.ft_face, utf32); const idx = ftc.FT_Get_Char_Index(self.ft_face, utf32);
if (idx > 0) break :glyph_index idx; if (idx > 0) break :glyph_index idx;
// Unknown glyph. // Unknown glyph.
log.warn("glyph not found: {x}", .{utf32});
// TODO: render something more identifiable than a space // TODO: render something more identifiable than a space
break :glyph_index ftc.FT_Get_Char_Index(self.ft_face, ' '); 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), .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; return gop.value_ptr;
} }

View File

@ -34,6 +34,7 @@ texture: gl.Texture,
/// The font atlas. /// The font atlas.
font_atlas: FontAtlas, font_atlas: FontAtlas,
atlas_dirty: bool,
/// Whether the cursor is visible or not. This is used to control cursor /// Whether the cursor is visible or not. This is used to control cursor
/// blinking. /// blinking.
@ -220,6 +221,7 @@ pub fn init(alloc: Allocator) !Grid {
.vbo = vbo, .vbo = vbo,
.texture = tex, .texture = tex,
.font_atlas = font, .font_atlas = font,
.atlas_dirty = false,
.cursor_visible = true, .cursor_visible = true,
.cursor_style = .box, .cursor_style = .box,
.foreground = .{ .r = 255, .g = 255, .b = 255 }, .foreground = .{ .r = 255, .g = 255, .b = 255 },
@ -238,27 +240,6 @@ pub fn deinit(self: *Grid) void {
self.* = undefined; 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. /// updateCells updates our GPU cells from the current terminal view.
/// The updated cells will take effect on the next render. /// The updated cells will take effect on the next render.
pub fn updateCells(self: *Grid, term: Terminal) !void { pub fn updateCells(self: *Grid, term: Terminal) !void {
@ -307,7 +288,12 @@ pub fn updateCells(self: *Grid, term: Terminal) !void {
// Get our glyph // Get our glyph
// TODO: if we add a glyph, I think we need to rerender the texture. // 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; const fg = cell.fg orelse self.foreground;
self.cells.appendAssumeCapacity(.{ 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 }); 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 { pub fn render(self: Grid) !void {
const t = trace(@src()); const t = trace(@src());
defer t.end(); defer t.end();

View File

@ -499,6 +499,9 @@ fn renderTimerCallback(t: *libuv.Timer) void {
// Update the cells for drawing // Update the cells for drawing
win.grid.updateCells(win.terminal) catch unreachable; win.grid.updateCells(win.terminal) catch unreachable;
// Update our texture if we have to
win.grid.flushAtlas() catch unreachable;
// Set our background // Set our background
gl.clearColor(win.bg_r, win.bg_g, win.bg_b, win.bg_a); gl.clearColor(win.bg_r, win.bg_g, win.bg_b, win.bg_a);
gl.clear(gl.c.GL_COLOR_BUFFER_BIT); gl.clear(gl.c.GL_COLOR_BUFFER_BIT);

View File

@ -114,6 +114,30 @@ pub const Binding = struct {
data, 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. /// Create a single texture.