1use core::marker::PhantomData;
2
3use 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
15pub type ImageRawLE<'a, C> = ImageRaw<'a, C, LittleEndian>;
17
18pub type ImageRawBE<'a, C> = ImageRaw<'a, C, BigEndian>;
20
21#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
113#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
114pub struct ImageRaw<'a, C, BO = BigEndian>
115where
116 C: PixelColor + From<<C as PixelColor>::Raw>,
117 BO: ByteOrder,
118{
119 data: &'a [u8],
121
122 size: Size,
124
125 pixel_type: PhantomData<C>,
126 byte_order: PhantomData<BO>,
127}
128
129impl<'a, C, BO> ImageRaw<'a, C, BO>
130where
131 C: PixelColor + From<<C as PixelColor>::Raw>,
132 BO: ByteOrder,
133{
134 pub const fn new(data: &'a [u8], width: u32) -> Self {
141 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 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
176const fn bytes_per_row(width: u32, bits_per_pixel: usize) -> usize {
178 (width as usize * bits_per_pixel + 7) / 8
179}
180
181impl<'a, C, BO> ImageDrawable for ImageRaw<'a, C, BO>
182where
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 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
227impl<C, BO> OriginDimensions for ImageRaw<'_, C, BO>
228where
229 C: PixelColor + From<<C as PixelColor>::Raw>,
230 BO: ByteOrder,
231{
232 fn size(&self) -> Size {
233 self.size
234 }
235}
236
237impl<'a, C, BO> GetPixel for ImageRaw<'a, C, BO>
238where
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 RawDataSlice::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
257struct ContiguousPixels<'a, C, BO>
258where
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
272impl<'a, C, BO> ContiguousPixels<'a, C, BO>
273where
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 = RawDataSlice::new(image.data).into_iter();
280
281 if initial_skip > 0 {
282 iter.nth(initial_skip - 1);
283 }
284
285 let remaining_y = 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
298impl<'a, C, BO> Iterator for ContiguousPixels<'a, C, BO>
299where
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 if 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| c.into())
322 }
323}
324
325#[cfg(test)]
326mod 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 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, 0x55, 0xFF, 0xAA, 0x80, ];
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, 0x55, 0xFF, 0xAA, ];
384 let image_data: ImageRaw<BinaryColor> = ImageRaw::new(&data, 9);
385
386 assert_pattern(
387 image_data,
388 &[
389 "#.#.#.#..", ".#.#.#.##", ],
392 );
393 }
394
395 #[test]
396 fn bpp1_new() {
397 let data = [
398 0xAA, 0x00, 0x55, 0xFF, 0xAA, 0x80, ];
402 let image_data: ImageRaw<BinaryColor> = ImageRaw::new(&data, 9);
403
404 assert_pattern(
405 image_data,
406 &[
407 "#.#.#.#..", ".#.#.#.##", "#.#.#.#.#", ],
411 );
412 }
413
414 #[test]
415 fn bpp1_get_pixel() {
416 let data = [
417 0xAA, 0x00, 0x55, 0xFF, 0xAA, 0x80, ];
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, 0b00_00_00_00, 0b11_10_01_00, 0b11_11_11_11, ];
439 let image_data: ImageRaw<Gray2> = ImageRaw::new(&data, 5);
440
441 assert_pattern(
442 image_data,
443 &[
444 "01230", "32103", ],
447 );
448 }
449
450 #[test]
451 fn bpp4() {
452 let data = [
453 0b0001_1000, 0b1111_0000, 0b0101_1010, 0b0000_0000, ];
458 let image_data: ImageRaw<Gray4> = ImageRaw::new(&data, 3);
459
460 assert_pattern(
461 image_data,
462 &[
463 "18F", "5A0", ],
466 );
467 }
468
469 #[test]
470 fn bpp8_1() {
471 let data = [
472 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, ];
476 let image_data: ImageRaw<Gray8> = ImageRaw::new(&data, 2);
477
478 assert_pattern(
479 image_data,
480 &[
481 "12", "34", "56", ],
485 );
486 }
487
488 #[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, 0xE0, 0x07, 0x1F, 0x00, 0x00, 0x00, ];
519 let image_data: ImageRawLE<Rgb565> = ImageRaw::new(&data, 1);
520
521 assert_pattern(
522 image_data,
523 &[
524 "R", "G", "B", "K", ],
529 );
530 }
531
532 #[test]
533 fn bpp16_big_endian() {
534 let data = [
535 0xF8, 0x00, 0x07, 0xE0, 0x00, 0x1F, 0x00, 0x00, ];
540 let image_data: ImageRawBE<Rgb565> = ImageRaw::new(&data, 2);
541
542 assert_pattern(
543 image_data,
544 &[
545 "RG", "BK", ],
548 );
549 }
550
551 #[test]
552 fn bpp16_big_endian_get_pixel() {
553 let data = [
554 0xF8, 0x00, 0x07, 0xE0, 0x00, 0x1F, 0x00, 0x00, ];
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, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, ];
575 let image_data: ImageRawLE<Bgr888> = ImageRaw::new(&data, 1);
576
577 assert_pattern(
578 image_data,
579 &[
580 "R", "G", "B", "K", ],
585 );
586 }
587
588 #[test]
589 fn bpp24_big_endian() {
590 let data = [
591 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, ];
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, 0x9A, 0xBC, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, ];
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!(display.eq(&expected_display));
632 }
633
634 #[test]
635 fn bpp32_big_endian() {
636 let data = [
637 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, ];
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!(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, 0x55, 0xFF, 0xAA, 0x80, ];
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}