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