| 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 | |