╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ SubTitle.zig ─────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯
370 "....", 371 "....", 372 ".##.", 373 ".##.", 374 "....", 375 "....", 376 }, 377 '.' => .{ 378 "....", 379 "....", 380 "....", 381 "....", 382 ".##.", 383 ".##.", 384 }, 385 '?' => .{ 386 ".##.", 387 "#..#", 388 "..#.", 389 ".#..", 390 "....", 391 ".#..", 392 }, 393 '!' => .{ 394 ".##.", 395 ".##.", 396 ".##.", 397 ".##.", 398 "....", 399 ".##.", 400 }, 401 '\'' => .{ 402 ".##.", 403 "..#.", 404 "....", 405 "....", 406 "....", 407 "....", 408 }, 409 ',' => .{ 410 "....", 411 "....", 412 "....", 413 "....", 414 ".##.", 415 ".#..", 416 }, 417 ':' => .{ 418 "....", 419 ".##.", 420 "....", 421 "....", 422 ".##.", 423 "....", 424 }, 425 '/' => .{ 426 "...#", 427 "...#", 428 "..#.", 429 ".#..", 430 "#...", 431 "#...", 432 }, 433 else => null, 434 }; 435 } 436 437 // whether every char of `text` has a glyph 438 pub fn covers(text: []const u8) bool { 439 for (text) |c| { 440 if (glyphFor(c) == null) return false; 441 } 442 return true; 443 } 444 445 // map a 2x3 sub-pixel pattern to its corresponding sextant codepoint 446 fn sextantCodepoint(pattern: u6) u21 { 447 return switch (pattern) { 448 0b000000 => ' ', 449 0b010101 => 0x258C, // ▌ left half block 450 0b101010 => 0x2590, // ▐ right half block 451 0b111111 => 0x2588, // █ full block 452 else => blk: { 453 var offset: u21 = @as(u21, pattern) - 1; 454 if (pattern > 0b010101) offset -= 1; 455 if (pattern > 0b101010) offset -= 1; 456 break :blk 0x1FB00 + offset; 457 }, 458 }; 459 } 460 461 fn renderSubTitle(allocator: std.mem.Allocator, input: []const u8) ![]const u8 { 462 var rows: [2]std.ArrayList(u8) = .{ .empty, .empty }; 463 defer for (&rows) |*r| r.deinit(allocator); 464 465 var first = true; 466 for (input) |c| { 467 const glyph = glyphFor(c) orelse continue; 468 469 var on: [6][4]bool = std.mem.zeroes([6][4]bool); 470 for (0..6) |r| { 471 for (0..4) |col| { 472 on[r][col] = glyph[r][col] == '#'; 473 } 474 } 475 476 if (!first) { 477 for (&rows) |*r| try r.append(allocator, ' '); 478 } 479 first = false; 480 481 for (0..2) |text_row| { 482 for (0..2) |text_col| { 483 var pattern: u6 = 0; 484 for (0..3) |sub_row| { 485 for (0..2) |sub_col| { 486 const row_idx = text_row * 3 + sub_row; 487 const col_idx = text_col * 2 + sub_col; 488 if (on[row_idx][col_idx]) { 489 pattern |= @as(u6, 1) << @intCast(sub_row * 2 + sub_col); 490 } 491 } 492 } 493 const cp = sextantCodepoint(pattern); 494 var utf8_buf: [4]u8 = undefined; 495 const len = try std.unicode.utf8Encode(cp, &utf8_buf); 496 try rows[text_row].appendSlice(allocator, utf8_buf[0..len]); 497 } 498 } 499 } 500 501 var out: std.ArrayList(u8) = .empty; 502 errdefer out.deinit(allocator); 503 try out.appendSlice(allocator, rows[0].items); 504 try out.append(allocator, '\n'); 505 try out.appendSlice(allocator, rows[1].items); 506 return out.toOwnedSlice(allocator); 507 } 508