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