1use rlvgl_core::draw::draw_widget_bg;
3use rlvgl_core::event::Event;
4use rlvgl_core::image::{BlitOpts, ImageDescriptor};
5use rlvgl_core::renderer::Renderer;
6use rlvgl_core::style::Style;
7use rlvgl_core::widget::{Color, Rect, Widget};
8
9pub struct Image<'a> {
11 bounds: Rect,
12 pub style: Style,
14 width: i32,
16 height: i32,
17 pixels: &'a [Color],
18 blit_opts: BlitOpts,
19 hidden: bool,
21}
22
23impl<'a> Image<'a> {
24 pub fn new(bounds: Rect, width: i32, height: i32, pixels: &'a [Color]) -> Self {
26 Self {
27 bounds,
28 style: Style::default(),
29 width,
30 height,
31 pixels,
32 blit_opts: BlitOpts::default(),
33 hidden: false,
34 }
35 }
36
37 pub fn with_blit_opts(mut self, blit_opts: BlitOpts) -> Self {
39 self.blit_opts = blit_opts;
40 self
41 }
42
43 pub fn set_pixels(&mut self, width: i32, height: i32, pixels: &'a [Color]) {
53 self.width = width;
54 self.height = height;
55 self.pixels = pixels;
56 }
57
58 pub fn set_blit_opts(&mut self, blit_opts: BlitOpts) {
61 self.blit_opts = blit_opts;
62 }
63
64 pub fn set_hidden(&mut self, hidden: bool) {
71 self.hidden = hidden;
72 }
73
74 pub fn is_hidden(&self) -> bool {
76 self.hidden
77 }
78}
79
80impl<'a> Widget for Image<'a> {
81 fn bounds(&self) -> Rect {
82 self.bounds
83 }
84
85 fn draw(&self, renderer: &mut dyn Renderer) {
86 if self.hidden {
89 return;
90 }
91 draw_widget_bg(renderer, self.bounds, &self.style);
92 let Some(width) = u16::try_from(self.width).ok() else {
93 return;
94 };
95 let Some(height) = u16::try_from(self.height).ok() else {
96 return;
97 };
98 let descriptor = ImageDescriptor::from_color_slice(self.pixels, width, height);
99 renderer.blit_image(self.bounds, &descriptor, &self.blit_opts);
100 }
101
102 fn handle_event(&mut self, _event: &Event) -> bool {
104 false
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn set_pixels_swaps_buffer_and_dims() {
114 let off = [Color(1, 2, 3, 255)];
115 let on = [Color(4, 5, 6, 255), Color(7, 8, 9, 255)];
116 let bounds = Rect {
117 x: 0,
118 y: 0,
119 width: 48,
120 height: 48,
121 };
122 let mut img = Image::new(bounds, 1, 1, &off);
123 assert_eq!(img.width, 1);
124 assert_eq!(img.height, 1);
125 assert_eq!(img.pixels.len(), 1);
126
127 img.set_pixels(2, 1, &on);
129 assert_eq!(img.width, 2);
130 assert_eq!(img.height, 1);
131 assert_eq!(img.pixels.len(), 2);
132 assert_eq!(img.pixels[0].0, 4);
133
134 img.set_pixels(1, 1, &off);
136 assert_eq!(img.pixels.len(), 1);
137 assert_eq!(img.pixels[0].0, 1);
138 }
139
140 struct CountingRenderer {
142 fills: u32,
143 blits: u32,
144 }
145 impl rlvgl_core::renderer::Renderer for CountingRenderer {
146 fn fill_rect(&mut self, _rect: Rect, _color: Color) {
147 self.fills += 1;
148 }
149 fn draw_text(&mut self, _pos: (i32, i32), _text: &str, _color: Color) {}
150 fn blit_image(
151 &mut self,
152 _dest: Rect,
153 _desc: &rlvgl_core::image::ImageDescriptor<'_>,
154 _opts: &BlitOpts,
155 ) {
156 self.blits += 1;
157 }
158 }
159
160 #[test]
161 fn set_hidden_makes_draw_a_noop() {
162 let px = [Color(10, 20, 30, 255)];
163 let bounds = Rect {
164 x: 0,
165 y: 0,
166 width: 8,
167 height: 8,
168 };
169 let mut img = Image::new(bounds, 1, 1, &px);
170 assert!(!img.is_hidden());
171
172 let mut r1 = CountingRenderer { fills: 0, blits: 0 };
174 img.draw(&mut r1);
175 assert!(r1.fills + r1.blits > 0, "visible image must paint");
176
177 img.set_hidden(true);
179 assert!(img.is_hidden());
180 let mut r2 = CountingRenderer { fills: 0, blits: 0 };
181 img.draw(&mut r2);
182 assert_eq!(r2.fills + r2.blits, 0, "hidden image must paint nothing");
183
184 img.set_hidden(false);
186 let mut r3 = CountingRenderer { fills: 0, blits: 0 };
187 img.draw(&mut r3);
188 assert!(r3.fills + r3.blits > 0, "shown image must paint again");
189 }
190}