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