embedded_graphics/image/
mod.rs

1//! Image support for embedded-graphics
2//!
3//! The two main types used to draw images are [`ImageDrawable`] and [`Image`].
4//!
5//! [`ImageDrawable`] is implemented to add support for different image formats. This crate includes
6//! an implementation for [raw pixel data]. Additional implementations for other image formats are
7//! provided by external crates like [tinybmp] and [tinytga].
8//!
9//! The [`Image`] object is used to specify the location at which an [`ImageDrawable`] is drawn.
10//! Images are drawn relative to their top-left corner.
11//!
12//! # Examples
13//!
14//! ## Display an RGB565 raw data image
15//!
16//! This example displays a small image created from a raw data array. The image is RGB565 encoded,
17//! so it uses the `Rgb565` color type.
18//!
19//! ```rust
20//! use embedded_graphics::{
21//!     image::{Image, ImageRaw, ImageRawBE},
22//!     pixelcolor::Rgb565,
23//!     prelude::*,
24//! };
25//! # use embedded_graphics::mock_display::MockDisplay as Display;
26//!
27//! let mut display: Display<Rgb565> = Display::default();
28//!
29//! // Raw big endian image data for demonstration purposes. A real image would likely be much
30//! // larger.
31//! let data = [
32//!     0x00, 0x00, 0xF8, 0x00, 0x07, 0xE0, 0xFF, 0xE0, //
33//!     0x00, 0x1F, 0x07, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, //
34//! ];
35//!
36//! // Create a raw image instance. Other image formats will require different code to load them.
37//! // All code after loading is the same for any image format.
38//! let raw: ImageRawBE<Rgb565> = ImageRaw::new(&data, 4);
39//!
40//! // Create an `Image` object to position the image at `Point::zero()`.
41//! let image = Image::new(&raw, Point::zero());
42//!
43//! // Draw the image to the display.
44//! image.draw(&mut display)?;
45//!
46//! # Ok::<(), core::convert::Infallible>(())
47//! ```
48//!
49//! ## Sub images
50//!
51//! [`SubImage`]s are used to split a larger image drawables into multiple parts, e.g. to draw a
52//! single sprite from a sprite atlas as in this example. Use the [`sub_image`] method provided by
53//! [`ImageDrawableExt`] to get a sub image from an image drawable. [`ImageDrawableExt`] is included
54//! in the [`prelude`], which this example takes advantage of.
55//!
56//! ```rust
57//! use embedded_graphics::{
58//!     image::{Image, ImageRaw, ImageRawBE},
59//!     pixelcolor::Rgb565,
60//!     prelude::*,
61//!     primitives::Rectangle,
62//! };
63//! # use embedded_graphics::mock_display::MockDisplay as Display;
64//!
65//! let mut display: Display<Rgb565> = Display::default();
66//!
67//! let data = [ 0xF8, 0x00, 0x07, 0xE0, 0xFF, 0xE0, /* ... */ ];
68//! // or: let data = include_bytes!("sprite_atlas.raw");
69//!
70//! # let data = [0u8; 32 * 16 * 2];
71//! let sprite_atlas: ImageRawBE<Rgb565> = ImageRaw::new(&data, 32);
72//!
73//! // Create individual sub images for each sprite in the sprite atlas.
74//! // The position and size of the sub images is defined by a `Rectangle`.
75//! let sprite_1 = sprite_atlas.sub_image(&Rectangle::new(Point::new(0, 0), Size::new(16, 16)));
76//! let sprite_2 = sprite_atlas.sub_image(&Rectangle::new(Point::new(16, 0), Size::new(16, 16)));
77//!
78//! // Create `Image` objects to draw the sprites at different positions on the display.
79//! Image::new(&sprite_1, Point::new(10, 10)).draw(&mut display)?;
80//! Image::new(&sprite_2, Point::new(40, 30)).draw(&mut display)?;
81//!
82//! # Ok::<(), core::convert::Infallible>(())
83//! ```
84//!
85//! # Implementing new image formats
86//!
87//! To add embedded-graphics support for an new image format the [`ImageDrawable`] and
88//! [`OriginDimensions`] traits must be implemented. See the [`ImageDrawable`] documentation
89//! for more information.
90//!
91//! [tinytga]: https://crates.io/crates/tinytga
92//! [tinybmp]: https://crates.io/crates/tinybmp
93//! [raw pixel data]: ImageRaw
94//! [`sub_image`]: ImageDrawableExt::sub_image
95//! [`OriginDimensions`]: super::geometry::OriginDimensions
96//! [`prelude`]: super::prelude
97
98mod image_drawable_ext;
99mod image_raw;
100mod sub_image;
101
102pub use embedded_graphics_core::image::{GetPixel, ImageDrawable};
103pub use image_drawable_ext::ImageDrawableExt;
104pub use image_raw::{ImageRaw, ImageRawBE, ImageRawLE};
105pub use sub_image::SubImage;
106
107use crate::{
108    draw_target::{DrawTarget, DrawTargetExt},
109    geometry::{Dimensions, OriginDimensions, Point},
110    primitives::Rectangle,
111    transform::Transform,
112    Drawable,
113};
114use core::fmt::Debug;
115
116/// Image object.
117///
118/// The `Image` struct serves as a wrapper around an [`ImageDrawable`] that provides support for
119/// an image format (raw bytes, BMP, TGA, etc). It allows an image to be repositioned using
120/// [`Transform::translate`] or [`Transform::translate_mut`] and drawn to a display that
121/// implements the [`DrawTarget`] trait.
122///
123/// Refer to the [module documentation] for examples.
124///
125/// [module documentation]: self
126/// [`Transform::translate`]: super::transform::Transform::translate
127/// [`Transform::translate_mut`]: super::transform::Transform::translate_mut
128/// [`DrawTarget`]: super::draw_target::DrawTarget
129#[derive(Debug, Clone, Copy)]
130#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
131pub struct Image<'a, T> {
132    image_drawable: &'a T,
133    offset: Point,
134}
135
136impl<'a, T> Image<'a, T>
137where
138    T: ImageDrawable,
139{
140    /// Creates a new `Image`.
141    pub const fn new(image_drawable: &'a T, position: Point) -> Self {
142        Self {
143            image_drawable,
144            offset: position,
145        }
146    }
147
148    /// Creates a new `Image` centered around a given point.
149    pub fn with_center(image_drawable: &'a T, center: Point) -> Self {
150        let offset = Rectangle::with_center(center, image_drawable.size()).top_left;
151
152        Self {
153            image_drawable,
154            offset,
155        }
156    }
157}
158
159impl<T> Transform for Image<'_, T> {
160    /// Translate the image by a given delta, returning a new image
161    ///
162    /// # Examples
163    ///
164    /// ## Move an image around
165    ///
166    /// This examples moves a 4x4 black and white image by `(10, 20)` pixels without mutating the
167    /// original image
168    ///
169    /// ```rust
170    /// use embedded_graphics::{
171    ///     geometry::Point,
172    ///     image::{Image, ImageRaw},
173    ///     pixelcolor::BinaryColor,
174    ///     prelude::*,
175    /// };
176    ///
177    /// let image: ImageRaw<BinaryColor> = ImageRaw::new(&[0xff, 0x00, 0xff, 0x00], 4);
178    ///
179    /// let image = Image::new(&image, Point::zero());
180    ///
181    /// let image_moved = image.translate(Point::new(10, 20));
182    ///
183    /// assert_eq!(image.bounding_box().top_left, Point::zero());
184    /// assert_eq!(image_moved.bounding_box().top_left, Point::new(10, 20));
185    /// ```
186    fn translate(&self, by: Point) -> Self {
187        Self {
188            image_drawable: self.image_drawable,
189            offset: self.offset + by,
190        }
191    }
192
193    /// Translate the image by a given delta, modifying the original object
194    ///
195    /// # Examples
196    ///
197    /// ## Move an image around
198    ///
199    /// This examples moves a 4x4 black and white image by `(10, 20)` pixels by mutating the
200    /// original image
201    ///
202    /// ```rust
203    /// use embedded_graphics::{
204    ///     geometry::Point,
205    ///     image::{Image, ImageRaw},
206    ///     pixelcolor::BinaryColor,
207    ///     prelude::*,
208    /// };
209    ///
210    /// let image: ImageRaw<BinaryColor> = ImageRaw::new(&[0xff, 0x00, 0xff, 0x00], 4);
211    ///
212    /// let mut image = Image::new(&image, Point::zero());
213    ///
214    /// image.translate_mut(Point::new(10, 20));
215    ///
216    /// assert_eq!(image.bounding_box().top_left, Point::new(10, 20));
217    /// ```
218    fn translate_mut(&mut self, by: Point) -> &mut Self {
219        self.offset += by;
220
221        self
222    }
223}
224
225impl<'a, T> Drawable for Image<'a, T>
226where
227    T: ImageDrawable,
228{
229    type Color = T::Color;
230    type Output = ();
231
232    fn draw<D>(&self, display: &mut D) -> Result<Self::Output, D::Error>
233    where
234        D: DrawTarget<Color = Self::Color>,
235    {
236        self.image_drawable
237            .draw(&mut display.translated(self.offset))
238    }
239}
240
241impl<'a, T> Dimensions for Image<'a, T>
242where
243    T: OriginDimensions,
244{
245    fn bounding_box(&self) -> Rectangle {
246        self.image_drawable.bounding_box().translate(self.offset)
247    }
248}
249
250#[cfg(test)]
251mod tests {
252    use super::*;
253    use crate::{geometry::Size, mock_display::MockDisplay, pixelcolor::BinaryColor};
254
255    #[test]
256    fn negative_top_left() {
257        let image: ImageRaw<BinaryColor> = ImageRaw::new(&[0xff, 0x00, 0xff, 0x00], 4);
258
259        let image = Image::new(&image, Point::zero()).translate(Point::new(-1, -1));
260
261        assert_eq!(
262            image.bounding_box(),
263            Rectangle::new(Point::new(-1, -1), Size::new(4, 4))
264        );
265    }
266
267    #[test]
268    fn dimensions() {
269        let image: ImageRaw<BinaryColor> = ImageRaw::new(&[0xff, 0x00, 0xFF, 0x00], 4);
270
271        let image = Image::new(&image, Point::zero()).translate(Point::new(100, 200));
272
273        assert_eq!(
274            image.bounding_box(),
275            Rectangle::new(Point::new(100, 200), Size::new(4, 4))
276        );
277    }
278
279    #[test]
280    fn position() {
281        let image_raw: ImageRaw<BinaryColor> = ImageRaw::new(&[0xAA, 0x55, 0xAA, 0x55], 4);
282
283        let mut display = MockDisplay::new();
284        Image::new(&image_raw, Point::new(1, 2))
285            .draw(&mut display)
286            .unwrap();
287
288        display.assert_pattern(&[
289            "     ", //
290            "     ", //
291            " #.#.", //
292            " .#.#", //
293            " #.#.", //
294            " .#.#", //
295        ]);
296    }
297
298    #[test]
299    fn with_center() {
300        let image_raw: ImageRaw<BinaryColor> = ImageRaw::new(&[0xAA, 0x55, 0xAA, 0x55], 4);
301
302        let mut display = MockDisplay::new();
303        Image::with_center(&image_raw, Point::new(1, 2))
304            .draw(&mut display)
305            .unwrap();
306
307        display.assert_pattern(&[
308            "    ", //
309            "#.#.", //
310            ".#.#", //
311            "#.#.", //
312            ".#.#", //
313        ]);
314    }
315}