use byteorder::{BigEndian, ReadBytesExt}; use colors::{BitDepth, ColorType}; use crc::crc32; use error::PngError; use std::io::Cursor; #[derive(Debug, Clone, Copy)] /// Headers from the IHDR chunk of the image pub struct IhdrData { /// The width of the image in pixels pub width: u32, /// The height of the image in pixels pub height: u32, /// The color type of the image pub color_type: ColorType, /// The bit depth of the image pub bit_depth: BitDepth, /// The compression method used for this image (0 for DEFLATE) pub compression: u8, /// The filter mode used for this image (currently only 0 is valid) pub filter: u8, /// The interlacing mode of the image (0 = None, 1 = Adam7) pub interlaced: u8, } #[derive(Debug, PartialEq, Clone)] /// Options to use for performing operations on headers (such as stripping) pub enum Headers { /// None None, /// Some, with a list of 4-character chunk codes Some(Vec), /// Headers that won't affect rendering (all but cHRM, gAMA, iCCP, sBIT, sRGB, bKGD, hIST, pHYs, sPLT) Safe, /// All non-critical headers All, } #[inline] pub fn file_header_is_valid(bytes: &[u8]) -> bool { let expected_header: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]; *bytes == expected_header } pub fn parse_next_header(byte_data: &[u8], byte_offset: &mut usize, fix_errors: bool) -> Result)>, PngError> { let mut rdr = Cursor::new(byte_data.iter() .skip(*byte_offset) .take(4) .cloned() .collect::>()); let length: u32 = match rdr.read_u32::() { Ok(x) => x, Err(_) => return Err(PngError::new("Invalid data found; unable to read PNG file")), }; *byte_offset += 4; let mut header_bytes: Vec = byte_data.iter().skip(*byte_offset).take(4).cloned().collect(); let header = match String::from_utf8(header_bytes.clone()) { Ok(x) => x, Err(_) => return Err(PngError::new("Invalid data found; unable to read PNG file")), }; if header == "IEND" { // End of data return Ok(None); } *byte_offset += 4; let data: Vec = byte_data.iter() .skip(*byte_offset) .take(length as usize) .cloned() .collect(); *byte_offset += length as usize; let mut rdr = Cursor::new(byte_data.iter() .skip(*byte_offset) .take(4) .cloned() .collect::>()); let crc: u32 = match rdr.read_u32::() { Ok(x) => x, Err(_) => return Err(PngError::new("Invalid data found; unable to read PNG file")), }; *byte_offset += 4; header_bytes.extend_from_slice(&data); if !fix_errors && crc32::checksum_ieee(header_bytes.as_ref()) != crc { return Err(PngError::new(&format!("CRC Mismatch in {} header; May be recoverable by using --fix", header))); } Ok(Some((header, data))) } pub fn parse_ihdr_header(byte_data: &[u8]) -> Result { let mut rdr = Cursor::new(&byte_data[0..8]); Ok(IhdrData { color_type: match byte_data[9] { 0 => ColorType::Grayscale, 2 => ColorType::RGB, 3 => ColorType::Indexed, 4 => ColorType::GrayscaleAlpha, 6 => ColorType::RGBA, _ => return Err(PngError::new("Unexpected color type in header")), }, bit_depth: match byte_data[8] { 1 => BitDepth::One, 2 => BitDepth::Two, 4 => BitDepth::Four, 8 => BitDepth::Eight, 16 => BitDepth::Sixteen, _ => return Err(PngError::new("Unexpected bit depth in header")), }, width: rdr.read_u32::().unwrap(), height: rdr.read_u32::().unwrap(), compression: byte_data[10], filter: byte_data[11], interlaced: byte_data[12], }) }