╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ SubTitle.zig ─────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
113 "#..#", 114 "#..#", 115 "#..#", 116 "###.", 117 }, 118 'E' => .{ 119 "####", 120 "#...", 121 "###.", 122 "#...", 123 "#...", 124 "####", 125 }, 126 'F' => .{ 127 "####", 128 "#...", 129 "###.", 130 "#...", 131 "#...", 132 "#...", 133 }, 134 'G' => .{ 135 ".###", 136 "#...", 137 "#.##", 138 "#..#", 139 "#..#", 140 ".###", 141 }, 142 'H' => .{ 143 "#..#", 144 "#..#", 145 "####", 146 "#..#", 147 "#..#", 148 "#..#", 149 }, 150 'I' => .{ 151 "####", 152 ".##.", 153 ".##.", 154 ".##.", 155 ".##.", 156 "####", 157 }, 158 'J' => .{ 159 "..##", 160 "...#", 161 "...#", 162 "...#", 163 "#..#", 164 ".##.", 165 }, 166 'K' => .{ 167 "#..#", 168 "#.#.", 169 "##..", 170 "##..", 171 "#.#.", 172 "#..#", 173 }, 174 'L' => .{ 175 "#...", 176 "#...", 177 "#...", 178 "#...", 179 "#...", 180 "####", 181 }, 182 'M' => .{ 183 "#..#", 184 "####", 185 "####", 186 "#..#", 187 "#..#", 188 "#..#", 189 }, 190 'N' => .{ 191 "#..#", 192 "##.#", 193 "##.#", 194 "#.##", 195 "#.##", 196 "#..#", 197 }, 198 'O' => .{ 199 ".##.", 200 "#..#", 201 "#..#", 202 "#..#", 203 "#..#", 204 ".##.", 205 }, 206 'P' => .{ 207 "###.", 208 "#..#", 209 "###.", 210 "#...", 211 "#...", 212 "#...", 213 }, 214 'Q' => .{ 215 ".##.", 216 "#..#", 217 "#..#", 218 "#.##", 219 ".##.", 220 "...#", 221 }, 222 'R' => .{ 223 "###.", 224 "#..#", 225 "###.", 226 "##..", 227 "#.#.", 228 "#..#", 229 }, 230 'S' => .{ 231 ".###", 232 "#...", 233 ".##.", 234 "...#", 235 "#..#", 236 "###.", 237 }, 238 'T' => .{ 239 "####", 240 ".##.", 241 ".##.", 242 ".##.", 243 ".##.", 244 ".##.", 245 }, 246 'U' => .{ 247 "#..#", 248 "#..#", 249 "#..#", 250 "#..#", 251 "#..#", 252 ".##.", 253 }, 254 'V' => .{ 255 "#..#", 256 "#..#", 257 "#..#", 258 "#..#", 259 ".##.", 260 ".##.", 261 }, 262 'W' => .{ 263 "#..#", 264 "#..#", 265 "#..#", 266 "####", 267 "####", 268 ".##.", 269 }, 270 'X' => .{ 271 "#..#", 272 "#..#", 273 ".##.", 274 ".##.", 275 "#..#", 276 "#..#", 277 }, 278 'Y' => .{ 279 "#..#", 280 "#..#", 281 ".##.", 282 ".##.", 283 ".##.", 284 ".##.", 285 }, 286 'Z' => .{ 287 "####", 288 "...#", 289 "..#.", 290 ".#..", 291 "#...", 292 "####", 293 }, 294 '0' => .{ 295 ".##.", 296 "#.##", 297 "#.##", 298 "##.#", 299 "##.#", 300 ".##.", 301 }, 302 '1' => .{ 303 ".##.", 304 "###.", 305 ".##.", 306 ".##.", 307 ".##.", 308 "####", 309 }, 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