Skip to main content

zest_widget/widget/
image.rs

1//! Static raster image. Blits a borrowed slice of pixels into its arranged
2//! rect via [`Renderer::draw_image`] (zest_core::Renderer::draw_image),
3//! centering it when the slot is larger than the image.
4//!
5//! The pixel buffer is borrowed (`&'a [C]`), so the owner, typically a
6//! screen holding a decoded frame, keeps it alive for the frame.
7
8use super::Widget;
9use core::marker::PhantomData;
10use embedded_graphics::{pixelcolor::PixelColor, prelude::*, primitives::Rectangle};
11use zest_core::{Constraints, Length, RenderError, Renderer, TouchPhase};
12use zest_theme::Theme;
13
14/// Raster image drawn from a borrowed, row-major pixel slice.
15pub struct Image<'a, C: PixelColor, M: Clone> {
16    rect: Rectangle,
17    pixels: &'a [C],
18    image_size: Size,
19    width: Length,
20    height: Length,
21    _phantom: PhantomData<M>,
22}
23
24impl<'a, C: PixelColor, M: Clone> Image<'a, C, M> {
25    /// New image from a row-major `pixels` slice of `image_size`. Defaults to
26    /// shrinking to the image's intrinsic size; position is assigned by the
27    /// parent via `arrange`.
28    pub fn new(pixels: &'a [C], image_size: Size) -> Self {
29        Self {
30            rect: Rectangle::zero(),
31            pixels,
32            image_size,
33            width: Length::Shrink,
34            height: Length::Shrink,
35            _phantom: PhantomData,
36        }
37    }
38
39    /// Width sizing intent.
40    #[must_use]
41    pub fn width(mut self, width: impl Into<Length>) -> Self {
42        self.width = width.into();
43        self
44    }
45
46    /// Height sizing intent.
47    #[must_use]
48    pub fn height(mut self, height: impl Into<Length>) -> Self {
49        self.height = height.into();
50        self
51    }
52}
53
54impl<'a, C: PixelColor, M: Clone> Widget<C, M> for Image<'a, C, M> {
55    fn measure(&mut self, constraints: Constraints) -> Size {
56        let width = self
57            .width
58            .resolve(self.image_size.width, constraints.max.width);
59        let height = self
60            .height
61            .resolve(self.image_size.height, constraints.max.height);
62        constraints.clamp(Size::new(width, height))
63    }
64
65    fn preferred_size(&self) -> (Length, Length) {
66        (self.width, self.height)
67    }
68
69    fn arrange(&mut self, rect: Rectangle) {
70        self.rect = rect;
71    }
72
73    fn rect(&self) -> Rectangle {
74        self.rect
75    }
76
77    fn handle_touch(&mut self, _point: Point, _phase: TouchPhase) -> Option<M> {
78        None
79    }
80
81    fn draw<'t>(
82        &self,
83        renderer: &mut dyn Renderer<C>,
84        _theme: &Theme<'t, C>,
85    ) -> Result<(), RenderError> {
86        let dx = (self.rect.size.width as i32 - self.image_size.width as i32).max(0) / 2;
87        let dy = (self.rect.size.height as i32 - self.image_size.height as i32).max(0) / 2;
88        let origin = self.rect.top_left + Point::new(dx, dy);
89        renderer.draw_image(origin, self.image_size, self.pixels)
90    }
91}