1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use byteorder::{BigEndian, ReadBytesExt};
use colors::{BitDepth, ColorType};
use crc::crc32;
use error::PngError;
use std::io::Cursor;
#[derive(Debug, Clone, Copy)]
pub struct IhdrData {
pub width: u32,
pub height: u32,
pub color_type: ColorType,
pub bit_depth: BitDepth,
pub compression: u8,
pub filter: u8,
pub interlaced: u8,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Headers {
None,
Some(Vec<String>),
Safe,
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<Option<(String, Vec<u8>)>, PngError> {
let mut rdr = Cursor::new(byte_data
.iter()
.skip(*byte_offset)
.take(4)
.cloned()
.collect::<Vec<u8>>());
let length: u32 = match rdr.read_u32::<BigEndian>() {
Ok(x) => x,
Err(_) => return Err(PngError::new("Invalid data found; unable to read PNG file")),
};
*byte_offset += 4;
let mut header_bytes: Vec<u8> = 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" {
return Ok(None);
}
*byte_offset += 4;
let data: Vec<u8> = 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::<Vec<u8>>());
let crc: u32 = match rdr.read_u32::<BigEndian>() {
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<IhdrData, PngError> {
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::<BigEndian>().unwrap(),
height: rdr.read_u32::<BigEndian>().unwrap(),
compression: byte_data[10],
filter: byte_data[11],
interlaced: byte_data[12],
})
}