╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Title.zig ────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
476 '-' => .{ 477 "......", 478 "......", 479 "......", 480 "......", 481 ".####.", 482 "......", 483 "......", 484 "......", 485 "......", 486 }, 487 '.' => .{ 488 "......", 489 "......", 490 "......", 491 "......", 492 "......", 493 "......", 494 "......", 495 "..##..", 496 "..##..", 497 }, 498 else => null, 499 }; 500 } 501 502 // map a 2x3 sub-pixel pattern to its corresponding sextant codepoint 503 fn sextantCodepoint(pattern: u6) u21 { 504 return switch (pattern) { 505 0b000000 => ' ', 506 0b010101 => 0x258C, // ▌ left half block 507 0b101010 => 0x2590, // ▐ right half block 508 0b111111 => 0x2588, // █ full block 509 else => blk: { 510 var offset: u21 = @as(u21, pattern) - 1; 511 if (pattern > 0b010101) offset -= 1; 512 if (pattern > 0b101010) offset -= 1; 513 break :blk 0x1FB00 + offset; 514 }, 515 }; 516 } 517 518 fn renderTitle(allocator: std.mem.Allocator, input: []const u8) ![]const u8 { 519 var rows: [3]std.ArrayList(u8) = .{ .empty, .empty, .empty }; 520 defer for (&rows) |*r| r.deinit(allocator); 521 522 var first = true; 523 for (input) |c| { 524 const glyph = glyphFor(c) orelse continue; 525 526 // row-aligned dither: keep on-pixels only at even bitmap rows. 527 var kept: [9][6]bool = std.mem.zeroes([9][6]bool); 528 for (0..9) |r| { 529 if (r % 2 != 0) continue; 530 for (0..6) |col| { 531 kept[r][col] = glyph[r][col] == '#'; 532 } 533 } 534 535 if (!first) { 536 for (&rows) |*r| try r.append(allocator, ' '); 537 } 538 first = false; 539 540 for (0..3) |text_row| { 541 for (0..3) |text_col| { 542 var pattern: u6 = 0; 543 for (0..3) |sub_row| { 544 for (0..2) |sub_col| { 545 const row_idx = text_row * 3 + sub_row; 546 const col_idx = text_col * 2 + sub_col; 547 if (kept[row_idx][col_idx]) { 548 pattern |= @as(u6, 1) << @intCast(sub_row * 2 + sub_col); 549 } 550 } 551 } 552 const cp = sextantCodepoint(pattern); 553 var utf8_buf: [4]u8 = undefined; 554 const len = try std.unicode.utf8Encode(cp, &utf8_buf); 555 try rows[text_row].appendSlice(allocator, utf8_buf[0..len]); 556 } 557 } 558 } 559 560 var out: std.ArrayList(u8) = .empty; 561 errdefer out.deinit(allocator); 562 try out.appendSlice(allocator, rows[0].items); 563 try out.append(allocator, '\n'); 564 try out.appendSlice(allocator, rows[1].items); 565 try out.append(allocator, '\n'); 566 try out.appendSlice(allocator, rows[2].items); 567 return out.toOwnedSlice(allocator); 568 } 569