1use crate::canvas::Canvas;
2use crate::color::CanvasColor;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct RowCell {
7 pub glyph: char,
9 pub color: CanvasColor,
11}
12
13pub type RowBuffer = Vec<RowCell>;
15
16pub trait GraphicsArea {
21 fn nrows(&self) -> usize;
23
24 fn ncols(&self) -> usize;
26
27 fn blank_char(&self) -> char {
29 ' '
30 }
31
32 fn prepare_render(&mut self) {}
34
35 fn render_row(&self, row: usize, out: &mut RowBuffer);
37
38 fn finish_render(&mut self) {}
40}
41
42impl<C: Canvas> GraphicsArea for C {
43 fn nrows(&self) -> usize {
44 self.char_height()
45 }
46
47 fn ncols(&self) -> usize {
48 self.char_width()
49 }
50
51 fn render_row(&self, row: usize, out: &mut RowBuffer) {
52 out.clear();
53 out.extend(
54 self.row_cells(row)
55 .map(|(glyph, color)| RowCell { glyph, color }),
56 );
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::{GraphicsArea, RowBuffer};
63 use crate::canvas::{
64 AsciiCanvas, BlockCanvas, BrailleCanvas, Canvas, DensityCanvas, DotCanvas,
65 };
66 use crate::color::CanvasColor;
67
68 #[test]
69 fn canvas_graphics_area_render_row_matches_glyph_and_color_cells() {
70 let mut canvas = BrailleCanvas::new(1, 1, 0.0, 0.0, 1.0, 1.0);
71 canvas.pixel(0, 0, CanvasColor::BLUE);
72 canvas.pixel(1, 3, CanvasColor::RED);
73
74 let mut row = RowBuffer::new();
75 canvas.render_row(0, &mut row);
76
77 assert_eq!(row.len(), 1);
78 assert_eq!(row[0].glyph, '⢁');
79 assert_eq!(row[0].color, CanvasColor::MAGENTA);
80 }
81
82 #[test]
83 fn canvas_graphics_area_reports_dimensions() {
84 let canvas = BrailleCanvas::new(4, 3, 0.0, 0.0, 1.0, 1.0);
85 assert_eq!(canvas.ncols(), 4);
86 assert_eq!(canvas.nrows(), 3);
87 }
88
89 #[test]
90 fn all_canvas_types_render_rows_through_graphics_area() {
91 fn assert_single_cell<C: Canvas + GraphicsArea>(mut canvas: C) {
92 canvas.pixel(0, 0, CanvasColor::GREEN);
93 let mut row = RowBuffer::new();
94 canvas.render_row(0, &mut row);
95 assert_eq!(row.len(), 1);
96 assert_eq!(row[0].color, CanvasColor::GREEN);
97 }
98
99 assert_single_cell(BrailleCanvas::new(1, 1, 0.0, 0.0, 1.0, 1.0));
100 assert_single_cell(BlockCanvas::new(1, 1, 0.0, 0.0, 1.0, 1.0));
101 assert_single_cell(AsciiCanvas::new(1, 1, 0.0, 0.0, 1.0, 1.0));
102 assert_single_cell(DotCanvas::new(1, 1, 0.0, 0.0, 1.0, 1.0));
103 assert_single_cell(DensityCanvas::new(1, 1, 0.0, 0.0, 1.0, 1.0));
104 }
105
106 #[test]
107 fn render_row_reuses_buffer_by_replacing_previous_contents() {
108 let mut canvas = DotCanvas::new(2, 1, 0.0, 0.0, 1.0, 1.0);
109 canvas.pixel(0, 0, CanvasColor::YELLOW);
110 canvas.pixel(1, 0, CanvasColor::CYAN);
111
112 let mut row = RowBuffer::new();
113 row.push(super::RowCell {
114 glyph: 'x',
115 color: CanvasColor::RED,
116 });
117
118 canvas.render_row(0, &mut row);
119
120 assert_eq!(row.len(), 2);
121 assert_eq!(row[0].glyph, '\'');
122 assert_eq!(row[0].color, CanvasColor::YELLOW);
123 assert_eq!(row[1].glyph, '\'');
124 assert_eq!(row[1].color, CanvasColor::CYAN);
125 }
126
127 #[test]
128 fn graphics_area_default_hooks_are_noops() {
129 let mut canvas = BlockCanvas::new(1, 1, 0.0, 0.0, 1.0, 1.0);
130 assert_eq!(canvas.blank_char(), ' ');
131
132 canvas.prepare_render();
133 canvas.finish_render();
134
135 let mut row = RowBuffer::new();
136 canvas.render_row(0, &mut row);
137 assert_eq!(row.len(), 1);
138 }
139}