1 | //! Decoding of netpbm image formats (pbm, pgm, ppm and pam). |
2 | //! |
3 | //! The formats pbm, pgm and ppm are fully supported. The pam decoder recognizes the tuple types |
4 | //! `BLACKANDWHITE`, `GRAYSCALE` and `RGB` and explicitly recognizes but rejects their `_ALPHA` |
5 | //! variants for now as alpha color types are unsupported. |
6 | use self::autobreak::AutoBreak; |
7 | pub use self::decoder::PnmDecoder; |
8 | pub use self::encoder::PnmEncoder; |
9 | use self::header::HeaderRecord; |
10 | pub use self::header::{ |
11 | ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader, |
12 | }; |
13 | pub use self::header::{PnmHeader, PnmSubtype, SampleEncoding}; |
14 | |
15 | mod autobreak; |
16 | mod decoder; |
17 | mod encoder; |
18 | mod header; |
19 | |
20 | #[cfg (test)] |
21 | mod tests { |
22 | use super::*; |
23 | use crate::color::ColorType; |
24 | use crate::image::ImageDecoder; |
25 | use byteorder::{ByteOrder, NativeEndian}; |
26 | |
27 | fn execute_roundtrip_default(buffer: &[u8], width: u32, height: u32, color: ColorType) { |
28 | let mut encoded_buffer = Vec::new(); |
29 | |
30 | { |
31 | let mut encoder = PnmEncoder::new(&mut encoded_buffer); |
32 | encoder |
33 | .encode(buffer, width, height, color) |
34 | .expect("Failed to encode the image buffer" ); |
35 | } |
36 | |
37 | let (header, loaded_color, loaded_image) = { |
38 | let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap(); |
39 | let color_type = decoder.color_type(); |
40 | let mut image = vec![0; decoder.total_bytes() as usize]; |
41 | decoder |
42 | .read_image(&mut image) |
43 | .expect("Failed to decode the image" ); |
44 | let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner(); |
45 | (header, color_type, image) |
46 | }; |
47 | |
48 | assert_eq!(header.width(), width); |
49 | assert_eq!(header.height(), height); |
50 | assert_eq!(loaded_color, color); |
51 | assert_eq!(loaded_image.as_slice(), buffer); |
52 | } |
53 | |
54 | fn execute_roundtrip_with_subtype( |
55 | buffer: &[u8], |
56 | width: u32, |
57 | height: u32, |
58 | color: ColorType, |
59 | subtype: PnmSubtype, |
60 | ) { |
61 | let mut encoded_buffer = Vec::new(); |
62 | |
63 | { |
64 | let mut encoder = PnmEncoder::new(&mut encoded_buffer).with_subtype(subtype); |
65 | encoder |
66 | .encode(buffer, width, height, color) |
67 | .expect("Failed to encode the image buffer" ); |
68 | } |
69 | |
70 | let (header, loaded_color, loaded_image) = { |
71 | let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap(); |
72 | let color_type = decoder.color_type(); |
73 | let mut image = vec![0; decoder.total_bytes() as usize]; |
74 | decoder |
75 | .read_image(&mut image) |
76 | .expect("Failed to decode the image" ); |
77 | let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner(); |
78 | (header, color_type, image) |
79 | }; |
80 | |
81 | assert_eq!(header.width(), width); |
82 | assert_eq!(header.height(), height); |
83 | assert_eq!(header.subtype(), subtype); |
84 | assert_eq!(loaded_color, color); |
85 | assert_eq!(loaded_image.as_slice(), buffer); |
86 | } |
87 | |
88 | fn execute_roundtrip_u16(buffer: &[u16], width: u32, height: u32, color: ColorType) { |
89 | let mut encoded_buffer = Vec::new(); |
90 | |
91 | { |
92 | let mut encoder = PnmEncoder::new(&mut encoded_buffer); |
93 | encoder |
94 | .encode(buffer, width, height, color) |
95 | .expect("Failed to encode the image buffer" ); |
96 | } |
97 | |
98 | let (header, loaded_color, loaded_image) = { |
99 | let decoder = PnmDecoder::new(&encoded_buffer[..]).unwrap(); |
100 | let color_type = decoder.color_type(); |
101 | let mut image = vec![0; decoder.total_bytes() as usize]; |
102 | decoder |
103 | .read_image(&mut image) |
104 | .expect("Failed to decode the image" ); |
105 | let (_, header) = PnmDecoder::new(&encoded_buffer[..]).unwrap().into_inner(); |
106 | (header, color_type, image) |
107 | }; |
108 | |
109 | let mut buffer_u8 = vec![0; buffer.len() * 2]; |
110 | NativeEndian::write_u16_into(buffer, &mut buffer_u8[..]); |
111 | |
112 | assert_eq!(header.width(), width); |
113 | assert_eq!(header.height(), height); |
114 | assert_eq!(loaded_color, color); |
115 | assert_eq!(loaded_image, buffer_u8); |
116 | } |
117 | |
118 | #[test ] |
119 | fn roundtrip_gray() { |
120 | #[rustfmt::skip] |
121 | let buf: [u8; 16] = [ |
122 | 0, 0, 0, 255, |
123 | 255, 255, 255, 255, |
124 | 255, 0, 255, 0, |
125 | 255, 0, 0, 0, |
126 | ]; |
127 | |
128 | execute_roundtrip_default(&buf, 4, 4, ColorType::L8); |
129 | execute_roundtrip_with_subtype(&buf, 4, 4, ColorType::L8, PnmSubtype::ArbitraryMap); |
130 | execute_roundtrip_with_subtype( |
131 | &buf, |
132 | 4, |
133 | 4, |
134 | ColorType::L8, |
135 | PnmSubtype::Graymap(SampleEncoding::Ascii), |
136 | ); |
137 | execute_roundtrip_with_subtype( |
138 | &buf, |
139 | 4, |
140 | 4, |
141 | ColorType::L8, |
142 | PnmSubtype::Graymap(SampleEncoding::Binary), |
143 | ); |
144 | } |
145 | |
146 | #[test ] |
147 | fn roundtrip_rgb() { |
148 | #[rustfmt::skip] |
149 | let buf: [u8; 27] = [ |
150 | 0, 0, 0, |
151 | 0, 0, 255, |
152 | 0, 255, 0, |
153 | 0, 255, 255, |
154 | 255, 0, 0, |
155 | 255, 0, 255, |
156 | 255, 255, 0, |
157 | 255, 255, 255, |
158 | 255, 255, 255, |
159 | ]; |
160 | execute_roundtrip_default(&buf, 3, 3, ColorType::Rgb8); |
161 | execute_roundtrip_with_subtype(&buf, 3, 3, ColorType::Rgb8, PnmSubtype::ArbitraryMap); |
162 | execute_roundtrip_with_subtype( |
163 | &buf, |
164 | 3, |
165 | 3, |
166 | ColorType::Rgb8, |
167 | PnmSubtype::Pixmap(SampleEncoding::Binary), |
168 | ); |
169 | execute_roundtrip_with_subtype( |
170 | &buf, |
171 | 3, |
172 | 3, |
173 | ColorType::Rgb8, |
174 | PnmSubtype::Pixmap(SampleEncoding::Ascii), |
175 | ); |
176 | } |
177 | |
178 | #[test ] |
179 | fn roundtrip_u16() { |
180 | let buf: [u16; 6] = [0, 1, 0xFFFF, 0x1234, 0x3412, 0xBEAF]; |
181 | |
182 | execute_roundtrip_u16(&buf, 6, 1, ColorType::L16); |
183 | } |
184 | } |
185 | |