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