finish win32 gui support for double-wide characters

This commit is contained in:
Jonathan Marler 2025-09-23 13:32:13 -06:00 committed by CJ van den Berg
parent 8278a080af
commit 05b87b1406
3 changed files with 25 additions and 7 deletions

View file

@ -75,6 +75,7 @@ pub fn render(
self: *const DwriteRenderer, self: *const DwriteRenderer,
font: Font, font: Font,
utf8: []const u8, utf8: []const u8,
double_width: bool,
) void { ) void {
var utf16_buf: [10]u16 = undefined; var utf16_buf: [10]u16 = undefined;
const utf16_len = std.unicode.utf8ToUtf16Le(&utf16_buf, utf8) catch unreachable; const utf16_len = std.unicode.utf8ToUtf16Le(&utf16_buf, utf8) catch unreachable;
@ -85,7 +86,10 @@ pub fn render(
const rect: win32.D2D_RECT_F = .{ const rect: win32.D2D_RECT_F = .{
.left = 0, .left = 0,
.top = 0, .top = 0,
.right = @floatFromInt(font.cell_size.x), .right = if (double_width)
@as(f32, @floatFromInt(font.cell_size.x)) * @as(f32, @floatFromInt(font.cell_size.x))
else
@as(f32, @floatFromInt(font.cell_size.x)),
.bottom = @floatFromInt(font.cell_size.y), .bottom = @floatFromInt(font.cell_size.y),
}; };
self.render_target.BeginDraw(); self.render_target.BeginDraw();

View file

@ -149,7 +149,7 @@ pub const WindowState = struct {
} }
// TODO: this should take a utf8 graphme instead // TODO: this should take a utf8 graphme instead
pub fn generateGlyph(state: *WindowState, font: Font, codepoint: u21, right_half: bool) u32 { pub fn generateGlyph(state: *WindowState, font: Font, codepoint: u21, kind: enum { single, left, right }) u32 {
// for now we'll just use 1 texture and leverage the entire thing // for now we'll just use 1 texture and leverage the entire thing
const texture_cell_count: XY(u16) = getD3d11TextureMaxCellCount(font.cell_size); const texture_cell_count: XY(u16) = getD3d11TextureMaxCellCount(font.cell_size);
const texture_cell_count_total: u32 = const texture_cell_count_total: u32 =
@ -184,6 +184,11 @@ pub const WindowState = struct {
break :blk &(state.glyph_index_cache.?); break :blk &(state.glyph_index_cache.?);
}; };
const right_half: bool = switch (kind) {
.single, .left => false,
.right => true,
};
switch (glyph_index_cache.reserve( switch (glyph_index_cache.reserve(
global.glyph_cache_arena.allocator(), global.glyph_cache_arena.allocator(),
codepoint, codepoint,
@ -194,8 +199,11 @@ pub const WindowState = struct {
// defer if (!render_success) state.glyph_index_cache.remove(reserved.index); // defer if (!render_success) state.glyph_index_cache.remove(reserved.index);
const pos: XY(u16) = cellPosFromIndex(reserved.index, texture_cell_count.x); const pos: XY(u16) = cellPosFromIndex(reserved.index, texture_cell_count.x);
const coord = coordFromCellPos(font.cell_size, pos); const coord = coordFromCellPos(font.cell_size, pos);
var staging_size = font.cell_size; const staging_size: XY(u16) = .{
staging_size.x = if (right_half) staging_size.x * 2 else staging_size.x; // twice the width to handle double-wide glyphs
.x = font.cell_size.x * 2,
.y = font.cell_size.y,
};
const staging = global.staging_texture.update(staging_size); const staging = global.staging_texture.update(staging_size);
var utf8_buf: [7]u8 = undefined; var utf8_buf: [7]u8 = undefined;
const utf8_len: u3 = std.unicode.utf8Encode(codepoint, &utf8_buf) catch |e| std.debug.panic( const utf8_len: u3 = std.unicode.utf8Encode(codepoint, &utf8_buf) catch |e| std.debug.panic(
@ -205,6 +213,10 @@ pub const WindowState = struct {
staging.text_renderer.render( staging.text_renderer.render(
font, font,
utf8_buf[0..utf8_len], utf8_buf[0..utf8_len],
switch (kind) {
.single => false,
.left, .right => true,
},
); );
const box: win32.D3D11_BOX = .{ const box: win32.D3D11_BOX = .{
.left = if (right_half) font.cell_size.x else 0, .left = if (right_half) font.cell_size.x else 0,
@ -292,7 +304,7 @@ pub fn paint(
} }
const copy_col_count: u16 = @min(col_count, shader_col_count); const copy_col_count: u16 = @min(col_count, shader_col_count);
const blank_space_glyph_index = state.generateGlyph(font, ' ', false); const blank_space_glyph_index = state.generateGlyph(font, ' ', .single);
const cell_count: u32 = @as(u32, shader_col_count) * @as(u32, shader_row_count); const cell_count: u32 = @as(u32, shader_col_count) * @as(u32, shader_row_count);
state.shader_cells.updateCount(cell_count); state.shader_cells.updateCount(cell_count);

View file

@ -1083,6 +1083,7 @@ fn WndProc(
) catch |e| oom(e); ) catch |e| oom(e);
var prev_width: usize = 1; var prev_width: usize = 1;
var prev_cell: render.Cell = undefined; var prev_cell: render.Cell = undefined;
var prev_codepoint: u21 = undefined;
for (global.screen.buf, global.render_cells.items) |*screen_cell, *render_cell| { for (global.screen.buf, global.render_cells.items) |*screen_cell, *render_cell| {
const width = screen_cell.char.width; const width = screen_cell.char.width;
const codepoint = if (std.unicode.utf8ValidateSlice(screen_cell.char.grapheme)) const codepoint = if (std.unicode.utf8ValidateSlice(screen_cell.char.grapheme))
@ -1091,16 +1092,17 @@ fn WndProc(
std.unicode.replacement_character; std.unicode.replacement_character;
if (prev_width > 1) { if (prev_width > 1) {
render_cell.* = prev_cell; render_cell.* = prev_cell;
render_cell.glyph_index = state.render_state.generateGlyph(font, codepoint, true); render_cell.glyph_index = state.render_state.generateGlyph(font, prev_codepoint, .right);
} else { } else {
render_cell.* = .{ render_cell.* = .{
.glyph_index = state.render_state.generateGlyph(font, codepoint, false), .glyph_index = state.render_state.generateGlyph(font, codepoint, if (width == 1) .single else .left),
.background = renderColorFromVaxis(screen_cell.style.bg), .background = renderColorFromVaxis(screen_cell.style.bg),
.foreground = renderColorFromVaxis(screen_cell.style.fg), .foreground = renderColorFromVaxis(screen_cell.style.fg),
}; };
} }
prev_width = width; prev_width = width;
prev_cell = render_cell.*; prev_cell = render_cell.*;
prev_codepoint = codepoint;
} }
render.paint( render.paint(
&state.render_state, &state.render_state,