fn read_image()

in image/src/codecs/ico/decoder.rs [295:385]


    fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
        match self.inner_decoder {
            PNG(decoder) => {
                if self.selected_entry.image_length < PNG_SIGNATURE.len() as u32 {
                    return Err(DecoderError::PngShorterThanHeader.into());
                }

                // Check if the image dimensions match the ones in the image data.
                let (width, height) = decoder.dimensions();
                if !self.selected_entry.matches_dimensions(width, height) {
                    return Err(DecoderError::ImageEntryDimensionMismatch {
                        format: IcoEntryImageFormat::Png,
                        entry: (self.selected_entry.real_width(), self.selected_entry.real_height()),
                        image: (width, height)
                    }.into());
                }

                // Embedded PNG images can only be of the 32BPP RGBA format.
                // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473/
                if decoder.color_type() != ColorType::Rgba8 {
                    return Err(DecoderError::PngNotRgba.into());
                }

                decoder.read_image(buf)
            }
            BMP(mut decoder) => {
                let (width, height) = decoder.dimensions();
                if !self.selected_entry.matches_dimensions(width, height) {
                    return Err(DecoderError::ImageEntryDimensionMismatch {
                        format: IcoEntryImageFormat::Bmp,
                        entry: (self.selected_entry.real_width(), self.selected_entry.real_height()),
                        image: (width, height)
                    }.into());
                }

                // The ICO decoder needs an alpha channel to apply the AND mask.
                if decoder.color_type() != ColorType::Rgba8 {
                    return Err(ImageError::Unsupported(UnsupportedError::from_format_and_kind(
                        ImageFormat::Bmp.into(),
                        UnsupportedErrorKind::Color(decoder.color_type().into()),
                    )));
                }

                decoder.read_image_data(buf)?;

                let r = decoder.reader();
                let image_end = r.seek(SeekFrom::Current(0))?;
                let data_end =
                    u64::from(self.selected_entry.image_offset + self.selected_entry.image_length);

                let mask_row_bytes = ((width + 31) / 32) * 4;
                let mask_length = u64::from(mask_row_bytes) * u64::from(height);

                // data_end should be image_end + the mask length (mask_row_bytes * height).
                // According to
                // https://devblogs.microsoft.com/oldnewthing/20101021-00/?p=12483
                // the mask is required, but according to Wikipedia
                // https://en.wikipedia.org/wiki/ICO_(file_format)
                // the mask is not required. Unfortunately, Wikipedia does not have a citation
                // for that claim, so we can't be sure which is correct.
                if data_end >= image_end + mask_length {
                    // If there's an AND mask following the image, read and apply it.
                    for y in 0..height {
                        let mut x = 0;
                        for _ in 0..mask_row_bytes {
                            // Apply the bits of each byte until we reach the end of the row.
                            let mask_byte = r.read_u8()?;
                            for bit in (0..8).rev() {
                                if x >= width {
                                    break;
                                }
                                if mask_byte & (1 << bit) != 0 {
                                    // Set alpha channel to transparent.
                                    buf[((height - y - 1) * width + x) as usize * 4 + 3] = 0;
                                }
                                x += 1;
                            }
                        }
                    }

                    Ok(())
                } else if data_end == image_end {
                    // accept images with no mask data
                    Ok(())
                } else {
                    Err(DecoderError::InvalidDataSize.into())
                }
            }
        }
    }