ici_files/
image.rs

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    /// Replace palette for image
68    /// Will only return an error if the new palette has less colors than the image needs
69    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    /// Replace palette for image, any pixels outside the new palette will be replaced with `id`
79    /// Will only return an error if id is outside the new palette
80    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    /// Replace palette for image, any color indexes outside the palette will be expanded with `color`
105    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    /// # Safety
141    ///
142    /// Out of bounds may occur
143    #[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    /// # Safety
163    ///
164    /// Out of bounds may occur
165    #[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    /// # Safety
181    ///
182    /// Out of bounds may occur
183    #[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    /// # Safety
197    ///
198    /// Out of bounds may occur
199    #[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    /// # Safety
214    ///
215    /// Out of bounds may occur
216    #[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    /// # Safety
256    ///
257    /// Out of bounds may occur
258    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    /// # Safety
287    ///
288    /// Out of bounds may occur
289    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    /// # Safety
317    ///
318    /// Out of bounds may occur
319    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    /// # Safety
352    ///
353    /// Out of bounds may occur
354    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    /// # Safety
384    ///
385    /// Out of bounds may occur
386    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    /// Errors will only be returned if you [FilePalette::Name] and the len is invalid
441    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    /// Create an [IndexedImage], image palette will be filled with transparency unless file contains colors
455    /// use `image.set_palette*` to replace the palette
456    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}