1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4use crate::errors::IndexedImageError;
5use crate::errors::IndexedImageError::*;
6use crate::file::FileType::Image;
7use crate::file::{verify_format, HEADER};
8use crate::palette;
9use crate::palette::FilePalette;
10use crate::prelude::*;
11use crate::scaling::*;
12
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14#[derive(Debug, Clone, Eq, PartialEq)]
15pub struct IndexedImage {
16 width: u8,
17 height: u8,
18 palette: Vec<Color>,
19 pixels: Vec<u8>,
20 highest_palette_idx: u8,
21}
22
23impl IndexedImage {
24 pub fn new(
25 width: u8,
26 height: u8,
27 palette: Vec<Color>,
28 pixels: Vec<u8>,
29 ) -> Result<Self, IndexedImageError> {
30 if width == 0 {
31 return Err(WidthIsZero);
32 }
33 if height == 0 {
34 return Err(HeightIsZero);
35 }
36 if palette.is_empty() {
37 return Err(PaletteIsEmpty);
38 }
39 if pixels.len() != (width as usize * height as usize) {
40 return Err(MissingData(pixels.len(), width as usize * height as usize));
41 }
42 let highest_palette_idx = *pixels
43 .iter()
44 .max()
45 .expect("Unable to get highest color index");
46 Ok(Self {
47 width,
48 height,
49 palette,
50 pixels,
51 highest_palette_idx,
52 })
53 }
54
55 pub fn blank(width: u8, height: u8, palette: Vec<Color>) -> Self {
56 Self {
57 width,
58 height,
59 palette,
60 pixels: vec![0; width as usize * height as usize],
61 highest_palette_idx: 0,
62 }
63 }
64}
65
66impl IndexedImage {
67 pub fn set_palette(&mut self, palette: &[Color]) -> Result<(), IndexedImageError> {
70 assert!(!palette.is_empty());
71 if palette.len() < self.highest_palette_idx as usize {
72 return Err(PaletteTooFewColors(self.highest_palette_idx));
73 }
74 self.palette = palette.to_vec();
75 Ok(())
76 }
77
78 pub fn set_palette_replace_id(
81 &mut self,
82 palette: &[Color],
83 id: u8,
84 ) -> Result<(), IndexedImageError> {
85 let new_palette_len = palette.len() as u8;
86 assert!(!palette.is_empty());
87 if new_palette_len <= id {
88 return Err(IdOutsideOfNewPalette);
89 }
90 self.palette = palette.to_vec();
91 for i in self.pixels.iter_mut() {
92 if *i >= new_palette_len {
93 *i = id;
94 }
95 }
96 self.highest_palette_idx = *self
97 .pixels
98 .iter()
99 .max()
100 .expect("Unable to get highest color index");
101 Ok(())
102 }
103
104 pub fn set_palette_replace_color<C: Into<Color> + Copy>(
106 &mut self,
107 palette: &[Color],
108 color: C,
109 ) {
110 assert!(!palette.is_empty());
111 let mut tmp_pal = palette.to_vec();
112 while tmp_pal.len() <= self.highest_palette_idx as usize {
113 tmp_pal.push(color.into());
114 }
115 self.palette = tmp_pal;
116 }
117
118 #[inline]
119 pub fn size(&self) -> (u8, u8) {
120 (self.width, self.height)
121 }
122
123 #[inline]
124 pub fn set_pixel(&mut self, pixel_idx: usize, color_idx: u8) -> Result<(), IndexedImageError> {
125 if pixel_idx >= self.pixels.len() {
126 return Err(IndexOutOfRange(pixel_idx, self.pixels.len(), "pixels"));
127 }
128 if color_idx >= self.palette.len() as u8 {
129 return Err(IndexOutOfRange(
130 color_idx as usize,
131 self.palette.len(),
132 "palette",
133 ));
134 }
135 self.pixels[pixel_idx] = color_idx;
136 self.highest_palette_idx = self.highest_palette_idx.max(color_idx);
137 Ok(())
138 }
139
140 #[inline]
144 pub unsafe fn set_pixel_unchecked(&mut self, pixel_idx: usize, color_idx: u8) {
145 self.highest_palette_idx = self.highest_palette_idx.max(color_idx);
146 self.pixels[pixel_idx] = color_idx;
147 }
148
149 #[inline]
150 pub fn get_pixels(&self) -> &[u8] {
151 &self.pixels
152 }
153
154 #[inline]
155 pub fn get_pixel(&self, pixel_idx: usize) -> Result<u8, IndexedImageError> {
156 if pixel_idx >= self.pixels.len() {
157 return Err(IndexOutOfRange(pixel_idx, self.pixels.len(), "pixels"));
158 }
159 Ok(self.pixels[pixel_idx])
160 }
161
162 #[inline]
166 pub unsafe fn get_pixel_unchecked(&self, pixel_idx: usize) -> u8 {
167 self.pixels[pixel_idx]
168 }
169
170 pub fn get_pixel_index(&self, x: u8, y: u8) -> Result<usize, IndexedImageError> {
171 if x >= self.width {
172 return Err(IndexOutOfRange(x as usize, self.width as usize, "width"));
173 }
174 if y >= self.height {
175 return Err(IndexOutOfRange(y as usize, self.height as usize, "height"));
176 }
177 Ok(x as usize + y as usize * self.width as usize)
178 }
179
180 #[inline]
184 pub unsafe fn get_pixel_index_unchecked(&self, x: u8, y: u8) -> usize {
185 x as usize + y as usize * self.width as usize
186 }
187
188 #[inline]
189 pub fn get_color(&self, idx: u8) -> Result<Color, IndexedImageError> {
190 if idx >= self.palette.len() as u8 {
191 return Err(IndexOutOfRange(idx as usize, self.palette.len(), "palette"));
192 }
193 Ok(self.palette[idx as usize])
194 }
195
196 #[inline]
200 pub unsafe fn get_color_unchecked(&self, idx: u8) -> Color {
201 self.palette[idx as usize]
202 }
203
204 #[inline]
205 pub fn set_color(&mut self, idx: u8, color: Color) -> Result<(), IndexedImageError> {
206 if idx >= self.palette.len() as u8 {
207 return Err(IndexOutOfRange(idx as usize, self.palette.len(), "palette"));
208 }
209 self.palette[idx as usize] = color;
210 Ok(())
211 }
212
213 #[inline]
217 pub fn set_color_unchecked(&mut self, idx: u8, color: Color) {
218 self.palette[idx as usize] = color;
219 }
220
221 #[inline]
222 pub fn get_palette(&self) -> &[Color] {
223 &self.palette
224 }
225
226 #[inline]
227 pub fn min_palette_size_supported(&self) -> u8 {
228 self.highest_palette_idx
229 }
230
231 #[inline]
232 pub fn width(&self) -> u8 {
233 self.width
234 }
235
236 #[inline]
237 pub fn height(&self) -> u8 {
238 self.height
239 }
240
241 pub fn rotate_cw(&self) -> IndexedImage {
242 let mut output = IndexedImage::blank(self.height, self.width, self.palette.clone());
243 for y in 0..self.height {
244 for x in 0..self.width {
245 let new_y = x;
246 let new_x = output.width - y - 1;
247 let new_i = output.get_pixel_index(new_x, new_y).unwrap();
248 let i = output.get_pixel_index(x, y).unwrap();
249 output.set_pixel(new_i, self.get_pixel(i).unwrap()).unwrap();
250 }
251 }
252 output
253 }
254
255 pub unsafe fn rotate_cw_unchecked(&self) -> IndexedImage {
259 let mut output = IndexedImage::blank(self.height, self.width, self.palette.clone());
260 for y in 0..self.height {
261 for x in 0..self.width {
262 let new_y = x;
263 let new_x = output.width - y - 1;
264 let new_i = output.get_pixel_index_unchecked(new_x, new_y);
265 let i = output.get_pixel_index_unchecked(x, y);
266 output.set_pixel_unchecked(new_i, self.get_pixel_unchecked(i));
267 }
268 }
269 output
270 }
271
272 pub fn rotate_ccw(&self) -> IndexedImage {
273 let mut output = IndexedImage::blank(self.height, self.width, self.palette.clone());
274 for y in 0..self.height {
275 for x in 0..self.width {
276 let new_y = output.height - x - 1;
277 let new_x = y;
278 let new_i = output.get_pixel_index(new_x, new_y).unwrap();
279 let i = output.get_pixel_index(x, y).unwrap();
280 output.set_pixel(new_i, self.get_pixel(i).unwrap()).unwrap();
281 }
282 }
283 output
284 }
285
286 pub unsafe fn rotate_ccw_unchecked(&self) -> IndexedImage {
290 let mut output = IndexedImage::blank(self.height, self.width, self.palette.clone());
291 for y in 0..self.height {
292 for x in 0..self.width {
293 let new_y = output.height - x - 1;
294 let new_x = y;
295 let new_i = output.get_pixel_index_unchecked(new_x, new_y);
296 let i = output.get_pixel_index_unchecked(x, y);
297 output.set_pixel_unchecked(new_i, self.get_pixel_unchecked(i));
298 }
299 }
300 output
301 }
302
303 pub fn flip_vertical(&self) -> Result<IndexedImage, IndexedImageError> {
304 let mut output = IndexedImage::blank(self.width, self.height, self.palette.clone());
305 for y in 0..self.height {
306 let target_y = self.height - 1 - y;
307 for x in 0..self.width {
308 let target_i = output.get_pixel_index(x, target_y)?;
309 let source_i = self.get_pixel_index(x, y)?;
310 output.set_pixel(target_i, self.get_pixel(source_i)?)?;
311 }
312 }
313 Ok(output)
314 }
315
316 pub unsafe fn flip_vertical_unchecked(&self) -> IndexedImage {
320 let mut output = self.clone();
321 let half_height = (output.height as f32 / 2.).floor() as u8;
322 for y in 0..half_height {
323 std::ptr::swap_nonoverlapping(
324 &mut output.pixels[y as usize * output.width as usize],
325 &mut output.pixels[(output.height - 1 - y) as usize * output.width as usize],
326 output.width as usize,
327 );
328 }
329 output
330 }
331
332 pub fn flip_horizontal(&self) -> Result<IndexedImage, IndexedImageError> {
333 let mut output = IndexedImage::blank(self.width, self.height, self.palette.clone());
334 let half_width = (self.width as f32 / 2.).floor() as u8;
335 for y in 0..self.height {
336 for x in 0..half_width {
337 let target_right_i = output.get_pixel_index(self.width - x - 1, y)?;
338 let source_left_i = self.get_pixel_index(x, y)?;
339 let target_left_i = output.get_pixel_index(x, y)?;
340 let source_right_i = self.get_pixel_index(self.width - 1 - x, y)?;
341 let source_left = self.get_pixel(source_left_i)?;
342 let source_right = self.get_pixel(source_right_i)?;
343
344 output.set_pixel(target_left_i, source_right)?;
345 output.set_pixel(target_right_i, source_left)?;
346 }
347 }
348 Ok(output)
349 }
350
351 pub unsafe fn flip_horizontal_unchecked(&self) -> IndexedImage {
355 let mut output = IndexedImage::blank(self.width, self.height, self.palette.clone());
356 let half_width = (self.width as f32 / 2.).floor() as u8;
357 for y in 0..self.height {
358 for x in 0..half_width {
359 let target_right_i = output.get_pixel_index_unchecked(self.width - 1 - x, y);
360 let source_left_i = self.get_pixel_index_unchecked(x, y);
361 let target_left_i = output.get_pixel_index_unchecked(x, y);
362 let source_right_i = self.get_pixel_index_unchecked(self.width - 1 - x, y);
363 let source_left = self.get_pixel_unchecked(source_left_i);
364 let source_right = self.get_pixel_unchecked(source_right_i);
365
366 output.set_pixel_unchecked(target_left_i, source_right);
367 output.set_pixel_unchecked(target_right_i, source_left);
368 }
369 }
370 output
371 }
372
373 pub fn scale(&self, algo: Scaling) -> Result<IndexedImage, IndexedImageError> {
374 match algo {
375 Scaling::NearestNeighbour { x_scale, y_scale } => {
376 scale_nearest_neighbor(self, usize::from(x_scale), usize::from(y_scale))
377 }
378 Scaling::Epx2x => scale_epx(self),
379 Scaling::Epx4x => scale_epx(&scale_epx(self)?),
380 }
381 }
382
383 pub unsafe fn scale_unchecked(&self, algo: Scaling) -> IndexedImage {
387 match algo {
388 Scaling::NearestNeighbour { x_scale, y_scale } => {
389 scale_nearest_neighbor_unchecked(self, usize::from(x_scale), usize::from(y_scale))
390 }
391 Scaling::Epx2x => scale_epx_unchecked(self),
392 Scaling::Epx4x => scale_epx_unchecked(&scale_epx_unchecked(self)),
393 }
394 }
395
396 pub fn tint_palette_add(&self, color_diff: &[(isize, isize, isize, isize)]) -> IndexedImage {
397 let mut output = self.clone();
398
399 for (i, color) in output.palette.iter_mut().enumerate() {
400 let diff = color_diff[i];
401 color.tint_add(diff.0, diff.1, diff.2, diff.3)
402 }
403
404 output
405 }
406
407 pub fn tint_palette_mut(&self, color_diff: &[(f32, f32, f32, f32)]) -> IndexedImage {
408 let mut output = self.clone();
409
410 for (i, color) in output.palette.iter_mut().enumerate() {
411 let diff = color_diff[i];
412 color.tint_mul(diff.0, diff.1, diff.2, diff.3)
413 }
414
415 output
416 }
417
418 pub fn tint_add(&self, color_diff: &(isize, isize, isize, isize)) -> IndexedImage {
419 let mut output = self.clone();
420
421 for color in output.palette.iter_mut() {
422 color.tint_add(color_diff.0, color_diff.1, color_diff.2, color_diff.3)
423 }
424
425 output
426 }
427
428 pub fn tint_mul(&self, color_diff: &(f32, f32, f32, f32)) -> IndexedImage {
429 let mut output = self.clone();
430
431 for color in output.palette.iter_mut() {
432 color.tint_mul(color_diff.0, color_diff.1, color_diff.2, color_diff.3)
433 }
434
435 output
436 }
437}
438
439impl IndexedImage {
440 pub fn to_file_contents(&self, palette: &FilePalette) -> Result<Vec<u8>, IndexedImageError> {
442 let mut output = vec![];
443 output.extend_from_slice(&HEADER);
444 output.push(Image.to_byte());
445
446 palette::write(palette, self.get_palette(), &mut output)?;
447 output.push(self.width);
448 output.push(self.height);
449 output.extend_from_slice(&self.pixels);
450
451 Ok(output)
452 }
453
454 pub fn from_file_contents(
457 bytes: &[u8],
458 ) -> Result<(IndexedImage, FilePalette), IndexedImageError> {
459 let file_type = verify_format(bytes)?;
460 if file_type != Image {
461 return Err(InvalidFileFormat(
462 0,
463 format!("Expected Image file but found {}", file_type.name()),
464 ));
465 }
466 let idx = HEADER.len() + 1;
467 let (skip, pal_type, colors) = palette::read(idx, bytes)?;
468
469 let start = idx + skip;
470 if bytes.len() < start + 3 {
471 return Err(InvalidFileFormat(
472 start,
473 "Incomplete pixels data".to_string(),
474 ));
475 }
476 let width = bytes[start];
477 let height = bytes[start + 1];
478 let pixels_len = width as usize * height as usize;
479 if bytes.len() < start + 2 + pixels_len {
480 return Err(InvalidFileFormat(
481 start + 2,
482 format!(
483 "Incomplete pixels data, found {} but expected {}",
484 pixels_len,
485 width * height
486 ),
487 ));
488 }
489 let pixels = &bytes[start + 2..start + 2 + pixels_len];
490
491 let highest = *pixels.iter().max().expect("Invalid pixels data") as usize;
492 let colors = match colors {
493 None => vec![TRANSPARENT; highest + 1],
494 Some(colors) => colors,
495 };
496
497 IndexedImage::new(width, height, colors, pixels.to_vec()).map(|image| (image, pal_type))
498 }
499}
500
501#[cfg(test)]
502mod test {
503 use crate::palette::FilePalette::*;
504
505 use super::*;
506
507 #[test]
508 fn write_and_read_no_data() {
509 let width = 2;
510 let height = 2;
511 let input = IndexedImage::new(
512 2,
513 2,
514 vec![
515 TRANSPARENT,
516 Color::new(50, 51, 52, 53),
517 Color::new(60, 61, 62, 63),
518 ],
519 vec![0, 0, 1, 2],
520 )
521 .unwrap();
522 let bytes = input.to_file_contents(&NoData).unwrap();
523 assert_eq!(
524 bytes,
525 vec![
526 HEADER[0],
527 HEADER[1],
528 HEADER[2],
529 HEADER[3],
530 Image.to_byte(),
531 NoData.to_byte(),
532 width,
533 height,
534 0,
535 0,
536 1,
537 2,
538 ]
539 );
540 let (output, pal) = IndexedImage::from_file_contents(&bytes).unwrap();
541 let mut cloned = input.clone();
542 cloned
543 .set_palette(&[TRANSPARENT, TRANSPARENT, TRANSPARENT])
544 .unwrap();
545 assert_eq!(cloned, output);
546 assert_eq!(pal, NoData);
547 }
548
549 #[test]
550 fn write_and_read_id() {
551 let width = 2;
552 let height = 2;
553 let input = IndexedImage::new(
554 2,
555 2,
556 vec![
557 TRANSPARENT,
558 Color::new(50, 51, 52, 53),
559 Color::new(60, 61, 62, 63),
560 ],
561 vec![0, 0, 1, 2],
562 )
563 .unwrap();
564 let bytes = input.to_file_contents(&ID(15)).unwrap();
565 assert_eq!(
566 bytes,
567 vec![
568 HEADER[0],
569 HEADER[1],
570 HEADER[2],
571 HEADER[3],
572 Image.to_byte(),
573 ID(0).to_byte(),
574 0,
575 15,
576 width,
577 height,
578 0,
579 0,
580 1,
581 2,
582 ]
583 );
584 let (output, pal) = IndexedImage::from_file_contents(&bytes).unwrap();
585 let mut cloned = input.clone();
586 cloned
587 .set_palette(&[TRANSPARENT, TRANSPARENT, TRANSPARENT])
588 .unwrap();
589 assert_eq!(cloned, output);
590 assert_eq!(pal, ID(15));
591 }
592
593 #[test]
594 fn write_and_read_name() {
595 let width = 2;
596 let height = 2;
597 let input = IndexedImage::new(
598 2,
599 2,
600 vec![
601 TRANSPARENT,
602 Color::new(50, 51, 52, 53),
603 Color::new(60, 61, 62, 63),
604 ],
605 vec![0, 0, 1, 2],
606 )
607 .unwrap();
608 let bytes = input.to_file_contents(&Name("Test".to_string())).unwrap();
609 assert_eq!(
610 bytes,
611 vec![
612 HEADER[0],
613 HEADER[1],
614 HEADER[2],
615 HEADER[3],
616 Image.to_byte(),
617 Name(String::new()).to_byte(),
618 4,
619 b'T',
620 b'e',
621 b's',
622 b't',
623 width,
624 height,
625 0,
626 0,
627 1,
628 2,
629 ]
630 );
631 let (output, pal) = IndexedImage::from_file_contents(&bytes).unwrap();
632 let mut cloned = input.clone();
633 cloned
634 .set_palette(&[TRANSPARENT, TRANSPARENT, TRANSPARENT])
635 .unwrap();
636 assert_eq!(cloned, output);
637 assert_eq!(pal, Name("Test".to_string()));
638 }
639
640 #[test]
641 fn write_and_read_colors() {
642 let width = 2;
643 let height = 2;
644 let input = IndexedImage::new(
645 2,
646 2,
647 vec![
648 TRANSPARENT,
649 Color::new(50, 51, 52, 53),
650 Color::new(60, 61, 62, 63),
651 ],
652 vec![0, 0, 1, 2],
653 )
654 .unwrap();
655 let bytes = input.to_file_contents(&Colors).unwrap();
656 assert_eq!(
657 bytes,
658 vec![
659 HEADER[0],
660 HEADER[1],
661 HEADER[2],
662 HEADER[3],
663 Image.to_byte(),
664 Colors.to_byte(),
665 3,
666 0,
667 0,
668 0,
669 0,
670 50,
671 51,
672 52,
673 53,
674 60,
675 61,
676 62,
677 63,
678 width,
679 height,
680 0,
681 0,
682 1,
683 2,
684 ]
685 );
686 let (output, pal) = IndexedImage::from_file_contents(&bytes).unwrap();
687 assert_eq!(input, output);
688 assert_eq!(pal, Colors);
689 }
690
691 #[test]
692 fn set_palette() {
693 let image = IndexedImage::new(
694 3,
695 3,
696 vec![Color::new(255, 255, 255, 255), Color::new(0, 0, 0, 255)],
697 vec![0, 1, 0, 1, 0, 1, 0, 1, 0],
698 )
699 .unwrap();
700 let mut modified = image.clone();
701 modified
702 .set_palette(&[Color::new(255, 0, 0, 255), Color::new(0, 255, 0, 255)])
703 .unwrap();
704 assert_eq!(image.highest_palette_idx, modified.highest_palette_idx);
705 assert_eq!(image.height, modified.height);
706 assert_eq!(image.width, modified.width);
707 assert_eq!(image.pixels, image.pixels);
708 assert_eq!(
709 modified.palette,
710 vec![Color::new(255, 0, 0, 255), Color::new(0, 255, 0, 255)]
711 );
712 }
713
714 #[test]
715 fn set_palette_id() {
716 let image = IndexedImage::new(
717 2,
718 4,
719 vec![
720 Color::new(1, 1, 1, 1),
721 Color::new(2, 2, 2, 2),
722 Color::new(3, 3, 3, 3),
723 ],
724 vec![0, 1, 2, 2, 0, 1, 2, 0],
725 )
726 .unwrap();
727 let mut modified = image.clone();
728 modified
729 .set_palette_replace_id(&[Color::new(5, 5, 5, 5)], 0)
730 .unwrap();
731 assert_eq!(modified.highest_palette_idx, 0);
732 assert_eq!(image.height, modified.height);
733 assert_eq!(image.width, modified.width);
734 assert_eq!(image.pixels, image.pixels);
735 assert_eq!(modified.palette, vec![Color::new(5, 5, 5, 5)]);
736 }
737
738 #[test]
739 fn set_palette_color() {
740 let image = IndexedImage::new(
741 2,
742 4,
743 vec![
744 Color::new(1, 1, 1, 1),
745 Color::new(2, 2, 2, 2),
746 Color::new(3, 3, 3, 3),
747 ],
748 vec![0, 1, 2, 2, 0, 1, 2, 0],
749 )
750 .unwrap();
751 let mut modified = image.clone();
752 modified.set_palette_replace_color(&[Color::new(5, 5, 5, 5)], Color::new(5, 5, 5, 5));
753 assert_eq!(modified.highest_palette_idx, 2);
754 assert_eq!(image.height, modified.height);
755 assert_eq!(image.width, modified.width);
756 assert_eq!(image.pixels, image.pixels);
757 assert_eq!(
758 modified.palette,
759 vec![
760 Color::new(5, 5, 5, 5),
761 Color::new(5, 5, 5, 5),
762 Color::new(5, 5, 5, 5),
763 ]
764 );
765 }
766
767 #[test]
768 fn test_ignores_extra_data() {
769 let data = vec![
770 HEADER[0],
771 HEADER[1],
772 HEADER[2],
773 HEADER[3],
774 Image.to_byte(),
775 Colors.to_byte(),
776 3,
777 0,
778 0,
779 0,
780 0,
781 50,
782 51,
783 52,
784 53,
785 60,
786 61,
787 62,
788 63,
789 2,
790 2,
791 0,
792 0,
793 1,
794 2,
795 100,
796 100,
797 100,
798 ];
799 let image = IndexedImage::new(
800 2,
801 2,
802 vec![
803 TRANSPARENT,
804 Color::new(50, 51, 52, 53),
805 Color::new(60, 61, 62, 63),
806 ],
807 vec![0, 0, 1, 2],
808 )
809 .unwrap();
810 let input = IndexedImage::from_file_contents(&data).unwrap();
811 assert_eq!(image, input.0);
812 }
813
814 #[test]
815 fn pixel_accessors() {
816 let mut image = IndexedImage::new(
817 2,
818 4,
819 vec![
820 Color::new(1, 1, 1, 1),
821 Color::new(2, 2, 2, 2),
822 Color::new(3, 3, 3, 3),
823 ],
824 vec![0, 1, 2, 2, 0, 1, 2, 0],
825 )
826 .unwrap();
827 let idx = image.get_pixel_index(1, 2).unwrap();
828 assert!(image.get_pixel_index(4, 4).is_err());
829 assert_eq!(image.get_pixel(idx).unwrap(), 1);
830 assert!(image.set_pixel(idx, 2).is_ok());
831 assert_eq!(image.get_pixel(idx).unwrap(), 2);
832 }
833}