1 | use core::marker::PhantomData; |
2 | |
3 | use crate::{ |
4 | draw_target::DrawTarget, |
5 | geometry::{Dimensions, OriginDimensions, Point, Size}, |
6 | image::{GetPixel, ImageDrawable}, |
7 | iterator::raw::RawDataSlice, |
8 | pixelcolor::{ |
9 | raw::{BigEndian, ByteOrder, LittleEndian, RawData}, |
10 | PixelColor, |
11 | }, |
12 | primitives::Rectangle, |
13 | }; |
14 | |
15 | /// Image with little endian data. |
16 | pub type ImageRawLE<'a, C> = ImageRaw<'a, C, LittleEndian>; |
17 | |
18 | /// Image with big endian data. |
19 | pub type ImageRawBE<'a, C> = ImageRaw<'a, C, BigEndian>; |
20 | |
21 | /// An image constructed from a slice of raw pixel data. |
22 | /// |
23 | /// The `ImageRaw` struct can be used to construct an image from a slice |
24 | /// of raw image data. The storage format is determined by the [`PixelColor`] |
25 | /// type `C` and the [`ByteOrder`] `BO`. The byteorder doesn't need to be |
26 | /// specified for colors which aren't stored in multiple bytes. |
27 | /// |
28 | /// For color types with less than 8 bits per pixels the start of each row is |
29 | /// aligned to the next whole byte. |
30 | /// |
31 | /// Details about the conversion of raw data to color types are explained in the |
32 | /// [`raw` module documentation]. |
33 | /// |
34 | /// To draw an `ImageRaw` object it needs to be wrapped in an [`Image`] object. |
35 | /// |
36 | /// # Examples |
37 | /// |
38 | /// ## Draw a 1BPP image |
39 | /// |
40 | /// This example creates an image from 1 bit per pixel data. |
41 | /// |
42 | /// ``` |
43 | /// use embedded_graphics::{ |
44 | /// image::{Image, ImageRaw}, |
45 | /// pixelcolor::BinaryColor, |
46 | /// prelude::*, |
47 | /// }; |
48 | /// # use embedded_graphics::mock_display::MockDisplay as Display; |
49 | /// |
50 | /// /// 12 x 5 pixel image with 1 bit per pixel. |
51 | /// /// The data for each row is 12 bits long and is padded with zeros on the |
52 | /// /// end because each row needs to contain a whole number of bytes. |
53 | /// #[rustfmt::skip] |
54 | /// const DATA: &[u8] = &[ |
55 | /// 0b11101111, 0b0101_0000, |
56 | /// 0b10001000, 0b0101_0000, |
57 | /// 0b11101011, 0b0101_0000, |
58 | /// 0b10001001, 0b0101_0000, |
59 | /// 0b11101111, 0b0101_0000, |
60 | /// ]; |
61 | /// |
62 | /// // The image dimensions and the format of the stored raw data must be specified |
63 | /// // when the `new` function is called. The data format can, for example, be specified |
64 | /// // by using the turbofish syntax. For the image dimensions only the width must be |
65 | /// // passed to the `new` function. The image height will be calculated based on the |
66 | /// // length of the image data and the data format. |
67 | /// let raw_image = ImageRaw::<BinaryColor>::new(DATA, 12); |
68 | /// |
69 | /// let image = Image::new(&raw_image, Point::zero()); |
70 | /// |
71 | /// let mut display = Display::default(); |
72 | /// |
73 | /// image.draw(&mut display)?; |
74 | /// # Ok::<(), core::convert::Infallible>(()) |
75 | /// ``` |
76 | /// |
77 | /// ## Draw an image that uses multibyte pixel encoding |
78 | /// |
79 | /// Colors with more than one byte per pixel need an additional type annotation for the byte order. |
80 | /// For convenience, the [`ImageRawBE`] and [`ImageRawLE`] type aliases can be used to abbreviate |
81 | /// the type. |
82 | /// |
83 | /// ``` |
84 | /// use embedded_graphics::{ |
85 | /// image::{Image, ImageRaw, ImageRawBE, ImageRawLE}, |
86 | /// pixelcolor::{ |
87 | /// raw::{BigEndian, LittleEndian}, |
88 | /// Rgb565, Rgb888, |
89 | /// }, |
90 | /// prelude::*, |
91 | /// }; |
92 | /// # const DATA: &[u8] = &[0x55; 8 * 8 * 3]; |
93 | /// |
94 | /// // Rgb888 image with 24 bits per pixel and big endian byte order |
95 | /// let image1 = ImageRawBE::<Rgb888>::new(DATA, 8); |
96 | /// // or: |
97 | /// let image2 = ImageRaw::<Rgb888, BigEndian>::new(DATA, 8); |
98 | /// # assert_eq!(image1, image2); |
99 | /// |
100 | /// // Rgb565 image with 16 bits per pixel and little endian byte order |
101 | /// let image1 = ImageRawLE::<Rgb565>::new(DATA, 16); |
102 | /// // or: |
103 | /// let image2 = ImageRaw::<Rgb565, LittleEndian>::new(DATA, 16); |
104 | /// # assert_eq!(image1, image2); |
105 | /// ``` |
106 | /// |
107 | /// [`raw` module documentation]: crate::pixelcolor::raw |
108 | /// [`Image`]: crate::image::Image |
109 | /// [`Drawable`]: crate::drawable::Drawable |
110 | /// [`PixelColor`]: crate::pixelcolor::PixelColor |
111 | /// [`ByteOrder`]: crate::pixelcolor::raw::ByteOrder |
112 | #[derive (Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] |
113 | #[cfg_attr (feature = "defmt" , derive(::defmt::Format))] |
114 | pub struct ImageRaw<'a, C, BO = BigEndian> |
115 | where |
116 | C: PixelColor + From<<C as PixelColor>::Raw>, |
117 | BO: ByteOrder, |
118 | { |
119 | /// Image data, packed as dictated by raw data type `C::Raw` |
120 | data: &'a [u8], |
121 | |
122 | /// Image size in pixels |
123 | size: Size, |
124 | |
125 | pixel_type: PhantomData<C>, |
126 | byte_order: PhantomData<BO>, |
127 | } |
128 | |
129 | impl<'a, C, BO> ImageRaw<'a, C, BO> |
130 | where |
131 | C: PixelColor + From<<C as PixelColor>::Raw>, |
132 | BO: ByteOrder, |
133 | { |
134 | /// Creates a new image. |
135 | /// |
136 | /// Only the width of the image needs to be specified. The height of the image will be |
137 | /// calculated based on the length of the given image data. If the length of the image data |
138 | /// isn't an integer multiple of the data length for a single row the last partial row will |
139 | /// be ignored. |
140 | pub const fn new(data: &'a [u8], width: u32) -> Self { |
141 | // Prevent panic for `width == 0` by returning a zero sized image. |
142 | if width == 0 { |
143 | return Self { |
144 | data: &[], |
145 | size: Size::zero(), |
146 | pixel_type: PhantomData, |
147 | byte_order: PhantomData, |
148 | }; |
149 | } |
150 | |
151 | let height = data.len() / bytes_per_row(width, C::Raw::BITS_PER_PIXEL); |
152 | |
153 | Self { |
154 | data, |
155 | size: Size::new(width, height as u32), |
156 | pixel_type: PhantomData, |
157 | byte_order: PhantomData, |
158 | } |
159 | } |
160 | |
161 | /// Returns the actual row width in pixels. |
162 | /// |
163 | /// For images with less than 8 bits per pixel each row is padded to contain an integer number |
164 | /// of bytes. This method returns the width of each row including the padding pixels. |
165 | const fn data_width(&self) -> u32 { |
166 | if C::Raw::BITS_PER_PIXEL < 8 { |
167 | let pixels_per_byte = 8 / C::Raw::BITS_PER_PIXEL as u32; |
168 | |
169 | bytes_per_row(self.size.width, C::Raw::BITS_PER_PIXEL) as u32 * pixels_per_byte |
170 | } else { |
171 | self.size.width |
172 | } |
173 | } |
174 | } |
175 | |
176 | /// Returns the length of each row in bytes. |
177 | const fn bytes_per_row(width: u32, bits_per_pixel: usize) -> usize { |
178 | (width as usize * bits_per_pixel + 7) / 8 |
179 | } |
180 | |
181 | impl<'a, C, BO> ImageDrawable for ImageRaw<'a, C, BO> |
182 | where |
183 | C: PixelColor + From<<C as PixelColor>::Raw>, |
184 | BO: ByteOrder, |
185 | RawDataSlice<'a, C::Raw, BO>: IntoIterator<Item = C::Raw>, |
186 | { |
187 | type Color = C; |
188 | |
189 | fn draw<D>(&self, target: &mut D) -> Result<(), D::Error> |
190 | where |
191 | D: DrawTarget<Color = C>, |
192 | { |
193 | let row_skip = self.data_width() - self.size.width; |
194 | |
195 | target.fill_contiguous( |
196 | &self.bounding_box(), |
197 | ContiguousPixels::new(self, self.size, 0, row_skip as usize), |
198 | ) |
199 | } |
200 | |
201 | fn draw_sub_image<D>(&self, target: &mut D, area: &Rectangle) -> Result<(), D::Error> |
202 | where |
203 | D: DrawTarget<Color = Self::Color>, |
204 | { |
205 | // Don't draw anything if `area` is zero sized or partially outside the image. |
206 | if area.is_zero_sized() |
207 | || area.top_left.x < 0 |
208 | || area.top_left.y < 0 |
209 | || area.top_left.x as u32 + area.size.width > self.size.width |
210 | || area.top_left.y as u32 + area.size.height > self.size.height |
211 | { |
212 | return Ok(()); |
213 | } |
214 | |
215 | let data_width = self.data_width() as usize; |
216 | |
217 | let initial_skip = area.top_left.y as usize * data_width + area.top_left.x as usize; |
218 | let row_skip = data_width - area.size.width as usize; |
219 | |
220 | target.fill_contiguous( |
221 | &Rectangle::new(Point::zero(), area.size), |
222 | ContiguousPixels::new(self, area.size, initial_skip, row_skip), |
223 | ) |
224 | } |
225 | } |
226 | |
227 | impl<C, BO> OriginDimensions for ImageRaw<'_, C, BO> |
228 | where |
229 | C: PixelColor + From<<C as PixelColor>::Raw>, |
230 | BO: ByteOrder, |
231 | { |
232 | fn size(&self) -> Size { |
233 | self.size |
234 | } |
235 | } |
236 | |
237 | impl<'a, C, BO> GetPixel for ImageRaw<'a, C, BO> |
238 | where |
239 | C: PixelColor + From<<C as PixelColor>::Raw>, |
240 | BO: ByteOrder, |
241 | RawDataSlice<'a, C::Raw, BO>: IntoIterator<Item = C::Raw>, |
242 | { |
243 | type Color = C; |
244 | |
245 | fn pixel(&self, p: Point) -> Option<Self::Color> { |
246 | if p.x < 0 || p.y < 0 || p.x >= self.size.width as i32 || p.y >= self.size.height as i32 { |
247 | return None; |
248 | } |
249 | |
250 | RawDataSliceOption<{unknown}>::new(self.data) |
251 | .into_iter() |
252 | .nth(p.x as usize + p.y as usize * self.data_width() as usize) |
253 | .map(|r| r.into()) |
254 | } |
255 | } |
256 | |
257 | struct ContiguousPixels<'a, C, BO> |
258 | where |
259 | C: PixelColor + From<<C as PixelColor>::Raw>, |
260 | BO: ByteOrder, |
261 | RawDataSlice<'a, C::Raw, BO>: IntoIterator<Item = C::Raw>, |
262 | { |
263 | iter: <RawDataSlice<'a, C::Raw, BO> as IntoIterator>::IntoIter, |
264 | |
265 | remaining_x: u32, |
266 | width: u32, |
267 | |
268 | remaining_y: u32, |
269 | row_skip: usize, |
270 | } |
271 | |
272 | impl<'a, C, BO> ContiguousPixels<'a, C, BO> |
273 | where |
274 | C: PixelColor + From<<C as PixelColor>::Raw>, |
275 | BO: ByteOrder, |
276 | RawDataSlice<'a, C::Raw, BO>: IntoIterator<Item = C::Raw>, |
277 | { |
278 | fn new(image: &ImageRaw<'a, C, BO>, size: Size, initial_skip: usize, row_skip: usize) -> Self { |
279 | let mut iter: ::Raw, …> as IntoIterator>::IntoIter = RawDataSlice::new(image.data).into_iter(); |
280 | |
281 | if initial_skip > 0 { |
282 | iter.nth(initial_skip - 1); |
283 | } |
284 | |
285 | // Set `remaining_y` to `0` if `width == 0` to prevent integer underflow in `next`. |
286 | let remaining_y: u32 = if size.width > 0 { size.height } else { 0 }; |
287 | |
288 | Self { |
289 | iter, |
290 | remaining_x: size.width, |
291 | width: size.width, |
292 | remaining_y, |
293 | row_skip, |
294 | } |
295 | } |
296 | } |
297 | |
298 | impl<'a, C, BO> Iterator for ContiguousPixels<'a, C, BO> |
299 | where |
300 | C: PixelColor + From<<C as PixelColor>::Raw>, |
301 | BO: ByteOrder, |
302 | RawDataSlice<'a, C::Raw, BO>: IntoIterator<Item = C::Raw>, |
303 | { |
304 | type Item = C; |
305 | |
306 | fn next(&mut self) -> Option<Self::Item> { |
307 | ifOption<::Raw> self.remaining_x > 0 { |
308 | self.remaining_x -= 1; |
309 | |
310 | self.iter.next() |
311 | } else { |
312 | if self.remaining_y == 0 { |
313 | return None; |
314 | } |
315 | |
316 | self.remaining_y -= 1; |
317 | self.remaining_x = self.width - 1; |
318 | |
319 | self.iter.nth(self.row_skip) |
320 | } |
321 | .map(|c: ::Raw| c.into()) |
322 | } |
323 | } |
324 | |
325 | #[cfg (test)] |
326 | mod tests { |
327 | use super::*; |
328 | use crate::{ |
329 | draw_target::DrawTarget, |
330 | geometry::Point, |
331 | image::Image, |
332 | iterator::PixelIteratorExt, |
333 | mock_display::{ColorMapping, MockDisplay}, |
334 | pixelcolor::{raw::RawU32, *}, |
335 | Drawable, Pixel, |
336 | }; |
337 | |
338 | #[derive (Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)] |
339 | struct TestColorU32(RawU32); |
340 | |
341 | impl PixelColor for TestColorU32 { |
342 | type Raw = RawU32; |
343 | } |
344 | |
345 | impl From<RawU32> for TestColorU32 { |
346 | fn from(data: RawU32) -> Self { |
347 | Self(data) |
348 | } |
349 | } |
350 | |
351 | /// Tests if the given image data matches an excepted `MockDisplay` pattern. |
352 | fn assert_pattern<C, BO>(image_data: ImageRaw<C, BO>, expected_pattern: &[&str]) |
353 | where |
354 | C: PixelColor + From<<C as PixelColor>::Raw> + ColorMapping, |
355 | BO: ByteOrder, |
356 | for<'a> RawDataSlice<'a, C::Raw, BO>: IntoIterator<Item = C::Raw>, |
357 | { |
358 | let image = Image::new(&image_data, Point::zero()); |
359 | let mut display = MockDisplay::new(); |
360 | image.draw(&mut display).unwrap(); |
361 | |
362 | display.assert_pattern(expected_pattern); |
363 | } |
364 | |
365 | #[test ] |
366 | fn image_dimensions() { |
367 | let data = [ |
368 | 0xAA, 0x00, // |
369 | 0x55, 0xFF, // |
370 | 0xAA, 0x80, // |
371 | ]; |
372 | let image_data: ImageRaw<BinaryColor> = ImageRaw::new(&data, 9); |
373 | |
374 | assert_eq!(image_data.size(), Size::new(9, 3)); |
375 | } |
376 | |
377 | #[test ] |
378 | fn truncated_data() { |
379 | let data = [ |
380 | 0xAA, 0x00, // |
381 | 0x55, 0xFF, // |
382 | 0xAA, // |
383 | ]; |
384 | let image_data: ImageRaw<BinaryColor> = ImageRaw::new(&data, 9); |
385 | |
386 | assert_pattern( |
387 | image_data, |
388 | &[ |
389 | "#.#.#.#.." , // |
390 | ".#.#.#.##" , // |
391 | ], |
392 | ); |
393 | } |
394 | |
395 | #[test ] |
396 | fn bpp1_new() { |
397 | let data = [ |
398 | 0xAA, 0x00, // |
399 | 0x55, 0xFF, // |
400 | 0xAA, 0x80, // |
401 | ]; |
402 | let image_data: ImageRaw<BinaryColor> = ImageRaw::new(&data, 9); |
403 | |
404 | assert_pattern( |
405 | image_data, |
406 | &[ |
407 | "#.#.#.#.." , // |
408 | ".#.#.#.##" , // |
409 | "#.#.#.#.#" , // |
410 | ], |
411 | ); |
412 | } |
413 | |
414 | #[test ] |
415 | fn bpp1_get_pixel() { |
416 | let data = [ |
417 | 0xAA, 0x00, // |
418 | 0x55, 0xFF, // |
419 | 0xAA, 0x80, // |
420 | ]; |
421 | let image_data: ImageRaw<BinaryColor> = ImageRaw::new(&data, 9); |
422 | |
423 | assert_eq!(image_data.pixel(Point::new(0, 0)), Some(BinaryColor::On)); |
424 | assert_eq!(image_data.pixel(Point::new(8, 0)), Some(BinaryColor::Off)); |
425 | assert_eq!(image_data.pixel(Point::new(0, 1)), Some(BinaryColor::Off)); |
426 | assert_eq!(image_data.pixel(Point::new(8, 1)), Some(BinaryColor::On)); |
427 | assert_eq!(image_data.pixel(Point::new(0, 2)), Some(BinaryColor::On)); |
428 | assert_eq!(image_data.pixel(Point::new(8, 2)), Some(BinaryColor::On)); |
429 | } |
430 | |
431 | #[test ] |
432 | fn bpp2() { |
433 | let data = [ |
434 | 0b00_01_10_11, // |
435 | 0b00_00_00_00, // |
436 | 0b11_10_01_00, // |
437 | 0b11_11_11_11, // |
438 | ]; |
439 | let image_data: ImageRaw<Gray2> = ImageRaw::new(&data, 5); |
440 | |
441 | assert_pattern( |
442 | image_data, |
443 | &[ |
444 | "01230" , // |
445 | "32103" , // |
446 | ], |
447 | ); |
448 | } |
449 | |
450 | #[test ] |
451 | fn bpp4() { |
452 | let data = [ |
453 | 0b0001_1000, // |
454 | 0b1111_0000, // |
455 | 0b0101_1010, // |
456 | 0b0000_0000, // |
457 | ]; |
458 | let image_data: ImageRaw<Gray4> = ImageRaw::new(&data, 3); |
459 | |
460 | assert_pattern( |
461 | image_data, |
462 | &[ |
463 | "18F" , // |
464 | "5A0" , // |
465 | ], |
466 | ); |
467 | } |
468 | |
469 | #[test ] |
470 | fn bpp8_1() { |
471 | let data = [ |
472 | 0x11, 0x22, // |
473 | 0x33, 0x44, // |
474 | 0x55, 0x66, // |
475 | ]; |
476 | let image_data: ImageRaw<Gray8> = ImageRaw::new(&data, 2); |
477 | |
478 | assert_pattern( |
479 | image_data, |
480 | &[ |
481 | "12" , // |
482 | "34" , // |
483 | "56" , // |
484 | ], |
485 | ); |
486 | } |
487 | |
488 | /// Additional test for luma values with different low and high nibbles, |
489 | /// which are not supported by `MockDisplay` patterns. |
490 | #[test ] |
491 | fn bpp8_2() { |
492 | let data = [0x01, 0x08, 0x10, 0x80]; |
493 | let image_data: ImageRaw<Gray8> = ImageRaw::new(&data, 4); |
494 | |
495 | let mut display = MockDisplay::new(); |
496 | Image::new(&image_data, Point::zero()) |
497 | .draw(&mut display) |
498 | .unwrap(); |
499 | |
500 | let mut expected = MockDisplay::new(); |
501 | expected |
502 | .fill_contiguous( |
503 | &expected.bounding_box(), |
504 | data.iter().copied().map(Gray8::new), |
505 | ) |
506 | .unwrap(); |
507 | |
508 | display.assert_eq(&expected); |
509 | } |
510 | |
511 | #[test ] |
512 | fn bpp16_little_endian() { |
513 | let data = [ |
514 | 0x00, 0xF8, // |
515 | 0xE0, 0x07, // |
516 | 0x1F, 0x00, // |
517 | 0x00, 0x00, // |
518 | ]; |
519 | let image_data: ImageRawLE<Rgb565> = ImageRaw::new(&data, 1); |
520 | |
521 | assert_pattern( |
522 | image_data, |
523 | &[ |
524 | "R" , // |
525 | "G" , // |
526 | "B" , // |
527 | "K" , // |
528 | ], |
529 | ); |
530 | } |
531 | |
532 | #[test ] |
533 | fn bpp16_big_endian() { |
534 | let data = [ |
535 | 0xF8, 0x00, // |
536 | 0x07, 0xE0, // |
537 | 0x00, 0x1F, // |
538 | 0x00, 0x00, // |
539 | ]; |
540 | let image_data: ImageRawBE<Rgb565> = ImageRaw::new(&data, 2); |
541 | |
542 | assert_pattern( |
543 | image_data, |
544 | &[ |
545 | "RG" , // |
546 | "BK" , // |
547 | ], |
548 | ); |
549 | } |
550 | |
551 | #[test ] |
552 | fn bpp16_big_endian_get_pixel() { |
553 | let data = [ |
554 | 0xF8, 0x00, // |
555 | 0x07, 0xE0, // |
556 | 0x00, 0x1F, // |
557 | 0x00, 0x00, // |
558 | ]; |
559 | let image_data: ImageRawBE<Rgb565> = ImageRaw::new(&data, 2); |
560 | |
561 | assert_eq!(image_data.pixel(Point::new(0, 0)), Some(Rgb565::RED)); |
562 | assert_eq!(image_data.pixel(Point::new(1, 0)), Some(Rgb565::GREEN)); |
563 | assert_eq!(image_data.pixel(Point::new(0, 1)), Some(Rgb565::BLUE)); |
564 | assert_eq!(image_data.pixel(Point::new(1, 1)), Some(Rgb565::BLACK)); |
565 | } |
566 | |
567 | #[test ] |
568 | fn bpp24_little_endian() { |
569 | let data = [ |
570 | 0xFF, 0x00, 0x00, // |
571 | 0x00, 0xFF, 0x00, // |
572 | 0x00, 0x00, 0xFF, // |
573 | 0x00, 0x00, 0x00, // |
574 | ]; |
575 | let image_data: ImageRawLE<Bgr888> = ImageRaw::new(&data, 1); |
576 | |
577 | assert_pattern( |
578 | image_data, |
579 | &[ |
580 | "R" , // |
581 | "G" , // |
582 | "B" , // |
583 | "K" , // |
584 | ], |
585 | ); |
586 | } |
587 | |
588 | #[test ] |
589 | fn bpp24_big_endian() { |
590 | let data = [ |
591 | 0xFF, 0x00, 0x00, // |
592 | 0x00, 0xFF, 0x00, // |
593 | 0x00, 0x00, 0xFF, // |
594 | 0x00, 0x00, 0x00, // |
595 | ]; |
596 | let image_data: ImageRawBE<Rgb888> = ImageRaw::new(&data, 4); |
597 | |
598 | assert_pattern(image_data, &["RGBK" ]); |
599 | } |
600 | |
601 | #[test ] |
602 | fn bpp32_little_endian() { |
603 | let data = [ |
604 | 0x12, 0x34, 0x56, 0x78, // |
605 | 0x9A, 0xBC, 0xDE, 0xF0, // |
606 | 0x00, 0x00, 0x00, 0x00, // |
607 | 0xFF, 0xFF, 0xFF, 0xFF, // |
608 | ]; |
609 | let image_data: ImageRawLE<TestColorU32> = ImageRaw::new(&data, 2); |
610 | |
611 | let mut display = MockDisplay::new(); |
612 | Image::new(&image_data, Point::zero()) |
613 | .draw(&mut display) |
614 | .unwrap(); |
615 | |
616 | let expected = [ |
617 | Pixel(Point::new(0, 0), TestColorU32(RawU32::new(0x78563412))), |
618 | Pixel(Point::new(1, 0), TestColorU32(RawU32::new(0xF0DEBC9A))), |
619 | Pixel(Point::new(0, 1), TestColorU32(RawU32::new(0x00000000))), |
620 | Pixel(Point::new(1, 1), TestColorU32(RawU32::new(0xFFFFFFFF))), |
621 | ]; |
622 | |
623 | let mut expected_display = MockDisplay::new(); |
624 | expected |
625 | .iter() |
626 | .copied() |
627 | .draw(&mut expected_display) |
628 | .unwrap(); |
629 | |
630 | // assert_eq can't be used here because ColorMapping isn't implemented for TestColorU32 |
631 | assert!(display.eq(&expected_display)); |
632 | } |
633 | |
634 | #[test ] |
635 | fn bpp32_big_endian() { |
636 | let data = [ |
637 | 0x12, 0x34, 0x56, 0x78, // |
638 | 0x9A, 0xBC, 0xDE, 0xF0, // |
639 | 0x00, 0x00, 0x00, 0x00, // |
640 | 0xFF, 0xFF, 0xFF, 0xFF, // |
641 | ]; |
642 | let image_data: ImageRawBE<TestColorU32> = ImageRaw::new(&data, 4); |
643 | |
644 | let mut display = MockDisplay::new(); |
645 | Image::new(&image_data, Point::zero()) |
646 | .draw(&mut display) |
647 | .unwrap(); |
648 | |
649 | let expected = [ |
650 | Pixel(Point::new(0, 0), TestColorU32(RawU32::new(0x12345678))), |
651 | Pixel(Point::new(1, 0), TestColorU32(RawU32::new(0x9ABCDEF0))), |
652 | Pixel(Point::new(2, 0), TestColorU32(RawU32::new(0x00000000))), |
653 | Pixel(Point::new(3, 0), TestColorU32(RawU32::new(0xFFFFFFFF))), |
654 | ]; |
655 | |
656 | let mut expected_display = MockDisplay::new(); |
657 | expected |
658 | .iter() |
659 | .copied() |
660 | .draw(&mut expected_display) |
661 | .unwrap(); |
662 | |
663 | // assert_eq can't be used here because ColorMapping isn't implemented for TestColorU32 |
664 | assert!(display.eq(&expected_display)); |
665 | } |
666 | |
667 | #[test ] |
668 | fn calculated_height() { |
669 | let data = [0u8; 1]; |
670 | assert_eq!(ImageRaw::<BinaryColor>::new(&data, 12).size().height, 0); |
671 | |
672 | let data = [0u8; 2]; |
673 | assert_eq!(ImageRaw::<BinaryColor>::new(&data, 12).size().height, 1); |
674 | |
675 | let data = [0u8; 3]; |
676 | assert_eq!(ImageRaw::<BinaryColor>::new(&data, 12).size().height, 1); |
677 | |
678 | let data = [0u8; 4]; |
679 | assert_eq!(ImageRaw::<BinaryColor>::new(&data, 12).size().height, 2); |
680 | } |
681 | |
682 | #[test ] |
683 | fn binary_image_with_zero_width() { |
684 | let image = ImageRaw::<BinaryColor>::new(&[], 0); |
685 | |
686 | assert_eq!(image.size, Size::zero()); |
687 | } |
688 | |
689 | #[test ] |
690 | fn pixel_out_of_bounds() { |
691 | let data = [ |
692 | 0xAA, 0x00, // |
693 | 0x55, 0xFF, // |
694 | 0xAA, 0x80, // |
695 | ]; |
696 | let image_data = ImageRaw::<BinaryColor>::new(&data, 9); |
697 | |
698 | assert_eq!(image_data.pixel(Point::new(-1, 0)), None); |
699 | assert_eq!(image_data.pixel(Point::new(0, -1)), None); |
700 | assert_eq!(image_data.pixel(Point::new(9, 0)), None); |
701 | assert_eq!(image_data.pixel(Point::new(9, 3)), None); |
702 | } |
703 | } |
704 | |