lcd_async/
test_image.rs

1use core::marker::PhantomData;
2
3use embedded_graphics_core::{
4    geometry::{AnchorPoint, AnchorX},
5    prelude::*,
6    primitives::Rectangle,
7};
8
9/// Test image.
10///
11/// The test image can be used to check if the display is working and to
12/// identify the correct orientation and color settings.
13///
14/// # Expected output
15///
16#[doc = include_str!("../assets/test_image.svg")]
17///
18/// Note that the gray border around the image above is only added to make the
19/// white border visible on the light rustdoc theme and will not be visible when
20/// the test image is drawn.
21///
22/// - There should be a one pixel white border around the display.  
23///   Modify the [display size](crate::Builder::display_size) and [display
24///   offset](crate::Builder::display_offset) settings, if at least one
25///   edge of the white border isn't drawn or if there is a gap between the
26///   white border and the edge of the display.
27/// - A white triangle should be drawn in the top left corner and the RGB label text should not be mirrored.  
28///   Modify the [orientation](crate::Builder::orientation) setting to
29///   rotate and mirror the display until the test image is displayed correctly.
30///   Note that the white triangle might not be visible on displays with rounded
31///   corners.
32/// - The colored bars should match the labels.  
33///   Use the [color inversion](crate::Builder::invert_colors) and [color
34///   order](crate::Builder::color_order) settings until the colored bars
35///   and labels match.
36#[derive(Default)]
37pub struct TestImage<C: RgbColor> {
38    color_type: PhantomData<C>,
39}
40
41impl<C: RgbColor> TestImage<C> {
42    /// Creates a new test image
43    pub const fn new() -> Self {
44        Self {
45            color_type: PhantomData,
46        }
47    }
48}
49
50const BORDER_WIDTH: u32 = 1;
51const BORDER_PADDING: u32 = 4;
52const TOP_LEFT_MARKER_SIZE: u32 = 20;
53
54impl<C: RgbColor> Drawable for TestImage<C> {
55    type Color = C;
56    type Output = ();
57
58    fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
59    where
60        D: DrawTarget<Color = Self::Color>,
61    {
62        draw_border(target, BORDER_WIDTH)?;
63
64        let color_bar_area = target
65            .bounding_box()
66            .offset(-i32::try_from(BORDER_WIDTH + BORDER_PADDING).unwrap());
67        draw_color_bars(target, &color_bar_area)?;
68
69        draw_top_left_marker(target, &color_bar_area, TOP_LEFT_MARKER_SIZE)?;
70
71        Ok(())
72    }
73}
74
75/// Draws a white border around the draw target.
76fn draw_border<D>(target: &mut D, width: u32) -> Result<(), D::Error>
77where
78    D: DrawTarget,
79    D::Color: RgbColor,
80{
81    let bounding_box = target.bounding_box();
82    let inner_box = bounding_box.offset(-i32::try_from(width).unwrap());
83
84    target.fill_contiguous(
85        &bounding_box,
86        bounding_box.points().map(|p| {
87            if inner_box.contains(p) {
88                D::Color::BLACK
89            } else {
90                D::Color::WHITE
91            }
92        }),
93    )
94}
95
96/// Draws RGB color bars and labels.
97fn draw_color_bars<D>(target: &mut D, area: &Rectangle) -> Result<(), D::Error>
98where
99    D: DrawTarget,
100    D::Color: RgbColor,
101{
102    target.fill_solid(area, RgbColor::GREEN)?;
103    Character::new(G, area.center()).draw(target)?;
104
105    let rect = area.resized_width(area.size.width / 3, AnchorX::Left);
106    target.fill_solid(&rect, RgbColor::RED)?;
107    Character::new(R, rect.center()).draw(target)?;
108
109    let rect = area.resized_width(area.size.width / 3, AnchorX::Right);
110    target.fill_solid(&rect, RgbColor::BLUE)?;
111    Character::new(B, rect.center()).draw(target)?;
112
113    Ok(())
114}
115
116// Draws a triangular marker in the top left corner.
117fn draw_top_left_marker<D>(target: &mut D, area: &Rectangle, size: u32) -> Result<(), D::Error>
118where
119    D: DrawTarget,
120    D::Color: RgbColor,
121{
122    let mut rect = area.resized(Size::new(size, 1), AnchorPoint::TopLeft);
123
124    while rect.size.width > 0 {
125        target.fill_solid(&rect, D::Color::WHITE)?;
126
127        rect.top_left.y += 1;
128        rect.size.width -= 1;
129    }
130
131    Ok(())
132}
133
134const R: &[u8] = &[
135    0, 0, 0, 0, 0, 0, 0, 0, 0, //
136    0, 0, 0, 0, 0, 0, 0, 0, 0, //
137    0, 0, 1, 1, 1, 1, 0, 0, 0, //
138    0, 0, 1, 0, 0, 0, 1, 0, 0, //
139    0, 0, 1, 0, 0, 0, 1, 0, 0, //
140    0, 0, 1, 1, 1, 1, 0, 0, 0, //
141    0, 0, 1, 0, 1, 0, 0, 0, 0, //
142    0, 0, 1, 0, 0, 1, 0, 0, 0, //
143    0, 0, 1, 0, 0, 0, 1, 0, 0, //
144    0, 0, 0, 0, 0, 0, 0, 0, 0, //
145    0, 0, 0, 0, 0, 0, 0, 0, 0, //
146];
147
148const G: &[u8] = &[
149    0, 0, 0, 0, 0, 0, 0, 0, 0, //
150    0, 0, 0, 0, 0, 0, 0, 0, 0, //
151    0, 0, 0, 1, 1, 1, 1, 0, 0, //
152    0, 0, 1, 0, 0, 0, 0, 0, 0, //
153    0, 0, 1, 0, 0, 0, 0, 0, 0, //
154    0, 0, 1, 0, 1, 1, 1, 0, 0, //
155    0, 0, 1, 0, 0, 0, 1, 0, 0, //
156    0, 0, 1, 0, 0, 0, 1, 0, 0, //
157    0, 0, 0, 1, 1, 1, 1, 0, 0, //
158    0, 0, 0, 0, 0, 0, 0, 0, 0, //
159    0, 0, 0, 0, 0, 0, 0, 0, 0, //
160];
161
162const B: &[u8] = &[
163    0, 0, 0, 0, 0, 0, 0, 0, 0, //
164    0, 0, 0, 0, 0, 0, 0, 0, 0, //
165    0, 0, 1, 1, 1, 1, 0, 0, 0, //
166    0, 0, 1, 0, 0, 0, 1, 0, 0, //
167    0, 0, 1, 0, 0, 0, 1, 0, 0, //
168    0, 0, 1, 1, 1, 1, 0, 0, 0, //
169    0, 0, 1, 0, 0, 0, 1, 0, 0, //
170    0, 0, 1, 0, 0, 0, 1, 0, 0, //
171    0, 0, 1, 1, 1, 1, 0, 0, 0, //
172    0, 0, 0, 0, 0, 0, 0, 0, 0, //
173    0, 0, 0, 0, 0, 0, 0, 0, 0, //
174];
175
176struct Character<C> {
177    data: &'static [u8],
178    center: Point,
179    color_type: PhantomData<C>,
180}
181
182impl<C> Character<C> {
183    fn new(data: &'static [u8], center: Point) -> Self {
184        Self {
185            data,
186            center,
187            color_type: PhantomData,
188        }
189    }
190}
191
192impl<C: RgbColor> Drawable for Character<C> {
193    type Color = C;
194    type Output = ();
195
196    fn draw<D>(&self, target: &mut D) -> Result<(), D::Error>
197    where
198        D: DrawTarget<Color = Self::Color>,
199    {
200        let rect = Rectangle::with_center(self.center, Size::new(9, 11));
201
202        target.fill_contiguous(
203            &rect,
204            self.data.iter().map(|d| {
205                if *d == 0 {
206                    RgbColor::BLACK
207                } else {
208                    RgbColor::WHITE
209                }
210            }),
211        )
212    }
213}