╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ SubTitle.zig ─────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
310 '2' => .{ 311 ".##.", 312 "#..#", 313 "..#.", 314 ".#..", 315 "#...", 316 "####", 317 }, 318 '3' => .{ 319 "###.", 320 "...#", 321 ".##.", 322 "...#", 323 "#..#", 324 "###.", 325 }, 326 '4' => .{ 327 "..#.", 328 ".##.", 329 "#.#.", 330 "####", 331 "..#.", 332 "..#.", 333 }, 334 '5' => .{ 335 "####", 336 "#...", 337 "###.", 338 "...#", 339 "#..#", 340 "###.", 341 }, 342 '6' => .{ 343 ".##.", 344 "#...", 345 "###.", 346 "#..#", 347 "#..#", 348 ".##.", 349 }, 350 '7' => .{ 351 "####", 352 "...#", 353 "..#.", 354 "..#.", 355 ".#..", 356 ".#..", 357 }, 358 '8' => .{ 359 ".##.", 360 "#..#", 361 ".##.", 362 "#..#", 363 "#..#", 364 ".##.", 365 }, 366 '9' => .{ 367 ".##.", 368 "#..#", 369 "#..#", 370 ".###", 371 "...#", 372 ".##.", 373 }, 374 '-' => .{ 375 "....", 376 "....", 377 ".##.", 378 ".##.", 379 "....", 380 "....", 381 }, 382 '.' => .{ 383 "....", 384 "....", 385 "....", 386 "....", 387 ".##.", 388 ".##.", 389 }, 390 else => null, 391 }; 392 } 393 394 // whether every char of `text` has a glyph 395 pub fn covers(text: []const u8) bool { 396 for (text) |c| { 397 if (glyphFor(c) == null) return false; 398 } 399 return true; 400 } 401 402 // map a 2x3 sub-pixel pattern to its corresponding sextant codepoint 403 fn sextantCodepoint(pattern: u6) u21 { 404 return switch (pattern) { 405 0b000000 => ' ', 406 0b010101 => 0x258C, // ▌ left half block 407 0b101010 => 0x2590, // ▐ right half block 408 0b111111 => 0x2588, // █ full block 409 else => blk: { 410 var offset: u21 = @as(u21, pattern) - 1; 411 if (pattern > 0b010101) offset -= 1; 412 if (pattern > 0b101010) offset -= 1; 413 break :blk 0x1FB00 + offset; 414 }, 415 }; 416 } 417 418 fn renderSubTitle(allocator: std.mem.Allocator, input: []const u8) ![]const u8 { 419 var rows: [2]std.ArrayList(u8) = .{ .empty, .empty }; 420 defer for (&rows) |*r| r.deinit(allocator); 421 422 var first = true; 423 for (input) |c| { 424 const glyph = glyphFor(c) orelse continue; 425 426 var on: [6][4]bool = std.mem.zeroes([6][4]bool); 427 for (0..6) |r| { 428 for (0..4) |col| { 429 on[r][col] = glyph[r][col] == '#'; 430 } 431 } 432 433 if (!first) { 434 for (&rows) |*r| try r.append(allocator, ' '); 435 } 436 first = false; 437 438 for (0..2) |text_row| { 439 for (0..2) |text_col| { 440 var pattern: u6 = 0; 441 for (0..3) |sub_row| { 442 for (0..2) |sub_col| { 443 const row_idx = text_row * 3 + sub_row; 444 const col_idx = text_col * 2 + sub_col; 445 if (on[row_idx][col_idx]) { 446 pattern |= @as(u6, 1) << @intCast(sub_row * 2 + sub_col); 447 } 448 } 449 } 450 const cp = sextantCodepoint(pattern); 451 var utf8_buf: [4]u8 = undefined; 452 const len = try std.unicode.utf8Encode(cp, &utf8_buf); 453 try rows[text_row].appendSlice(allocator, utf8_buf[0..len]); 454 } 455 } 456 } 457 458 var out: std.ArrayList(u8) = .empty; 459 errdefer out.deinit(allocator); 460 try out.appendSlice(allocator, rows[0].items); 461 try out.append(allocator, '\n'); 462 try out.appendSlice(allocator, rows[1].items); 463 return out.toOwnedSlice(allocator); 464 } 465