diff --git a/src/render/software.zig b/src/render/software.zig index 1872142..6215fc6 100644 --- a/src/render/software.zig +++ b/src/render/software.zig @@ -102,19 +102,70 @@ pub const SoftwareRenderer = struct { const clip = self.getClip(); - // Simple text rendering - character by character + // UTF-8 text rendering - decode codepoints properly var x = t.x; - for (t.text) |char| { - if (char == '\n') { - // TODO: Handle newlines - continue; - } - + var i: usize = 0; + while (i < t.text.len) { // Check if character is visible if (x >= clip.right()) break; + // Decode UTF-8 codepoint + const byte = t.text[i]; + var codepoint: u21 = undefined; + var bytes_consumed: usize = 1; + + if (byte < 0x80) { + // ASCII (0x00-0x7F) + codepoint = byte; + } else if (byte < 0xC0) { + // Invalid start byte (continuation byte), skip + i += 1; + continue; + } else if (byte < 0xE0) { + // 2-byte sequence (0xC0-0xDF) + if (i + 1 < t.text.len) { + codepoint = (@as(u21, byte & 0x1F) << 6) | + @as(u21, t.text[i + 1] & 0x3F); + bytes_consumed = 2; + } else { + i += 1; + continue; + } + } else if (byte < 0xF0) { + // 3-byte sequence (0xE0-0xEF) + if (i + 2 < t.text.len) { + codepoint = (@as(u21, byte & 0x0F) << 12) | + (@as(u21, t.text[i + 1] & 0x3F) << 6) | + @as(u21, t.text[i + 2] & 0x3F); + bytes_consumed = 3; + } else { + i += 1; + continue; + } + } else { + // 4-byte sequence (0xF0-0xF7) - beyond Latin-1, skip + bytes_consumed = 4; + i += bytes_consumed; + x += @as(i32, @intCast(font.charWidth())); // placeholder space + continue; + } + + i += bytes_consumed; + + // Handle newlines + if (codepoint == '\n') { + continue; + } + + // Convert codepoint to Latin-1 for rendering + // Latin-1 covers 0x00-0xFF directly + const char_to_render: u8 = if (codepoint <= 0xFF) + @intCast(codepoint) + else + '?'; // Replacement for chars outside Latin-1 + // Render character - font.drawChar(self.framebuffer, x, t.y, char, t.color, clip); + font.drawChar(self.framebuffer, x, t.y, char_to_render, t.color, clip); x += @as(i32, @intCast(font.charWidth())); }