1use libc::c_int;
2use std::ffi::CString;
3
4use crate::ffi;
5use crate::{LedColor, LedFont};
6
7pub struct LedCanvas {
16 pub(crate) handle: *mut ffi::CLedCanvas,
17}
18
19impl LedCanvas {
20 #[must_use]
22 pub fn canvas_size(&self) -> (i32, i32) {
23 let (mut width, mut height): (c_int, c_int) = (0, 0);
24 unsafe {
25 ffi::led_canvas_get_size(
26 self.handle,
27 &mut width as *mut c_int,
28 &mut height as *mut c_int,
29 );
30 }
31 (width as i32, height as i32)
32 }
33
34 pub fn set(&mut self, x: i32, y: i32, color: &LedColor) {
36 unsafe {
37 ffi::led_canvas_set_pixel(
38 self.handle,
39 x as c_int,
40 y as c_int,
41 color.red,
42 color.green,
43 color.blue,
44 );
45 }
46 }
47
48 pub fn clear(&mut self) {
50 unsafe {
51 ffi::led_canvas_clear(self.handle);
52 }
53 }
54
55 pub fn fill(&mut self, color: &LedColor) {
57 unsafe {
58 ffi::led_canvas_fill(self.handle, color.red, color.green, color.blue);
59 }
60 }
61
62 pub fn draw_line(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: &LedColor) {
66 unsafe {
67 ffi::draw_line(
68 self.handle,
69 x0,
70 y0,
71 x1,
72 y1,
73 color.red,
74 color.green,
75 color.blue,
76 );
77 }
78 }
79
80 pub fn draw_circle(&mut self, x: i32, y: i32, radius: u32, color: &LedColor) {
84 unsafe {
85 ffi::draw_circle(
86 self.handle,
87 x as c_int,
88 y as c_int,
89 radius as c_int,
90 color.red,
91 color.green,
92 color.blue,
93 );
94 }
95 }
96
97 #[allow(clippy::too_many_arguments)]
98 pub fn draw_text(
104 &mut self,
105 font: &LedFont,
106 text: &str,
107 x: i32,
108 y: i32,
109 color: &LedColor,
110 kerning_offset: i32,
111 vertical: bool,
112 ) -> i32 {
113 let text = CString::new(text).expect("given string failed to convert into a CString");
114 unsafe {
115 if vertical {
116 ffi::vertical_draw_text(
117 self.handle,
118 font.handle,
119 x as c_int,
120 y as c_int,
121 color.red,
122 color.green,
123 color.blue,
124 text.as_ptr(),
125 kerning_offset as c_int,
126 ) as i32
127 } else {
128 ffi::draw_text(
129 self.handle,
130 font.handle,
131 x as c_int,
132 y as c_int,
133 color.red,
134 color.green,
135 color.blue,
136 text.as_ptr(),
137 kerning_offset as c_int,
138 ) as i32
139 }
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use crate::{LedMatrix, LedMatrixOptions, LedRuntimeOptions};
148 use std::f64::consts::PI;
149 use std::{thread, time};
150
151 fn led_matrix() -> LedMatrix {
152 let mut options = LedMatrixOptions::new();
153 let mut rt_options = LedRuntimeOptions::new();
154 options.set_hardware_mapping("adafruit-hat-pwm");
155 options.set_chain_length(2);
156 options.set_hardware_pulsing(false);
157 options.set_refresh_rate(true);
158 options.set_brightness(10).unwrap();
159 rt_options.set_gpio_slowdown(2);
160 LedMatrix::new(Some(options), Some(rt_options)).unwrap()
161 }
162
163 #[test]
164 #[serial_test::serial]
165 fn size() {
166 let matrix = led_matrix();
167 let canvas = matrix.canvas();
168 assert_eq!(canvas.canvas_size(), (64, 32));
169 }
170
171 #[test]
172 #[serial_test::serial]
173 fn draw_line() {
174 let matrix = led_matrix();
175 let mut canvas = matrix.canvas();
176 let (width, height) = canvas.canvas_size();
177 let mut color = LedColor {
178 red: 127,
179 green: 0,
180 blue: 0,
181 };
182
183 canvas.clear();
184 for x in 0..width {
185 color.blue = 255 - 3 * x as u8;
186 canvas.draw_line(x, 0, width - 1 - x, height - 1, &color);
187 thread::sleep(time::Duration::new(0, 10000000));
188 }
189 }
190
191 #[test]
192 #[serial_test::serial]
193 fn draw_circle() {
194 let matrix = led_matrix();
195 let mut canvas = matrix.canvas();
196 let (width, height) = canvas.canvas_size();
197 let mut color = LedColor {
198 red: 127,
199 green: 0,
200 blue: 0,
201 };
202 let (x, y) = (width / 2, height / 2);
203
204 canvas.clear();
205 for r in 0..(width / 2) {
206 color.green = color.red;
207 color.red = color.blue;
208 color.blue = (r * r) as u8;
209 canvas.draw_circle(x, y, r as u32, &color);
210 thread::sleep(time::Duration::new(0, 100000000));
211 }
212 }
213
214 #[test]
215 #[serial_test::serial]
216 fn gradient() {
217 let matrix = led_matrix();
218 let mut canvas = matrix.canvas();
219 let mut color = LedColor {
220 red: 0,
221 green: 0,
222 blue: 0,
223 };
224 let period = 400;
225 let duration = time::Duration::new(3, 0);
226 let sleep_duration = duration / period;
227
228 for t in 0..period {
229 let t = t as f64;
230 color.red = ((PI * t / period as f64).sin() * 255.) as u8;
231 color.green = ((2. * PI * t / period as f64).cos() * 255.) as u8;
232 color.blue = ((3. * PI * t / period as f64 + 0.3).cos() * 255.) as u8;
233 canvas.fill(&color);
234 thread::sleep(sleep_duration);
235 }
236 }
237
238 #[test]
239 #[serial_test::serial]
240 fn canvas_swap() {
241 let matrix = led_matrix();
242 let mut canvas = matrix.canvas();
243 let mut color = LedColor {
244 red: 127,
245 green: 127,
246 blue: 0,
247 };
248
249 canvas.fill(&color);
250 canvas = matrix.offscreen_canvas();
251 color.blue = 127;
252 canvas.fill(&color);
253 thread::sleep(time::Duration::new(0, 500000000));
254 canvas = matrix.swap(canvas);
255 color.red = 0;
256 canvas.fill(&color);
257 thread::sleep(time::Duration::new(0, 500000000));
258 let _ = matrix.swap(canvas);
259 thread::sleep(time::Duration::new(0, 500000000));
260 }
261}