zest_widget/widget/
canvas.rs1use super::Widget;
12use alloc::{vec, vec::Vec};
13use core::marker::PhantomData;
14use embedded_graphics::{pixelcolor::PixelColor, prelude::*, primitives::Rectangle};
15use zest_core::{Constraints, Length, RenderError, Renderer, TouchPhase};
16use zest_theme::Theme;
17
18#[derive(Clone, Debug)]
23pub struct CanvasBuffer<C: PixelColor> {
24 width: u32,
25 height: u32,
26 pixels: Vec<C>,
27}
28
29impl<C: PixelColor> CanvasBuffer<C> {
30 #[must_use]
32 pub fn new(width: u32, height: u32, fill: C) -> Self {
33 Self {
34 width,
35 height,
36 pixels: vec![fill; (width as usize) * (height as usize)],
37 }
38 }
39
40 #[must_use]
42 pub fn width(&self) -> u32 {
43 self.width
44 }
45
46 #[must_use]
48 pub fn height(&self) -> u32 {
49 self.height
50 }
51
52 #[must_use]
54 pub fn size(&self) -> Size {
55 Size::new(self.width, self.height)
56 }
57
58 #[must_use]
60 pub fn pixels(&self) -> &[C] {
61 &self.pixels
62 }
63
64 pub fn clear(&mut self, color: C) {
66 for px in &mut self.pixels {
67 *px = color;
68 }
69 }
70
71 pub fn set_pixel(&mut self, x: i32, y: i32, color: C) {
73 if x < 0 || y < 0 || x as u32 >= self.width || y as u32 >= self.height {
74 return;
75 }
76 let idx = (y as u32 * self.width + x as u32) as usize;
77 self.pixels[idx] = color;
78 }
79
80 pub fn fill_rect(&mut self, x: i32, y: i32, w: u32, h: u32, color: C) {
82 let x0 = x.max(0);
83 let y0 = y.max(0);
84 let x1 = (x + w as i32).min(self.width as i32);
85 let y1 = (y + h as i32).min(self.height as i32);
86 let mut py = y0;
87 while py < y1 {
88 let mut px = x0;
89 while px < x1 {
90 let idx = (py as u32 * self.width + px as u32) as usize;
91 self.pixels[idx] = color;
92 px += 1;
93 }
94 py += 1;
95 }
96 }
97
98 pub fn line(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: C) {
101 let dx = (x1 - x0).abs();
102 let dy = -(y1 - y0).abs();
103 let sx = if x0 < x1 { 1 } else { -1 };
104 let sy = if y0 < y1 { 1 } else { -1 };
105 let mut err = dx + dy;
106 let mut x = x0;
107 let mut y = y0;
108 loop {
109 self.set_pixel(x, y, color);
110 if x == x1 && y == y1 {
111 break;
112 }
113 let e2 = 2 * err;
114 if e2 >= dy {
115 err += dy;
116 x += sx;
117 }
118 if e2 <= dx {
119 err += dx;
120 y += sy;
121 }
122 }
123 }
124}
125
126pub struct Canvas<'a, C: PixelColor, M: Clone> {
131 rect: Rectangle,
132 buffer: &'a CanvasBuffer<C>,
133 width: Length,
134 height: Length,
135 _phantom: PhantomData<M>,
136}
137
138impl<'a, C: PixelColor, M: Clone> Canvas<'a, C, M> {
139 pub fn new(buffer: &'a CanvasBuffer<C>) -> Self {
142 Self {
143 rect: Rectangle::zero(),
144 buffer,
145 width: Length::Shrink,
146 height: Length::Shrink,
147 _phantom: PhantomData,
148 }
149 }
150
151 #[must_use]
153 pub fn width(mut self, width: impl Into<Length>) -> Self {
154 self.width = width.into();
155 self
156 }
157
158 #[must_use]
160 pub fn height(mut self, height: impl Into<Length>) -> Self {
161 self.height = height.into();
162 self
163 }
164}
165
166impl<'a, C: PixelColor, M: Clone> Widget<C, M> for Canvas<'a, C, M> {
167 fn measure(&mut self, constraints: Constraints) -> Size {
168 let size = self.buffer.size();
169 let w = self.width.resolve(size.width, constraints.max.width);
170 let h = self.height.resolve(size.height, constraints.max.height);
171 constraints.clamp(Size::new(w, h))
172 }
173
174 fn preferred_size(&self) -> (Length, Length) {
175 (self.width, self.height)
176 }
177
178 fn arrange(&mut self, rect: Rectangle) {
179 self.rect = rect;
180 }
181
182 fn rect(&self) -> Rectangle {
183 self.rect
184 }
185
186 fn handle_touch(&mut self, _point: Point, _phase: TouchPhase) -> Option<M> {
187 None
188 }
189
190 fn draw<'t>(
191 &self,
192 renderer: &mut dyn Renderer<C>,
193 _theme: &Theme<'t, C>,
194 ) -> Result<(), RenderError> {
195 let size = self.buffer.size();
196 let dx = (self.rect.size.width as i32 - size.width as i32).max(0) / 2;
197 let dy = (self.rect.size.height as i32 - size.height as i32).max(0) / 2;
198 let origin = self.rect.top_left + Point::new(dx, dy);
199 renderer.draw_image(origin, size, self.buffer.pixels())
200 }
201}