1use std::{fmt, io};
2
3#[derive(Clone, Copy, PartialEq, Eq, Debug)]
5pub enum SampleEncoding {
6 Binary,
8
9 Ascii,
11}
12
13#[derive(Clone, Copy, PartialEq, Eq, Debug)]
15pub enum PnmSubtype {
16 Bitmap(SampleEncoding),
18
19 Graymap(SampleEncoding),
21
22 Pixmap(SampleEncoding),
24
25 ArbitraryMap,
27}
28
29#[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#[derive(Clone, Copy, Debug)]
51pub struct BitmapHeader {
52 pub encoding: SampleEncoding,
54
55 pub height: u32,
57
58 pub width: u32,
60}
61
62#[derive(Clone, Copy, Debug)]
64pub struct GraymapHeader {
65 pub encoding: SampleEncoding,
67
68 pub height: u32,
70
71 pub width: u32,
73
74 pub maxwhite: u32,
76}
77
78#[derive(Clone, Copy, Debug)]
80pub struct PixmapHeader {
81 pub encoding: SampleEncoding,
83
84 pub height: u32,
86
87 pub width: u32,
89
90 pub maxval: u32,
92}
93
94#[derive(Clone, Debug)]
96pub struct ArbitraryHeader {
97 pub height: u32,
99
100 pub width: u32,
102
103 pub depth: u32,
105
106 pub maxval: u32,
108
109 pub tupltype: Option<ArbitraryTuplType>,
111}
112
113#[derive(Clone, Debug)]
115pub enum ArbitraryTuplType {
116 BlackAndWhite,
118
119 BlackAndWhiteAlpha,
121
122 Grayscale,
124
125 GrayscaleAlpha,
127
128 RGB,
130
131 RGBAlpha,
133
134 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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}