embedded_graphics_core/
drawable.rs

1//! `Drawable` trait and helpers
2use crate::{draw_target::DrawTarget, geometry::Point, pixelcolor::PixelColor};
3
4/// Marks an object as "drawable". Must be implemented for all graphics objects
5///
6/// The `Drawable` trait describes how a particular graphical object is drawn. A `Drawable` object
7/// can define its `draw` method as a collection of graphical primitives or as an iterator
8/// over pixels being rendered with [`DrawTarget`]'s [`draw_iter`] method.
9///
10/// ```rust
11/// use embedded_graphics::{
12///     mono_font::{ascii::FONT_6X9, MonoTextStyle},
13///     pixelcolor::{BinaryColor, PixelColor, Rgb888},
14///     prelude::*,
15///     primitives::{Rectangle, PrimitiveStyle},
16///     text::Text,
17/// };
18///
19/// struct Button<'a, C: PixelColor> {
20///     top_left: Point,
21///     size: Size,
22///     bg_color: C,
23///     fg_color: C,
24///     text: &'a str,
25/// }
26///
27/// impl<C> Drawable for Button<'_, C>
28/// where
29///     C: PixelColor + From<BinaryColor>,
30/// {
31///     type Color = C;
32///     type Output = ();
33///
34///     fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
35///     where
36///         D: DrawTarget<Color = C>,
37///     {
38///         Rectangle::new(self.top_left, self.size)
39///             .into_styled(PrimitiveStyle::with_fill(self.bg_color))
40///             .draw(target)?;
41///
42///         let style = MonoTextStyle::new(&FONT_6X9, self.fg_color);
43///
44///         Text::new(self.text, Point::new(6, 13), style).draw(target)?;
45///
46///         Ok(())
47///     }
48/// }
49///
50/// let mut button = Button {
51///     top_left: Point::zero(),
52///     size: Size::new(60, 20),
53///     bg_color: Rgb888::RED,
54///     fg_color: Rgb888::BLUE,
55///     text: "Click me!",
56/// };
57///
58/// # use embedded_graphics::mock_display::MockDisplay;
59/// # let mut display = MockDisplay::default();
60/// # display.set_allow_overdraw(true);
61/// button.draw(&mut display)?;
62/// # Ok::<(), core::convert::Infallible>(())
63/// ```
64///
65/// [`DrawTarget`]: crate::draw_target::DrawTarget
66/// [`draw_iter`]: crate::draw_target::DrawTarget::draw_iter
67pub trait Drawable {
68    /// The pixel color type.
69    type Color: PixelColor;
70
71    /// The return type of the `draw` method.
72    ///
73    /// The `Output` type can be used to return results and values produced from the drawing of the
74    /// current item. For example, rendering two differently styled text items next to each other
75    /// can make use of a returned value, allowing the next text to be positioned after the first:
76    ///
77    /// ```
78    /// use embedded_graphics::{
79    ///     mono_font::{
80    ///         ascii::{FONT_10X20, FONT_6X10},
81    ///         MonoTextStyle,
82    ///     },
83    ///     pixelcolor::BinaryColor,
84    ///     prelude::*,
85    ///     text::Text,
86    /// };
87    ///
88    /// # let mut display = embedded_graphics::mock_display::MockDisplay::new();
89    /// # display.set_allow_out_of_bounds_drawing(true);
90    /// let label_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On);
91    /// let value_style = MonoTextStyle::new(&FONT_10X20, BinaryColor::On);
92    ///
93    /// let next_point = Text::new("Label ", Point::new(10, 20), label_style)
94    ///     .draw(&mut display)?;
95    ///
96    /// Text::new("1234", next_point, value_style).draw(&mut display)?;
97    /// # Ok::<(), core::convert::Infallible>(())
98    /// ```
99    ///
100    /// Use `()` if no value should be returned.
101    type Output;
102
103    /// Draw the graphics object using the supplied DrawTarget.
104    fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
105    where
106        D: DrawTarget<Color = Self::Color>;
107}
108
109/// A single pixel.
110///
111/// `Pixel` objects are used to specify the position and color of drawn pixels.
112///
113/// # Examples
114///
115/// The [`Drawable`] trait is implemented for `Pixel` which allows single pixels
116/// to be drawn to a [`DrawTarget`]:
117/// ```
118/// use embedded_graphics::{pixelcolor::BinaryColor, prelude::*};
119/// # use embedded_graphics::mock_display::MockDisplay;
120/// # let mut display = MockDisplay::new();
121///
122/// Pixel(Point::new(1, 2), BinaryColor::On).draw(&mut display)?;
123/// # Ok::<(), core::convert::Infallible>(())
124/// ```
125///
126/// Iterators with `Pixel` items can also be drawn:
127///
128/// ```
129/// use embedded_graphics::{pixelcolor::BinaryColor, prelude::*};
130/// # use embedded_graphics::mock_display::MockDisplay;
131/// # let mut display = MockDisplay::new();
132///
133/// (0..32)
134///     .map(|i| Pixel(Point::new(i, i * 2), BinaryColor::On))
135///     .draw(&mut display)?;
136/// # Ok::<(), core::convert::Infallible>(())
137/// ```
138///
139/// [`DrawTarget`]: crate::draw_target::DrawTarget
140#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
141#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
142pub struct Pixel<C>(pub Point, pub C)
143where
144    C: PixelColor;
145
146impl<C> Drawable for Pixel<C>
147where
148    C: PixelColor,
149{
150    type Color = C;
151    type Output = ();
152
153    fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
154    where
155        D: DrawTarget<Color = C>,
156    {
157        target.draw_iter(core::iter::once(*self))
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    // NOTE: `crate` cannot be used here due to circular dependency resolution behavior.
164    use embedded_graphics::{
165        geometry::Point, mock_display::MockDisplay, pixelcolor::BinaryColor, Drawable, Pixel,
166    };
167
168    #[test]
169    fn draw_pixel() {
170        let mut display = MockDisplay::new();
171        Pixel(Point::new(0, 0), BinaryColor::On)
172            .draw(&mut display)
173            .unwrap();
174        Pixel(Point::new(2, 1), BinaryColor::On)
175            .draw(&mut display)
176            .unwrap();
177        Pixel(Point::new(1, 2), BinaryColor::On)
178            .draw(&mut display)
179            .unwrap();
180
181        display.assert_pattern(&[
182            "#  ", //
183            "  #", //
184            " # ", //
185        ]);
186    }
187}