image/codecs/pnm/
header.rs

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) => 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) => enc,
169            PnmSubtype::Graymap(enc) => enc,
170            PnmSubtype::Pixmap(enc) => 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}