1use std::{fmt, io};
2
3/// The kind of encoding used to store sample values
4#[derive(Clone, Copy, PartialEq, Eq, Debug)]
5pub enum SampleEncoding {
6 /// Samples are unsigned binary integers in big endian
7 Binary,
8
9 /// Samples are encoded as decimal ascii strings separated by whitespace
10 Ascii,
11}
12
13/// Denotes the category of the magic number
14#[derive(Clone, Copy, PartialEq, Eq, Debug)]
15pub enum PnmSubtype {
16 /// Magic numbers P1 and P4
17 Bitmap(SampleEncoding),
18
19 /// Magic numbers P2 and P5
20 Graymap(SampleEncoding),
21
22 /// Magic numbers P3 and P6
23 Pixmap(SampleEncoding),
24
25 /// Magic number P7
26 ArbitraryMap,
27}
28
29/// Stores the complete header data of a file.
30///
31/// Internally, provides mechanisms for lossless reencoding. After reading a file with the decoder
32/// it is possible to recover the header and construct an encoder. Using the encoder on the just
33/// loaded image should result in a byte copy of the original file (for single image pnms without
34/// additional trailing data).
35pub struct PnmHeader {
36 pub(crate) decoded: HeaderRecord,
37 pub(crate) encoded: Option<Vec<u8>>,
38}
39
40pub(crate) enum HeaderRecord {
41 Bitmap(BitmapHeader),
42 Graymap(GraymapHeader),
43 Pixmap(PixmapHeader),
44 Arbitrary(ArbitraryHeader),
45}
46
47/// Header produced by a `pbm` file ("Portable Bit Map")
48#[derive(Clone, Copy, Debug)]
49pub struct BitmapHeader {
50 /// Binary or Ascii encoded file
51 pub encoding: SampleEncoding,
52
53 /// Height of the image file
54 pub height: u32,
55
56 /// Width of the image file
57 pub width: u32,
58}
59
60/// Header produced by a `pgm` file ("Portable Gray Map")
61#[derive(Clone, Copy, Debug)]
62pub struct GraymapHeader {
63 /// Binary or Ascii encoded file
64 pub encoding: SampleEncoding,
65
66 /// Height of the image file
67 pub height: u32,
68
69 /// Width of the image file
70 pub width: u32,
71
72 /// Maximum sample value within the image
73 pub maxwhite: u32,
74}
75
76/// Header produced by a `ppm` file ("Portable Pixel Map")
77#[derive(Clone, Copy, Debug)]
78pub struct PixmapHeader {
79 /// Binary or Ascii encoded file
80 pub encoding: SampleEncoding,
81
82 /// Height of the image file
83 pub height: u32,
84
85 /// Width of the image file
86 pub width: u32,
87
88 /// Maximum sample value within the image
89 pub maxval: u32,
90}
91
92/// Header produced by a `pam` file ("Portable Arbitrary Map")
93#[derive(Clone, Debug)]
94pub struct ArbitraryHeader {
95 /// Height of the image file
96 pub height: u32,
97
98 /// Width of the image file
99 pub width: u32,
100
101 /// Number of color channels
102 pub depth: u32,
103
104 /// Maximum sample value within the image
105 pub maxval: u32,
106
107 /// Color interpretation of image pixels
108 pub tupltype: Option<ArbitraryTuplType>,
109}
110
111/// Standardized tuple type specifiers in the header of a `pam`.
112#[derive(Clone, Debug)]
113pub enum ArbitraryTuplType {
114 /// Pixels are either black (0) or white (1)
115 BlackAndWhite,
116
117 /// Pixels are either black (0) or white (1) and a second alpha channel
118 BlackAndWhiteAlpha,
119
120 /// Pixels represent the amount of white
121 Grayscale,
122
123 /// Grayscale with an additional alpha channel
124 GrayscaleAlpha,
125
126 /// Three channels: Red, Green, Blue
127 RGB,
128
129 /// Four channels: Red, Green, Blue, Alpha
130 RGBAlpha,
131
132 /// An image format which is not standardized
133 Custom(String),
134}
135
136impl ArbitraryTuplType {
137 pub(crate) fn name(&self) -> &str {
138 match self {
139 ArbitraryTuplType::BlackAndWhite => "BLACKANDWHITE",
140 ArbitraryTuplType::BlackAndWhiteAlpha => "BLACKANDWHITE_ALPHA",
141 ArbitraryTuplType::Grayscale => "GRAYSCALE",
142 ArbitraryTuplType::GrayscaleAlpha => "GRAYSCALE_ALPHA",
143 ArbitraryTuplType::RGB => "RGB",
144 ArbitraryTuplType::RGBAlpha => "RGB_ALPHA",
145 ArbitraryTuplType::Custom(custom: &String) => custom,
146 }
147 }
148}
149
150impl PnmSubtype {
151 /// Get the two magic constant bytes corresponding to this format subtype.
152 pub fn magic_constant(self) -> &'static [u8; 2] {
153 match self {
154 PnmSubtype::Bitmap(SampleEncoding::Ascii) => b"P1",
155 PnmSubtype::Graymap(SampleEncoding::Ascii) => b"P2",
156 PnmSubtype::Pixmap(SampleEncoding::Ascii) => b"P3",
157 PnmSubtype::Bitmap(SampleEncoding::Binary) => b"P4",
158 PnmSubtype::Graymap(SampleEncoding::Binary) => b"P5",
159 PnmSubtype::Pixmap(SampleEncoding::Binary) => b"P6",
160 PnmSubtype::ArbitraryMap => b"P7",
161 }
162 }
163
164 /// Whether samples are stored as binary or as decimal ascii
165 pub fn sample_encoding(self) -> SampleEncoding {
166 match self {
167 PnmSubtype::ArbitraryMap => SampleEncoding::Binary,
168 PnmSubtype::Bitmap(enc: SampleEncoding) => enc,
169 PnmSubtype::Graymap(enc: SampleEncoding) => enc,
170 PnmSubtype::Pixmap(enc: SampleEncoding) => enc,
171 }
172 }
173}
174
175impl PnmHeader {
176 /// Retrieve the format subtype from which the header was created.
177 pub fn subtype(&self) -> PnmSubtype {
178 match self.decoded {
179 HeaderRecord::Bitmap(BitmapHeader { encoding, .. }) => PnmSubtype::Bitmap(encoding),
180 HeaderRecord::Graymap(GraymapHeader { encoding, .. }) => PnmSubtype::Graymap(encoding),
181 HeaderRecord::Pixmap(PixmapHeader { encoding, .. }) => PnmSubtype::Pixmap(encoding),
182 HeaderRecord::Arbitrary(ArbitraryHeader { .. }) => PnmSubtype::ArbitraryMap,
183 }
184 }
185
186 /// The width of the image this header is for.
187 pub fn width(&self) -> u32 {
188 match self.decoded {
189 HeaderRecord::Bitmap(BitmapHeader { width, .. }) => width,
190 HeaderRecord::Graymap(GraymapHeader { width, .. }) => width,
191 HeaderRecord::Pixmap(PixmapHeader { width, .. }) => width,
192 HeaderRecord::Arbitrary(ArbitraryHeader { width, .. }) => width,
193 }
194 }
195
196 /// The height of the image this header is for.
197 pub fn height(&self) -> u32 {
198 match self.decoded {
199 HeaderRecord::Bitmap(BitmapHeader { height, .. }) => height,
200 HeaderRecord::Graymap(GraymapHeader { height, .. }) => height,
201 HeaderRecord::Pixmap(PixmapHeader { height, .. }) => height,
202 HeaderRecord::Arbitrary(ArbitraryHeader { height, .. }) => height,
203 }
204 }
205
206 /// The biggest value a sample can have. In other words, the colour resolution.
207 pub fn maximal_sample(&self) -> u32 {
208 match self.decoded {
209 HeaderRecord::Bitmap(BitmapHeader { .. }) => 1,
210 HeaderRecord::Graymap(GraymapHeader { maxwhite, .. }) => maxwhite,
211 HeaderRecord::Pixmap(PixmapHeader { maxval, .. }) => maxval,
212 HeaderRecord::Arbitrary(ArbitraryHeader { maxval, .. }) => maxval,
213 }
214 }
215
216 /// Retrieve the underlying bitmap header if any
217 pub fn as_bitmap(&self) -> Option<&BitmapHeader> {
218 match self.decoded {
219 HeaderRecord::Bitmap(ref bitmap) => Some(bitmap),
220 _ => None,
221 }
222 }
223
224 /// Retrieve the underlying graymap header if any
225 pub fn as_graymap(&self) -> Option<&GraymapHeader> {
226 match self.decoded {
227 HeaderRecord::Graymap(ref graymap) => Some(graymap),
228 _ => None,
229 }
230 }
231
232 /// Retrieve the underlying pixmap header if any
233 pub fn as_pixmap(&self) -> Option<&PixmapHeader> {
234 match self.decoded {
235 HeaderRecord::Pixmap(ref pixmap) => Some(pixmap),
236 _ => None,
237 }
238 }
239
240 /// Retrieve the underlying arbitrary header if any
241 pub fn as_arbitrary(&self) -> Option<&ArbitraryHeader> {
242 match self.decoded {
243 HeaderRecord::Arbitrary(ref arbitrary) => Some(arbitrary),
244 _ => None,
245 }
246 }
247
248 /// Write the header back into a binary stream
249 pub fn write(&self, writer: &mut dyn io::Write) -> io::Result<()> {
250 writer.write_all(self.subtype().magic_constant())?;
251 match *self {
252 PnmHeader {
253 encoded: Some(ref content),
254 ..
255 } => writer.write_all(content),
256 PnmHeader {
257 decoded:
258 HeaderRecord::Bitmap(BitmapHeader {
259 encoding: _encoding,
260 width,
261 height,
262 }),
263 ..
264 } => writeln!(writer, "\n{} {}", width, height),
265 PnmHeader {
266 decoded:
267 HeaderRecord::Graymap(GraymapHeader {
268 encoding: _encoding,
269 width,
270 height,
271 maxwhite,
272 }),
273 ..
274 } => writeln!(writer, "\n{} {} {}", width, height, maxwhite),
275 PnmHeader {
276 decoded:
277 HeaderRecord::Pixmap(PixmapHeader {
278 encoding: _encoding,
279 width,
280 height,
281 maxval,
282 }),
283 ..
284 } => writeln!(writer, "\n{} {} {}", width, height, maxval),
285 PnmHeader {
286 decoded:
287 HeaderRecord::Arbitrary(ArbitraryHeader {
288 width,
289 height,
290 depth,
291 maxval,
292 ref tupltype,
293 }),
294 ..
295 } => {
296 struct TupltypeWriter<'a>(&'a Option<ArbitraryTuplType>);
297 impl<'a> fmt::Display for TupltypeWriter<'a> {
298 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299 match self.0 {
300 Some(tt) => writeln!(f, "TUPLTYPE {}", tt.name()),
301 None => Ok(()),
302 }
303 }
304 }
305
306 writeln!(
307 writer,
308 "\nWIDTH {}\nHEIGHT {}\nDEPTH {}\nMAXVAL {}\n{}ENDHDR",
309 width,
310 height,
311 depth,
312 maxval,
313 TupltypeWriter(tupltype)
314 )
315 }
316 }
317 }
318}
319
320impl From<BitmapHeader> for PnmHeader {
321 fn from(header: BitmapHeader) -> Self {
322 PnmHeader {
323 decoded: HeaderRecord::Bitmap(header),
324 encoded: None,
325 }
326 }
327}
328
329impl From<GraymapHeader> for PnmHeader {
330 fn from(header: GraymapHeader) -> Self {
331 PnmHeader {
332 decoded: HeaderRecord::Graymap(header),
333 encoded: None,
334 }
335 }
336}
337
338impl From<PixmapHeader> for PnmHeader {
339 fn from(header: PixmapHeader) -> Self {
340 PnmHeader {
341 decoded: HeaderRecord::Pixmap(header),
342 encoded: None,
343 }
344 }
345}
346
347impl From<ArbitraryHeader> for PnmHeader {
348 fn from(header: ArbitraryHeader) -> Self {
349 PnmHeader {
350 decoded: HeaderRecord::Arbitrary(header),
351 encoded: None,
352 }
353 }
354}
355