1 | //! Framebuffer. |
2 | |
3 | use core::{convert::Infallible, marker::PhantomData}; |
4 | |
5 | use crate::{ |
6 | draw_target::DrawTarget, |
7 | geometry::{OriginDimensions, Point, Size}, |
8 | image::{GetPixel, ImageRaw}, |
9 | iterator::raw::RawDataSlice, |
10 | pixelcolor::{ |
11 | raw::{ |
12 | BigEndian, ByteOrder, LittleEndian, RawData, RawU1, RawU16, RawU2, RawU24, RawU32, |
13 | RawU4, RawU8, ToBytes, |
14 | }, |
15 | PixelColor, |
16 | }, |
17 | Pixel, |
18 | }; |
19 | |
20 | /// Calculates the required buffer size. |
21 | /// |
22 | /// This function is a workaround for current limitations in Rust const generics. |
23 | /// It can be used to calculate the `N` parameter based on the size and color type of the framebuffer. |
24 | pub const fn buffer_size<C: PixelColor>(width: usize, height: usize) -> usize { |
25 | buffer_size_bpp(width, height, C::Raw::BITS_PER_PIXEL) |
26 | } |
27 | |
28 | /// Calculates the required buffer size. |
29 | /// |
30 | /// This function is a workaround for current limitations in Rust const generics. |
31 | /// It can be used to calculate the `N` parameter based on the size and bit depth of the framebuffer. |
32 | pub const fn buffer_size_bpp(width: usize, height: usize, bpp: usize) -> usize { |
33 | (width * bpp + 7) / 8 * height |
34 | } |
35 | |
36 | /// A framebuffer. |
37 | /// |
38 | /// # Examples |
39 | /// |
40 | /// ``` |
41 | /// use embedded_graphics::{ |
42 | /// framebuffer, |
43 | /// framebuffer::{Framebuffer, buffer_size}, |
44 | /// pixelcolor::{Rgb565, raw::LittleEndian}, |
45 | /// prelude::*, |
46 | /// primitives::PrimitiveStyle, |
47 | /// }; |
48 | /// |
49 | /// let mut fb = Framebuffer::<Rgb565, _, LittleEndian, 320, 240, {buffer_size::<Rgb565>(320, 240)}>::new(); |
50 | /// |
51 | /// fb.bounding_box() |
52 | /// .into_styled(PrimitiveStyle::with_stroke(Rgb565::RED, 1)) |
53 | /// .draw(&mut fb) |
54 | /// .unwrap(); |
55 | /// ``` |
56 | #[derive (Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)] |
57 | pub struct Framebuffer<C, R, BO, const WIDTH: usize, const HEIGHT: usize, const N: usize> { |
58 | data: [u8; N], |
59 | color_type: PhantomData<C>, |
60 | raw_type: PhantomData<R>, |
61 | byte_order: PhantomData<BO>, |
62 | n_assert: (), |
63 | } |
64 | |
65 | impl<C, BO, const WIDTH: usize, const HEIGHT: usize, const N: usize> |
66 | Framebuffer<C, C::Raw, BO, WIDTH, HEIGHT, N> |
67 | where |
68 | C: PixelColor, |
69 | { |
70 | const BUFFER_SIZE: usize = buffer_size::<C>(WIDTH, HEIGHT); |
71 | |
72 | /// Static assertion that N is correct. |
73 | // MSRV: remove N when constant generic expressions are stabilized |
74 | const CHECK_N: () = assert!( |
75 | N >= Self::BUFFER_SIZE, |
76 | "Invalid N: see Framebuffer documentation for more information" |
77 | ); |
78 | |
79 | /// Creates a new framebuffer. |
80 | pub const fn new() -> Self { |
81 | Self { |
82 | data: [0; N], |
83 | color_type: PhantomData, |
84 | raw_type: PhantomData, |
85 | byte_order: PhantomData, |
86 | n_assert: Self::CHECK_N, |
87 | } |
88 | } |
89 | |
90 | /// Returns a reference to the raw framebuffer data. |
91 | pub const fn data(&self) -> &[u8; N] { |
92 | &self.data |
93 | } |
94 | |
95 | /// Returns a mutable reference to the raw framebuffer data. |
96 | pub fn data_mut(&mut self) -> &mut [u8; N] { |
97 | &mut self.data |
98 | } |
99 | } |
100 | |
101 | impl<C, BO, const WIDTH: usize, const HEIGHT: usize, const N: usize> |
102 | Framebuffer<C, C::Raw, BO, WIDTH, HEIGHT, N> |
103 | where |
104 | C: PixelColor + From<C::Raw>, |
105 | BO: ByteOrder, |
106 | for<'a> RawDataSlice<'a, C::Raw, BO>: IntoIterator<Item = C::Raw>, |
107 | { |
108 | /// Returns an image based on the framebuffer content. |
109 | pub fn as_image(&self) -> ImageRaw<'_, C, BO> { |
110 | ImageRaw::new(&self.data[0..Self::BUFFER_SIZE], WIDTH as u32) |
111 | } |
112 | } |
113 | |
114 | impl<C, BO, const WIDTH: usize, const HEIGHT: usize, const N: usize> GetPixel |
115 | for Framebuffer<C, C::Raw, BO, WIDTH, HEIGHT, N> |
116 | where |
117 | C: PixelColor + From<C::Raw>, |
118 | BO: ByteOrder, |
119 | for<'a> RawDataSlice<'a, C::Raw, BO>: IntoIterator<Item = C::Raw>, |
120 | { |
121 | type Color = C; |
122 | |
123 | fn pixel(&self, p: Point) -> Option<C> { |
124 | self.as_image().pixel(p) |
125 | } |
126 | } |
127 | |
128 | macro_rules! impl_bit { |
129 | ($raw_type:ident) => { |
130 | impl<C, BO, const WIDTH: usize, const HEIGHT: usize, const N: usize> |
131 | Framebuffer<C, $raw_type, BO, WIDTH, HEIGHT, N> |
132 | where |
133 | C: PixelColor + Into<$raw_type>, |
134 | { |
135 | /// Sets the color of a pixel. |
136 | /// |
137 | /// Trying to set a pixel outside the framebuffer is a noop. |
138 | pub fn set_pixel(&mut self, p: Point, c: C) { |
139 | if let (Ok(x), Ok(y)) = (usize::try_from(p.x), usize::try_from(p.y)) { |
140 | if x < WIDTH && y < HEIGHT { |
141 | let pixels_per_bit = 8 / C::Raw::BITS_PER_PIXEL; |
142 | let bits_per_row = WIDTH * C::Raw::BITS_PER_PIXEL; |
143 | let bytes_per_row = (bits_per_row + 7) / 8; |
144 | let byte_index = bytes_per_row * y + (x / pixels_per_bit); |
145 | let bit_index = 8 - (x % pixels_per_bit + 1) * C::Raw::BITS_PER_PIXEL; |
146 | |
147 | let mask = !((2u8.pow(C::Raw::BITS_PER_PIXEL as u32) - 1) << bit_index); |
148 | let bits = c.into().into_inner() << bit_index; |
149 | |
150 | self.data[byte_index] = self.data[byte_index] & mask | bits; |
151 | } |
152 | } |
153 | } |
154 | } |
155 | |
156 | impl<C, BO, const WIDTH: usize, const HEIGHT: usize, const N: usize> DrawTarget |
157 | for Framebuffer<C, $raw_type, BO, WIDTH, HEIGHT, N> |
158 | where |
159 | C: PixelColor<Raw = $raw_type> + Into<$raw_type>, |
160 | { |
161 | type Color = C; |
162 | type Error = Infallible; |
163 | |
164 | fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error> |
165 | where |
166 | I: IntoIterator<Item = Pixel<Self::Color>>, |
167 | { |
168 | for Pixel(p, c) in pixels { |
169 | self.set_pixel(p, c); |
170 | } |
171 | |
172 | Ok(()) |
173 | } |
174 | } |
175 | }; |
176 | } |
177 | |
178 | impl_bit!(RawU1); |
179 | impl_bit!(RawU2); |
180 | impl_bit!(RawU4); |
181 | |
182 | impl<C, BO, const WIDTH: usize, const HEIGHT: usize, const N: usize> |
183 | Framebuffer<C, RawU8, BO, WIDTH, HEIGHT, N> |
184 | where |
185 | C: PixelColor + Into<RawU8>, |
186 | { |
187 | /// Sets the color of a pixel. |
188 | /// |
189 | /// Setting a pixel outside the framebuffer's bounding box will be a noop. |
190 | pub fn set_pixel(&mut self, p: Point, c: C) { |
191 | if let (Ok(x: usize), Ok(y: usize)) = (usize::try_from(p.x), usize::try_from(p.y)) { |
192 | if x < WIDTH && y < HEIGHT { |
193 | let x: usize = p.x as usize; |
194 | let y: usize = p.y as usize; |
195 | |
196 | self.data[y * WIDTH + x] = c.into().into_inner(); |
197 | } |
198 | } |
199 | } |
200 | } |
201 | |
202 | impl<C, BO, const WIDTH: usize, const HEIGHT: usize, const N: usize> DrawTarget |
203 | for Framebuffer<C, RawU8, BO, WIDTH, HEIGHT, N> |
204 | where |
205 | C: PixelColor<Raw = RawU8> + Into<RawU8>, |
206 | { |
207 | type Color = C; |
208 | type Error = Infallible; |
209 | |
210 | fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error> |
211 | where |
212 | I: IntoIterator<Item = Pixel<Self::Color>>, |
213 | { |
214 | for Pixel(p: Point, c: C) in pixels { |
215 | self.set_pixel(p, c); |
216 | } |
217 | |
218 | Ok(()) |
219 | } |
220 | } |
221 | |
222 | macro_rules! impl_bytes { |
223 | ($raw_type:ty, $bo_type:ty, $to_bytes_fn:ident) => { |
224 | impl<C, const WIDTH: usize, const HEIGHT: usize, const N: usize> |
225 | Framebuffer<C, $raw_type, $bo_type, WIDTH, HEIGHT, N> |
226 | where |
227 | C: PixelColor + Into<$raw_type>, |
228 | { |
229 | /// Sets the color of a pixel. |
230 | /// |
231 | /// Trying to set a pixel outside the framebuffer is a noop. |
232 | pub fn set_pixel(&mut self, p: Point, c: C) { |
233 | const BYTES_PER_PIXEL: usize = <$raw_type>::BITS_PER_PIXEL / 8; |
234 | |
235 | if let (Ok(x), Ok(y)) = (usize::try_from(p.x), usize::try_from(p.y)) { |
236 | if x < WIDTH && y < HEIGHT { |
237 | let x = p.x as usize; |
238 | let y = p.y as usize; |
239 | |
240 | let index = (y * WIDTH + x) * BYTES_PER_PIXEL; |
241 | |
242 | self.data[index..index + BYTES_PER_PIXEL] |
243 | .copy_from_slice(&c.into().$to_bytes_fn()); |
244 | } |
245 | } |
246 | } |
247 | } |
248 | |
249 | impl<C, const WIDTH: usize, const HEIGHT: usize, const N: usize> DrawTarget |
250 | for Framebuffer<C, $raw_type, $bo_type, WIDTH, HEIGHT, N> |
251 | where |
252 | C: PixelColor<Raw = $raw_type> + Into<$raw_type>, |
253 | { |
254 | type Color = C; |
255 | type Error = Infallible; |
256 | |
257 | fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error> |
258 | where |
259 | I: IntoIterator<Item = Pixel<Self::Color>>, |
260 | { |
261 | for Pixel(p, c) in pixels { |
262 | self.set_pixel(p, c); |
263 | } |
264 | |
265 | Ok(()) |
266 | } |
267 | } |
268 | }; |
269 | |
270 | ($raw_type:ty) => { |
271 | impl_bytes!($raw_type, LittleEndian, to_le_bytes); |
272 | impl_bytes!($raw_type, BigEndian, to_be_bytes); |
273 | }; |
274 | } |
275 | |
276 | impl_bytes!(RawU16); |
277 | impl_bytes!(RawU24); |
278 | impl_bytes!(RawU32); |
279 | |
280 | impl<C, R, BO, const WIDTH: usize, const HEIGHT: usize, const N: usize> OriginDimensions |
281 | for Framebuffer<C, R, BO, WIDTH, HEIGHT, N> |
282 | { |
283 | fn size(&self) -> Size { |
284 | Size::new(WIDTH as u32, HEIGHT as u32) |
285 | } |
286 | } |
287 | |
288 | #[cfg (test)] |
289 | mod tests { |
290 | use embedded_graphics_core::prelude::GrayColor; |
291 | |
292 | use super::*; |
293 | |
294 | use crate::{ |
295 | geometry::Dimensions, |
296 | geometry::Point, |
297 | image::Image, |
298 | mock_display::MockDisplay, |
299 | pixelcolor::{BinaryColor, Gray2, Gray4, Gray8, Rgb565, Rgb888, RgbColor}, |
300 | primitives::{Primitive, PrimitiveStyle}, |
301 | Drawable, |
302 | }; |
303 | |
304 | /// Calculate the framebuffer generic constants. |
305 | macro_rules! framebuffer { |
306 | ($color_type:ty, $byte_order:ty, $width:expr, $height:expr) => { |
307 | $crate::framebuffer::Framebuffer::< |
308 | $color_type, |
309 | <$color_type as $crate::pixelcolor::PixelColor>::Raw, |
310 | $byte_order, |
311 | $width, |
312 | $height, |
313 | { $crate::framebuffer::buffer_size::<$color_type>($width, $height) }, |
314 | > |
315 | }; |
316 | |
317 | ($color_type:ty, $width:expr, $height:expr) => { |
318 | $crate::framebuffer::Framebuffer::< |
319 | $color_type, |
320 | <$color_type as $crate::pixelcolor::PixelColor>::Raw, |
321 | $crate::pixelcolor::raw::LittleEndian, |
322 | $width, |
323 | $height, |
324 | { $crate::framebuffer::buffer_size::<$color_type>($width, $height) }, |
325 | > |
326 | }; |
327 | } |
328 | |
329 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
330 | struct U32Color(u32); |
331 | |
332 | impl PixelColor for U32Color { |
333 | type Raw = RawU32; |
334 | } |
335 | |
336 | impl From<RawU32> for U32Color { |
337 | fn from(raw: RawU32) -> Self { |
338 | Self(raw.into_inner()) |
339 | } |
340 | } |
341 | |
342 | impl From<U32Color> for RawU32 { |
343 | fn from(color: U32Color) -> Self { |
344 | Self::new(color.0) |
345 | } |
346 | } |
347 | |
348 | #[test ] |
349 | fn raw_u1() { |
350 | let mut fb = <framebuffer!(BinaryColor, 9, 2)>::new(); |
351 | |
352 | use BinaryColor::{Off, On}; |
353 | fb.draw_iter( |
354 | [ |
355 | ((0, 0), On), // |
356 | ((8, 1), On), // |
357 | ((1, 1), On), // |
358 | ((1, 1), Off), // |
359 | ((-1, 0), On), // |
360 | ((0, -1), On), // |
361 | ((9, 0), On), // |
362 | ((0, 2), On), // |
363 | ] |
364 | .iter() |
365 | .map(|(p, c)| Pixel(Point::from(*p), *c)), |
366 | ) |
367 | .unwrap(); |
368 | |
369 | assert_eq!( |
370 | fb.data(), |
371 | &[ |
372 | 0b10000000, 0b00000000, // |
373 | 0b00000000, 0b10000000, // |
374 | ] |
375 | ); |
376 | } |
377 | |
378 | #[test ] |
379 | fn raw_u2() { |
380 | type FB = framebuffer!(Gray2, 6, 4); |
381 | let mut fb = FB::new(); |
382 | |
383 | fb.draw_iter( |
384 | [ |
385 | ((0, 0), 1), // |
386 | ((5, 3), 2), // |
387 | ((1, 1), 3), // |
388 | ((1, 2), 0), // |
389 | ((-1, 0), 3), // |
390 | ((0, -1), 3), // |
391 | ((6, 0), 3), // |
392 | ((0, 4), 3), // |
393 | ] |
394 | .iter() |
395 | .map(|(p, c)| Pixel(Point::from(*p), Gray2::new(*c))), |
396 | ) |
397 | .unwrap(); |
398 | |
399 | assert_eq!( |
400 | fb.data(), |
401 | &[ |
402 | 0b01000000, 0b00000000, // |
403 | 0b00110000, 0b00000000, // |
404 | 0b00000000, 0b00000000, // |
405 | 0b00000000, 0b00100000, // |
406 | ] |
407 | ); |
408 | } |
409 | |
410 | #[test ] |
411 | fn raw_u4() { |
412 | let mut fb = <framebuffer!(Gray4, 3, 2)>::new(); |
413 | |
414 | fb.draw_iter( |
415 | [ |
416 | ((0, 0), 0x1), // |
417 | ((2, 1), 0xF), // |
418 | ((1, 0), 0xA), // |
419 | ((1, 1), 0xB), // |
420 | ((-1, 0), 0xF), // |
421 | ((0, -1), 0xF), // |
422 | ((3, 0), 0xF), // |
423 | ((0, 2), 0xF), // |
424 | ] |
425 | .iter() |
426 | .map(|(p, c)| Pixel(Point::from(*p), Gray4::new(*c))), |
427 | ) |
428 | .unwrap(); |
429 | |
430 | assert_eq!( |
431 | fb.data(), |
432 | &[ |
433 | 0x1A, 0x00, // |
434 | 0x0B, 0xF0, // |
435 | ] |
436 | ); |
437 | } |
438 | |
439 | #[test ] |
440 | fn raw_u8() { |
441 | let mut fb = <framebuffer!(Gray8, 3, 2)>::new(); |
442 | |
443 | fb.draw_iter( |
444 | [ |
445 | ((0, 0), 0x10), // |
446 | ((2, 1), 0x22), // |
447 | ((1, 0), 0x3F), // |
448 | ((1, 1), 0xF0), // |
449 | ((-1, 0), 0xFF), // |
450 | ((0, -1), 0xFF), // |
451 | ((3, 0), 0xFF), // |
452 | ((0, 2), 0xFF), // |
453 | ] |
454 | .iter() |
455 | .map(|(p, c)| Pixel(Point::from(*p), Gray8::new(*c))), |
456 | ) |
457 | .unwrap(); |
458 | |
459 | assert_eq!( |
460 | fb.data(), |
461 | &[ |
462 | 0x10, 0x3F, 0x00, // |
463 | 0x00, 0xF0, 0x22, // |
464 | ] |
465 | ); |
466 | } |
467 | |
468 | #[test ] |
469 | fn raw_u16_le() { |
470 | let mut fb = <framebuffer!(Rgb565, 3, 2)>::new(); |
471 | |
472 | fb.draw_iter( |
473 | [ |
474 | ((0, 0), 0x1000), // |
475 | ((2, 1), 0x0001), // |
476 | ((1, 0), 0x1234), // |
477 | ((1, 1), 0x8765), // |
478 | ((-1, 0), 0xFFFF), // |
479 | ((0, -1), 0xFFFF), // |
480 | ((3, 0), 0xFFFF), // |
481 | ((0, 2), 0xFFFF), // |
482 | ] |
483 | .iter() |
484 | .map(|(p, c)| Pixel(Point::from(*p), Rgb565::from(RawU16::new(*c)))), |
485 | ) |
486 | .unwrap(); |
487 | |
488 | assert_eq!( |
489 | fb.data(), |
490 | &[ |
491 | 0x00, 0x10, 0x34, 0x12, 0x00, 0x00, // |
492 | 0x00, 0x00, 0x65, 0x87, 0x01, 0x00, // |
493 | ] |
494 | ); |
495 | } |
496 | |
497 | #[test ] |
498 | fn raw_u16_be() { |
499 | let mut fb = <framebuffer!(Rgb565, BigEndian, 3, 2)>::new(); |
500 | |
501 | fb.draw_iter( |
502 | [ |
503 | ((0, 0), 0x1000), // |
504 | ((2, 1), 0x0001), // |
505 | ((1, 0), 0x1234), // |
506 | ((1, 1), 0x8765), // |
507 | ((-1, 0), 0xFFFF), // |
508 | ((0, -1), 0xFFFF), // |
509 | ((3, 0), 0xFFFF), // |
510 | ((0, 2), 0xFFFF), // |
511 | ] |
512 | .iter() |
513 | .map(|(p, c)| Pixel(Point::from(*p), Rgb565::from(RawU16::new(*c)))), |
514 | ) |
515 | .unwrap(); |
516 | |
517 | assert_eq!( |
518 | fb.data(), |
519 | &[ |
520 | 0x10, 0x00, 0x12, 0x34, 0x00, 0x00, // |
521 | 0x00, 0x00, 0x87, 0x65, 0x00, 0x01, // |
522 | ] |
523 | ); |
524 | } |
525 | |
526 | #[test ] |
527 | fn raw_u24_le() { |
528 | let mut fb = <framebuffer!(Rgb888, 3, 2)>::new(); |
529 | |
530 | fb.draw_iter( |
531 | [ |
532 | ((0, 0), 0x100000), // |
533 | ((2, 1), 0x000001), // |
534 | ((1, 0), 0x123456), // |
535 | ((1, 1), 0x876543), // |
536 | ((-1, 0), 0xFFFFFF), // |
537 | ((0, -1), 0xFFFFFF), // |
538 | ((3, 0), 0xFFFFFF), // |
539 | ((0, 2), 0xFFFFFF), // |
540 | ] |
541 | .iter() |
542 | .map(|(p, c)| Pixel(Point::from(*p), Rgb888::from(RawU24::new(*c)))), |
543 | ) |
544 | .unwrap(); |
545 | |
546 | assert_eq!( |
547 | fb.data(), |
548 | &[ |
549 | 0x00, 0x00, 0x10, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, // |
550 | 0x00, 0x00, 0x00, 0x43, 0x65, 0x87, 0x01, 0x00, 0x00, // |
551 | ] |
552 | ); |
553 | } |
554 | |
555 | #[test ] |
556 | fn raw_u24_be() { |
557 | let mut fb = <framebuffer!(Rgb888, BigEndian, 3, 2)>::new(); |
558 | |
559 | fb.draw_iter( |
560 | [ |
561 | ((0, 0), 0x100000), // |
562 | ((2, 1), 0x000001), // |
563 | ((1, 0), 0x123456), // |
564 | ((1, 1), 0x876543), // |
565 | ((-1, 0), 0xFFFFFF), // |
566 | ((0, -1), 0xFFFFFF), // |
567 | ((3, 0), 0xFFFFFF), // |
568 | ((0, 2), 0xFFFFFF), // |
569 | ] |
570 | .iter() |
571 | .map(|(p, c)| Pixel(Point::from(*p), Rgb888::from(RawU24::new(*c)))), |
572 | ) |
573 | .unwrap(); |
574 | |
575 | assert_eq!( |
576 | fb.data(), |
577 | &[ |
578 | 0x10, 0x00, 0x00, 0x12, 0x34, 0x56, 0x00, 0x00, 0x00, // |
579 | 0x00, 0x00, 0x00, 0x87, 0x65, 0x43, 0x00, 0x00, 0x01, // |
580 | ] |
581 | ); |
582 | } |
583 | |
584 | #[test ] |
585 | fn raw_u32_le() { |
586 | let mut fb = <framebuffer!(U32Color, 3, 2)>::new(); |
587 | |
588 | fb.draw_iter( |
589 | [ |
590 | ((0, 0), 0x10000000), // |
591 | ((2, 1), 0x00000001), // |
592 | ((1, 0), 0x12345678), // |
593 | ((1, 1), 0x87654321), // |
594 | ((-1, 0), 0xFFFFFFFF), // |
595 | ((0, -1), 0xFFFFFFFF), // |
596 | ((3, 0), 0xFFFFFFFF), // |
597 | ((0, 2), 0xFFFFFFFF), // |
598 | ] |
599 | .iter() |
600 | .map(|(p, c)| Pixel(Point::from(*p), U32Color::from(RawU32::new(*c)))), |
601 | ) |
602 | .unwrap(); |
603 | |
604 | assert_eq!( |
605 | fb.data(), |
606 | &[ |
607 | 0x00, 0x00, 0x00, 0x10, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, // |
608 | 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x65, 0x87, 0x01, 0x00, 0x00, 0x00, // |
609 | ] |
610 | ); |
611 | } |
612 | |
613 | #[test ] |
614 | fn raw_u32_be() { |
615 | let mut fb = <framebuffer!(U32Color, BigEndian, 3, 2)>::new(); |
616 | |
617 | fb.draw_iter( |
618 | [ |
619 | ((0, 0), 0x10000000), // |
620 | ((2, 1), 0x00000001), // |
621 | ((1, 0), 0x12345678), // |
622 | ((1, 1), 0x87654321), // |
623 | ((-1, 0), 0xFFFFFFFF), // |
624 | ((0, -1), 0xFFFFFFFF), // |
625 | ((3, 0), 0xFFFFFFFF), // |
626 | ((0, 2), 0xFFFFFFFF), // |
627 | ] |
628 | .iter() |
629 | .map(|(p, c)| Pixel(Point::from(*p), U32Color::from(RawU32::new(*c)))), |
630 | ) |
631 | .unwrap(); |
632 | |
633 | assert_eq!( |
634 | fb.data(), |
635 | &[ |
636 | 0x10, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00, // |
637 | 0x00, 0x00, 0x00, 0x00, 0x87, 0x65, 0x43, 0x21, 0x00, 0x00, 0x00, 0x01, // |
638 | ] |
639 | ); |
640 | } |
641 | |
642 | #[test ] |
643 | fn as_image() { |
644 | let mut fb = <framebuffer!(BinaryColor, 10, 10)>::new(); |
645 | |
646 | fb.bounding_box() |
647 | .into_styled(PrimitiveStyle::with_stroke(BinaryColor::On, 1)) |
648 | .draw(&mut fb) |
649 | .unwrap(); |
650 | |
651 | fb.draw_iter( |
652 | [(7, 2), (8, 8)] |
653 | .into_iter() |
654 | .map(|p| Pixel(Point::from(p), BinaryColor::On)), |
655 | ) |
656 | .unwrap(); |
657 | |
658 | let mut display = MockDisplay::<BinaryColor>::new(); |
659 | Image::new(&fb.as_image(), Point::new(2, 1)) |
660 | .draw(&mut display) |
661 | .unwrap(); |
662 | |
663 | display.assert_pattern(&[ |
664 | " " , |
665 | " ##########" , |
666 | " #........#" , |
667 | " #......#.#" , |
668 | " #........#" , |
669 | " #........#" , |
670 | " #........#" , |
671 | " #........#" , |
672 | " #........#" , |
673 | " #.......##" , |
674 | " ##########" , |
675 | ]); |
676 | } |
677 | |
678 | #[test ] |
679 | fn pixel() { |
680 | let mut fb = <framebuffer!(BinaryColor, 10, 10)>::new(); |
681 | |
682 | fb.draw_iter( |
683 | [(7, 2), (8, 8)] |
684 | .into_iter() |
685 | .map(|p| Pixel(Point::from(p), BinaryColor::On)), |
686 | ) |
687 | .unwrap(); |
688 | |
689 | let expected = [ |
690 | ((0, 0), Some(BinaryColor::Off)), |
691 | ((1, 0), Some(BinaryColor::Off)), |
692 | ((1, 1), Some(BinaryColor::Off)), |
693 | ((7, 2), Some(BinaryColor::On)), |
694 | ((8, 8), Some(BinaryColor::On)), |
695 | ((-1, 0), None), |
696 | ((0, -1), None), |
697 | ((10, 0), None), |
698 | ((0, 10), None), |
699 | ]; |
700 | for (p, c) in expected { |
701 | assert_eq!(fb.pixel(p.into()), c, "{p:?}" ); |
702 | } |
703 | } |
704 | |
705 | #[test ] |
706 | fn set_pixel() { |
707 | // This tests only checks that the set_pixel methods are present for all BPPs. |
708 | // The correct function is tested indirectly in the DrawTarget tests. |
709 | |
710 | <framebuffer!(BinaryColor, 10, 10)>::new().set_pixel(Point::zero(), BinaryColor::On); |
711 | <framebuffer!(Gray2, 10, 10)>::new().set_pixel(Point::zero(), Gray2::WHITE); |
712 | <framebuffer!(Gray4, 10, 10)>::new().set_pixel(Point::zero(), Gray4::WHITE); |
713 | <framebuffer!(Gray8, 10, 10)>::new().set_pixel(Point::zero(), Gray8::WHITE); |
714 | <framebuffer!(Rgb565, 10, 10)>::new().set_pixel(Point::zero(), Rgb565::WHITE); |
715 | <framebuffer!(Rgb888, 10, 10)>::new().set_pixel(Point::zero(), Rgb888::WHITE); |
716 | <framebuffer!(U32Color, 10, 10)>::new().set_pixel(Point::zero(), U32Color(0)); |
717 | } |
718 | |
719 | #[test ] |
720 | fn oversized_buffer() { |
721 | let fb = Framebuffer::< |
722 | BinaryColor, |
723 | _, |
724 | LittleEndian, |
725 | 10, |
726 | 5, |
727 | { buffer_size::<BinaryColor>(10, 5) * 3 / 2 }, |
728 | >::new(); |
729 | |
730 | assert_eq!(fb.size(), Size::new(10, 5)); |
731 | assert_eq!(fb.as_image().size(), Size::new(10, 5)); |
732 | |
733 | let outside_x = Point::zero() + fb.size().x_axis(); |
734 | let outside_y = Point::zero() + fb.size().y_axis(); |
735 | |
736 | assert_eq!(fb.pixel(outside_x), None); |
737 | assert_eq!(fb.pixel(outside_y), None); |
738 | |
739 | let mut fb2 = fb.clone(); |
740 | fb2.set_pixel(outside_x, BinaryColor::On); |
741 | fb2.set_pixel(outside_y, BinaryColor::On); |
742 | |
743 | assert_eq!(fb, fb2); |
744 | } |
745 | } |
746 | |