1#[cfg(not(feature = "std"))]
4#[allow(unused_imports)]
5use alloc::{
6 borrow::ToOwned,
7 format,
8 string::{String, ToString},
9 vec,
10 vec::Vec,
11};
12
13use crate::{Canvas as RenderCanvas, Color, Pixel};
14
15macro_rules! impl_bit_canvas {
21 ($canvas:ident, $pixel:ident, $row_group:expr, $col_step:expr, $encode:expr) => {
22 #[doc(hidden)]
23 pub struct $canvas {
24 canvas: Vec<u8>,
25 width: u32,
26 dark_pixel: u8,
27 }
28
29 impl RenderCanvas for $canvas {
30 type Pixel = $pixel;
31 type Image = String;
32
33 fn new(width: u32, height: u32, dark_pixel: $pixel, light_pixel: $pixel) -> Self {
34 let a = vec![light_pixel.value(); (width * height) as usize];
35 $canvas { width, canvas: a, dark_pixel: dark_pixel.value() }
36 }
37
38 fn draw_dark_pixel(&mut self, x: u32, y: u32) {
39 self.canvas[(x + y * self.width) as usize] = self.dark_pixel;
40 }
41
42 fn into_image(self) -> String {
43 let w = self.width as usize;
44 let data = &self.canvas;
45 let row_group = $row_group;
46 let empty: &[u8] = &[];
47 let col_step: usize = $col_step;
48 let row_count = data.len() / w;
49 let output_rows = row_count.div_ceil(row_group);
50 let output_cols = w.div_ceil(col_step);
51 let mut out = String::with_capacity(output_rows * (output_cols + 1));
52
53 for group_start in (0..row_count).step_by(row_group) {
54 let actual = row_group.min(row_count - group_start);
55 let mut group: [&[u8]; $row_group] = [empty; $row_group];
56 for i in 0..actual {
57 let row_start = (group_start + i) * w;
58 group[i] = &data[row_start..row_start + w];
59 }
60 for col in (0..w).step_by(col_step) {
61 out.push_str($encode(&group, col));
62 }
63 out.push('\n');
64 }
65 if out.ends_with('\n') {
66 out.pop();
67 }
68 out
69 }
70 }
71 };
72}
73
74const CODEPAGE: [&str; 4] = [" ", "\u{2584}", "\u{2580}", "\u{2588}"];
78
79#[derive(Copy, Clone, PartialEq, Eq)]
82pub enum Dense1x2 {
83 Dark,
85 Light,
87}
88
89impl Pixel for Dense1x2 {
90 type Image = String;
91 type Canvas = Canvas1x2;
92 fn default_unit_size() -> (u32, u32) {
93 (1, 1)
94 }
95 fn default_color(color: Color) -> Dense1x2 {
96 color.select(Dense1x2::Dark, Dense1x2::Light)
97 }
98}
99
100impl Dense1x2 {
101 const fn value(self) -> u8 {
102 match self {
103 Dense1x2::Dark => 1,
104 Dense1x2::Light => 0,
105 }
106 }
107}
108
109fn encode_1x2(rows: &[&[u8]], col: usize) -> &'static str {
110 let top = rows[0].get(col).copied().unwrap_or(0);
111 let bot = rows[1].get(col).copied().unwrap_or(0);
112 CODEPAGE[usize::from(top * 2 + bot)]
113}
114
115impl_bit_canvas!(Canvas1x2, Dense1x2, 2, 1, encode_1x2 as fn(&[&[u8]], usize) -> &'static str);
116
117const QUADRANT: [&str; 16] = [
123 " ", "\u{2598}", "\u{259D}", "\u{2580}", "\u{2596}", "\u{258C}", "\u{259E}", "\u{259B}", "\u{2597}", "\u{259A}", "\u{2590}", "\u{259C}", "\u{2584}", "\u{2599}", "\u{259F}", "\u{2588}", ];
140
141#[derive(Copy, Clone, PartialEq, Eq)]
144pub enum Dense2x2 {
145 Dark,
147 Light,
149}
150
151impl Pixel for Dense2x2 {
152 type Image = String;
153 type Canvas = Canvas2x2;
154 fn default_unit_size() -> (u32, u32) {
155 (1, 1)
156 }
157 fn default_color(color: Color) -> Dense2x2 {
158 color.select(Dense2x2::Dark, Dense2x2::Light)
159 }
160}
161
162impl Dense2x2 {
163 const fn value(self) -> u8 {
164 match self {
165 Dense2x2::Dark => 1,
166 Dense2x2::Light => 0,
167 }
168 }
169}
170
171fn encode_2x2(rows: &[&[u8]], col: usize) -> &'static str {
172 let tl = rows[0][col] & 1;
173 let tr = rows[0].get(col + 1).copied().unwrap_or(0) & 1;
174 let bl = rows[1].get(col).copied().unwrap_or(0) & 1;
175 let br = rows[1].get(col + 1).copied().unwrap_or(0) & 1;
176 QUADRANT[(tl | (tr << 1) | (bl << 2) | (br << 3)) as usize]
177}
178
179impl_bit_canvas!(Canvas2x2, Dense2x2, 2, 2, encode_2x2 as fn(&[&[u8]], usize) -> &'static str);
180
181#[derive(Copy, Clone, PartialEq, Eq)]
202pub enum Braille {
203 Dark,
205 Light,
207}
208
209impl Pixel for Braille {
210 type Image = String;
211 type Canvas = CanvasBraille;
212 fn default_unit_size() -> (u32, u32) {
213 (1, 1)
214 }
215 fn default_color(color: Color) -> Braille {
216 color.select(Braille::Dark, Braille::Light)
217 }
218}
219
220impl Braille {
221 const fn value(self) -> u8 {
222 match self {
223 Braille::Dark => 1,
224 Braille::Light => 0,
225 }
226 }
227}
228
229const BRAILLE_UTF8: [[u8; 3]; 256] = {
231 let mut table = [[0u8; 3]; 256];
232 let mut i = 0usize;
233 while i < 256 {
234 let cp = 0x2800u32 + i as u32;
235 table[i][0] = ((cp >> 12) & 0x0F) as u8 | 0xE0;
236 table[i][1] = ((cp >> 6) & 0x3F) as u8 | 0x80;
237 table[i][2] = (cp & 0x3F) as u8 | 0x80;
238 i += 1;
239 }
240 table
241};
242
243fn encode_braille(rows: &[&[u8]], col: usize) -> &'static str {
244 let d1 = rows[0].get(col).copied().unwrap_or(0) & 1;
245 let d2 = rows[1].get(col).copied().unwrap_or(0) & 1;
246 let d3 = rows[2].get(col).copied().unwrap_or(0) & 1;
247 let d4 = rows[0].get(col + 1).copied().unwrap_or(0) & 1;
248 let d5 = rows[1].get(col + 1).copied().unwrap_or(0) & 1;
249 let d6 = rows[2].get(col + 1).copied().unwrap_or(0) & 1;
250 let d7 = rows[3].get(col).copied().unwrap_or(0) & 1;
251 let d8 = rows[3].get(col + 1).copied().unwrap_or(0) & 1;
252
253 let bits = d1 | (d2 << 1) | (d3 << 2) | (d4 << 3) | (d5 << 4) | (d6 << 5) | (d7 << 6) | (d8 << 7);
254 unsafe { core::str::from_utf8_unchecked(&BRAILLE_UTF8[bits as usize]) }
256}
257
258impl_bit_canvas!(CanvasBraille, Braille, 4, 2, encode_braille as fn(&[&[u8]], usize) -> &'static str);
259
260#[derive(Copy, Clone, PartialEq, Eq)]
289pub enum Dense3x2 {
290 Dark,
292 Light,
294}
295
296impl Pixel for Dense3x2 {
297 type Image = String;
298 type Canvas = Canvas3x2;
299 fn default_unit_size() -> (u32, u32) {
300 (1, 1)
301 }
302 fn default_color(color: Color) -> Dense3x2 {
303 color.select(Dense3x2::Dark, Dense3x2::Light)
304 }
305}
306
307impl Dense3x2 {
308 const fn value(self) -> u8 {
309 match self {
310 Dense3x2::Dark => 1,
311 Dense3x2::Light => 0,
312 }
313 }
314}
315
316const SEXTANT_UTF8: [[u8; 4]; 64] = {
319 let mut table = [[0u8; 4]; 64];
320 let mut i = 0usize;
321 while i < 64 {
322 let cp = 0x1FB00u32 + i as u32;
323 table[i][0] = 0xF0u8 | ((cp >> 18) & 0x07) as u8;
324 table[i][1] = 0x80u8 | ((cp >> 12) & 0x3F) as u8;
325 table[i][2] = 0x80u8 | ((cp >> 6) & 0x3F) as u8;
326 table[i][3] = 0x80u8 | (cp & 0x3F) as u8;
327 i += 1;
328 }
329 table
330};
331
332fn encode_3x2(rows: &[&[u8]], col: usize) -> &'static str {
335 let d0 = rows[0].get(col).copied().unwrap_or(0) & 1;
336 let d1 = rows[1].get(col).copied().unwrap_or(0) & 1;
337 let d2 = rows[2].get(col).copied().unwrap_or(0) & 1;
338 let d3 = rows[0].get(col + 1).copied().unwrap_or(0) & 1;
339 let d4 = rows[1].get(col + 1).copied().unwrap_or(0) & 1;
340 let d5 = rows[2].get(col + 1).copied().unwrap_or(0) & 1;
341
342 let bits = d0 | (d1 << 1) | (d2 << 2) | (d3 << 3) | (d4 << 4) | (d5 << 5);
343 if bits == 0 {
344 " "
345 } else {
346 unsafe { core::str::from_utf8_unchecked(&SEXTANT_UTF8[bits as usize]) }
348 }
349}
350
351impl_bit_canvas!(Canvas3x2, Dense3x2, 3, 2, encode_3x2 as fn(&[&[u8]], usize) -> &'static str);
352
353#[test]
356fn test_render_to_utf8_string() {
357 use crate::Renderer;
358 let colors = &[Color::Dark, Color::Light, Color::Light, Color::Dark];
359 let image: String = Renderer::<Dense1x2>::new(colors, 2, 1).build();
360
361 assert_eq!(&image, " ▄ \n ▀ ");
362
363 let image2 = Renderer::<Dense1x2>::new(colors, 2, 1).module_dimensions(2, 2).build();
364
365 assert_eq!(&image2, " \n ██ \n ██ \n ");
366}
367
368#[test]
369fn integration_render_utf8_1x2() {
370 use crate::Renderer;
371 use crate::unicode::Dense1x2;
372
373 let colors = [Color::Dark, Color::Light, Color::Light, Color::Dark];
374 let image = Renderer::<Dense1x2>::new(&colors, 2, 0).module_dimensions(1, 1).build();
375 assert_eq!(image, "▀▄");
376}
377
378#[test]
379fn integration_render_utf8_1x2_inverted() {
380 use crate::Renderer;
381 use crate::unicode::Dense1x2;
382
383 let colors = [Color::Dark, Color::Light, Color::Light, Color::Dark];
384 let image = Renderer::<Dense1x2>::new(&colors, 2, 0)
385 .dark_color(Dense1x2::Light)
386 .light_color(Dense1x2::Dark)
387 .module_dimensions(1, 1)
388 .build();
389 assert_eq!(image, "▄▀");
390}
391
392#[test]
393fn test_dense2x2_basic() {
394 use crate::Renderer;
395 let colors = &[Color::Dark, Color::Light, Color::Light, Color::Dark];
396 let image: String = Renderer::<Dense2x2>::new(colors, 2, 0).module_dimensions(1, 1).build();
397 assert_eq!(&image, "\u{259A}");
398}
399
400#[test]
401fn test_dense2x2_with_quiet_zone() {
402 use crate::Renderer;
403 let colors = &[Color::Dark, Color::Light, Color::Light, Color::Dark];
404 let image: String = Renderer::<Dense2x2>::new(colors, 2, 1).build();
405 assert!(image.chars().count() >= 1);
406}
407
408#[test]
409fn test_dense2x2_all_dark() {
410 use crate::Renderer;
411 let colors = vec![Color::Dark; 4];
412 let image: String = Renderer::<Dense2x2>::new(&colors, 2, 0).module_dimensions(1, 1).build();
413 assert_eq!(&image, "\u{2588}");
414}
415
416#[test]
417fn test_dense2x2_all_light() {
418 use crate::Renderer;
419 let colors = vec![Color::Light; 4];
420 let image: String = Renderer::<Dense2x2>::new(&colors, 2, 0).module_dimensions(1, 1).build();
421 assert_eq!(&image, " ");
422}
423
424#[test]
425fn integration_render_utf8_2x2() {
426 use crate::Renderer;
427 use crate::unicode::Dense2x2;
428
429 let colors = vec![
430 Color::Dark,
431 Color::Light,
432 Color::Light,
433 Color::Dark,
434 Color::Light,
435 Color::Dark,
436 Color::Dark,
437 Color::Light,
438 Color::Dark,
439 Color::Dark,
440 Color::Light,
441 Color::Light,
442 Color::Light,
443 Color::Light,
444 Color::Dark,
445 Color::Dark,
446 ];
447 let image = Renderer::<Dense2x2>::new(&colors, 4, 0).module_dimensions(1, 1).build();
448 assert!(!image.is_empty());
449 let dense1x2 = Renderer::<Dense1x2>::new(&colors, 4, 0).module_dimensions(1, 1).build();
450 assert!(image.len() < dense1x2.len());
451}
452
453#[test]
454fn test_braille_all_dark() {
455 use crate::Renderer;
456 let colors = vec![Color::Dark; 16];
457 let image: String = Renderer::<Braille>::new(&colors, 4, 0).module_dimensions(1, 1).build();
458 assert_eq!(&image, "\u{28FF}\u{28FF}");
459}
460
461#[test]
462fn test_braille_all_light() {
463 use crate::Renderer;
464 let colors = vec![Color::Light; 16];
465 let image: String = Renderer::<Braille>::new(&colors, 4, 0).module_dimensions(1, 1).build();
466 assert_eq!(&image, "\u{2800}\u{2800}");
467}
468
469#[test]
470fn test_braille_top_left_dot() {
471 use crate::Renderer;
472 let mut colors = vec![Color::Light; 16];
473 colors[0] = Color::Dark;
474 let image: String = Renderer::<Braille>::new(&colors, 4, 0).module_dimensions(1, 1).build();
475 assert_eq!(&image, "\u{2801}\u{2800}");
476}
477
478#[test]
479fn test_braille_density() {
480 use crate::Renderer;
481 use crate::unicode::{Braille, Dense1x2};
482
483 let colors = (0..64).map(|i| if i % 3 == 0 { Color::Dark } else { Color::Light }).collect::<Vec<_>>();
484 let braille = Renderer::<Braille>::new(&colors, 8, 0).module_dimensions(1, 1).build();
485 let dense1x2 = Renderer::<Dense1x2>::new(&colors, 8, 0).module_dimensions(1, 1).build();
486 assert!(braille.len() < dense1x2.len());
487}
488
489#[test]
490fn test_dense3x2_all_dark() {
491 use crate::Renderer;
492 let colors = vec![Color::Dark; 36];
494 let image: String = Renderer::<Dense3x2>::new(&colors, 6, 0).module_dimensions(1, 1).build();
495 assert_eq!(&image, "\u{1FB3F}\u{1FB3F}\u{1FB3F}\n\u{1FB3F}\u{1FB3F}\u{1FB3F}");
496}
497
498#[test]
499fn test_dense3x2_all_light() {
500 use crate::Renderer;
501 let colors = vec![Color::Light; 36];
502 let image: String = Renderer::<Dense3x2>::new(&colors, 6, 0).module_dimensions(1, 1).build();
503 assert_eq!(&image, " \n ");
504}
505
506#[test]
507fn test_dense3x2_top_left_cell() {
508 use crate::Renderer;
509 let mut colors = vec![Color::Light; 36];
511 colors[0] = Color::Dark;
512 let image: String = Renderer::<Dense3x2>::new(&colors, 6, 0).module_dimensions(1, 1).build();
513 assert!(image.starts_with('\u{1FB01}'));
515}
516
517#[test]
518fn test_dense3x2_density() {
519 use crate::Renderer;
520 use crate::unicode::{Dense1x2, Dense3x2};
521
522 let colors = (0..36).map(|i| if i % 2 == 0 { Color::Dark } else { Color::Light }).collect::<Vec<_>>();
523 let sextant = Renderer::<Dense3x2>::new(&colors, 6, 0).module_dimensions(1, 1).build();
524 let dense1x2 = Renderer::<Dense1x2>::new(&colors, 6, 0).module_dimensions(1, 1).build();
525 assert!(sextant.len() < dense1x2.len());
527}