1 | use std::error; |
2 | use std::fmt::{self, Display}; |
3 | use std::io::{self, BufRead, Cursor, Read}; |
4 | use std::marker::PhantomData; |
5 | use std::mem; |
6 | use std::num::ParseIntError; |
7 | use std::str::{self, FromStr}; |
8 | |
9 | use super::{ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader}; |
10 | use super::{HeaderRecord, PnmHeader, PnmSubtype, SampleEncoding}; |
11 | use crate::color::{ColorType, ExtendedColorType}; |
12 | use crate::error::{ |
13 | DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind, |
14 | }; |
15 | use crate::image::{self, ImageDecoder, ImageFormat}; |
16 | use crate::utils; |
17 | |
18 | use byteorder::{BigEndian, ByteOrder, NativeEndian}; |
19 | |
20 | /// All errors that can occur when attempting to parse a PNM |
21 | #[derive (Debug, Clone)] |
22 | enum DecoderError { |
23 | /// PNM's "P[123456]" signature wrong or missing |
24 | PnmMagicInvalid([u8; 2]), |
25 | /// Couldn't parse the specified string as an integer from the specified source |
26 | UnparsableValue(ErrorDataSource, String, ParseIntError), |
27 | |
28 | /// More than the exactly one allowed plane specified by the format |
29 | NonAsciiByteInHeader(u8), |
30 | /// The PAM header contained a non-ASCII byte |
31 | NonAsciiLineInPamHeader, |
32 | /// A sample string contained a non-ASCII byte |
33 | NonAsciiSample, |
34 | |
35 | /// The byte after the P7 magic was not 0x0A NEWLINE |
36 | NotNewlineAfterP7Magic(u8), |
37 | /// The PNM header had too few lines |
38 | UnexpectedPnmHeaderEnd, |
39 | |
40 | /// The specified line was specified twice |
41 | HeaderLineDuplicated(PnmHeaderLine), |
42 | /// The line with the specified ID was not understood |
43 | HeaderLineUnknown(String), |
44 | /// At least one of the required lines were missing from the header (are `None` here) |
45 | /// |
46 | /// Same names as [`PnmHeaderLine`](enum.PnmHeaderLine.html) |
47 | #[allow (missing_docs)] |
48 | HeaderLineMissing { |
49 | height: Option<u32>, |
50 | width: Option<u32>, |
51 | depth: Option<u32>, |
52 | maxval: Option<u32>, |
53 | }, |
54 | |
55 | /// Not enough data was provided to the Decoder to decode the image |
56 | InputTooShort, |
57 | /// Sample raster contained unexpected byte |
58 | UnexpectedByteInRaster(u8), |
59 | /// Specified sample was out of bounds (e.g. >1 in B&W) |
60 | SampleOutOfBounds(u8), |
61 | /// The image's maxval is zero |
62 | MaxvalZero, |
63 | /// The image's maxval exceeds 0xFFFF |
64 | MaxvalTooBig(u32), |
65 | |
66 | /// The specified tuple type supports restricted depths and maxvals, those restrictions were not met |
67 | InvalidDepthOrMaxval { |
68 | tuple_type: ArbitraryTuplType, |
69 | depth: u32, |
70 | maxval: u32, |
71 | }, |
72 | /// The specified tuple type supports restricted depths, those restrictions were not met |
73 | InvalidDepth { |
74 | tuple_type: ArbitraryTuplType, |
75 | depth: u32, |
76 | }, |
77 | /// The tuple type was not recognised by the parser |
78 | TupleTypeUnrecognised, |
79 | |
80 | /// Overflowed the specified value when parsing |
81 | Overflow, |
82 | } |
83 | |
84 | impl Display for DecoderError { |
85 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
86 | match self { |
87 | DecoderError::PnmMagicInvalid(magic) => f.write_fmt(format_args!( |
88 | "Expected magic constant for PNM: P1..P7, got [ {:#04X?}, {:#04X?}]" , |
89 | magic[0], magic[1] |
90 | )), |
91 | DecoderError::UnparsableValue(src, data, err) => { |
92 | f.write_fmt(format_args!("Error parsing {:?} as {}: {}" , data, src, err)) |
93 | } |
94 | |
95 | DecoderError::NonAsciiByteInHeader(c) => { |
96 | f.write_fmt(format_args!("Non-ASCII character {:#04X?} in header" , c)) |
97 | } |
98 | DecoderError::NonAsciiLineInPamHeader => f.write_str("Non-ASCII line in PAM header" ), |
99 | DecoderError::NonAsciiSample => { |
100 | f.write_str("Non-ASCII character where sample value was expected" ) |
101 | } |
102 | |
103 | DecoderError::NotNewlineAfterP7Magic(c) => f.write_fmt(format_args!( |
104 | "Expected newline after P7 magic, got {:#04X?}" , |
105 | c |
106 | )), |
107 | DecoderError::UnexpectedPnmHeaderEnd => f.write_str("Unexpected end of PNM header" ), |
108 | |
109 | DecoderError::HeaderLineDuplicated(line) => { |
110 | f.write_fmt(format_args!("Duplicate {} line" , line)) |
111 | } |
112 | DecoderError::HeaderLineUnknown(identifier) => f.write_fmt(format_args!( |
113 | "Unknown header line with identifier {:?}" , |
114 | identifier |
115 | )), |
116 | DecoderError::HeaderLineMissing { |
117 | height, |
118 | width, |
119 | depth, |
120 | maxval, |
121 | } => f.write_fmt(format_args!( |
122 | "Missing header line: have height= {:?}, width= {:?}, depth= {:?}, maxval= {:?}" , |
123 | height, width, depth, maxval |
124 | )), |
125 | |
126 | DecoderError::InputTooShort => { |
127 | f.write_str("Not enough data was provided to the Decoder to decode the image" ) |
128 | } |
129 | DecoderError::UnexpectedByteInRaster(c) => f.write_fmt(format_args!( |
130 | "Unexpected character {:#04X?} within sample raster" , |
131 | c |
132 | )), |
133 | DecoderError::SampleOutOfBounds(val) => { |
134 | f.write_fmt(format_args!("Sample value {} outside of bounds" , val)) |
135 | } |
136 | DecoderError::MaxvalZero => f.write_str("Image MAXVAL is zero" ), |
137 | DecoderError::MaxvalTooBig(maxval) => { |
138 | f.write_fmt(format_args!("Image MAXVAL exceeds {}: {}" , 0xFFFF, maxval)) |
139 | } |
140 | |
141 | DecoderError::InvalidDepthOrMaxval { |
142 | tuple_type, |
143 | depth, |
144 | maxval, |
145 | } => f.write_fmt(format_args!( |
146 | "Invalid depth ( {}) or maxval ( {}) for tuple type {}" , |
147 | depth, |
148 | maxval, |
149 | tuple_type.name() |
150 | )), |
151 | DecoderError::InvalidDepth { tuple_type, depth } => f.write_fmt(format_args!( |
152 | "Invalid depth ( {}) for tuple type {}" , |
153 | depth, |
154 | tuple_type.name() |
155 | )), |
156 | DecoderError::TupleTypeUnrecognised => f.write_str("Tuple type not recognized" ), |
157 | DecoderError::Overflow => f.write_str("Overflow when parsing value" ), |
158 | } |
159 | } |
160 | } |
161 | |
162 | /// Note: should `pnm` be extracted into a separate crate, |
163 | /// this will need to be hidden until that crate hits version `1.0`. |
164 | impl From<DecoderError> for ImageError { |
165 | fn from(e: DecoderError) -> ImageError { |
166 | ImageError::Decoding(DecodingError::new(format:ImageFormat::Pnm.into(), err:e)) |
167 | } |
168 | } |
169 | |
170 | impl error::Error for DecoderError { |
171 | fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
172 | match self { |
173 | DecoderError::UnparsableValue(_, _, err: &ParseIntError) => Some(err), |
174 | _ => None, |
175 | } |
176 | } |
177 | } |
178 | |
179 | /// Single-value lines in a PNM header |
180 | #[derive (Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] |
181 | enum PnmHeaderLine { |
182 | /// "HEIGHT" |
183 | Height, |
184 | /// "WIDTH" |
185 | Width, |
186 | /// "DEPTH" |
187 | Depth, |
188 | /// "MAXVAL", a.k.a. `maxwhite` |
189 | Maxval, |
190 | } |
191 | |
192 | impl Display for PnmHeaderLine { |
193 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
194 | f.write_str(data:match self { |
195 | PnmHeaderLine::Height => "HEIGHT" , |
196 | PnmHeaderLine::Width => "WIDTH" , |
197 | PnmHeaderLine::Depth => "DEPTH" , |
198 | PnmHeaderLine::Maxval => "MAXVAL" , |
199 | }) |
200 | } |
201 | } |
202 | |
203 | /// Single-value lines in a PNM header |
204 | #[derive (Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] |
205 | enum ErrorDataSource { |
206 | /// One of the header lines |
207 | Line(PnmHeaderLine), |
208 | /// Value in the preamble |
209 | Preamble, |
210 | /// Sample/pixel data |
211 | Sample, |
212 | } |
213 | |
214 | impl Display for ErrorDataSource { |
215 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
216 | match self { |
217 | ErrorDataSource::Line(l: &PnmHeaderLine) => l.fmt(f), |
218 | ErrorDataSource::Preamble => f.write_str(data:"number in preamble" ), |
219 | ErrorDataSource::Sample => f.write_str(data:"sample" ), |
220 | } |
221 | } |
222 | } |
223 | |
224 | /// Dynamic representation, represents all decodable (sample, depth) combinations. |
225 | #[derive (Clone, Copy)] |
226 | enum TupleType { |
227 | PbmBit, |
228 | BWBit, |
229 | GrayU8, |
230 | GrayU16, |
231 | RGBU8, |
232 | RGBU16, |
233 | } |
234 | |
235 | trait Sample { |
236 | type Representation; |
237 | |
238 | /// Representation size in bytes |
239 | fn sample_size() -> u32 { |
240 | std::mem::size_of::<Self::Representation>() as u32 |
241 | } |
242 | fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> { |
243 | Ok((width * height * samples * Self::sample_size()) as usize) |
244 | } |
245 | fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()>; |
246 | fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()>; |
247 | } |
248 | |
249 | struct U8; |
250 | struct U16; |
251 | struct PbmBit; |
252 | struct BWBit; |
253 | |
254 | trait DecodableImageHeader { |
255 | fn tuple_type(&self) -> ImageResult<TupleType>; |
256 | } |
257 | |
258 | /// PNM decoder |
259 | pub struct PnmDecoder<R> { |
260 | reader: R, |
261 | header: PnmHeader, |
262 | tuple: TupleType, |
263 | } |
264 | |
265 | impl<R: BufRead> PnmDecoder<R> { |
266 | /// Create a new decoder that decodes from the stream ```read``` |
267 | pub fn new(mut buffered_read: R) -> ImageResult<PnmDecoder<R>> { |
268 | let magic = buffered_read.read_magic_constant()?; |
269 | |
270 | let subtype = match magic { |
271 | [b'P' , b'1' ] => PnmSubtype::Bitmap(SampleEncoding::Ascii), |
272 | [b'P' , b'2' ] => PnmSubtype::Graymap(SampleEncoding::Ascii), |
273 | [b'P' , b'3' ] => PnmSubtype::Pixmap(SampleEncoding::Ascii), |
274 | [b'P' , b'4' ] => PnmSubtype::Bitmap(SampleEncoding::Binary), |
275 | [b'P' , b'5' ] => PnmSubtype::Graymap(SampleEncoding::Binary), |
276 | [b'P' , b'6' ] => PnmSubtype::Pixmap(SampleEncoding::Binary), |
277 | [b'P' , b'7' ] => PnmSubtype::ArbitraryMap, |
278 | _ => return Err(DecoderError::PnmMagicInvalid(magic).into()), |
279 | }; |
280 | |
281 | let decoder = match subtype { |
282 | PnmSubtype::Bitmap(enc) => PnmDecoder::read_bitmap_header(buffered_read, enc), |
283 | PnmSubtype::Graymap(enc) => PnmDecoder::read_graymap_header(buffered_read, enc), |
284 | PnmSubtype::Pixmap(enc) => PnmDecoder::read_pixmap_header(buffered_read, enc), |
285 | PnmSubtype::ArbitraryMap => PnmDecoder::read_arbitrary_header(buffered_read), |
286 | }?; |
287 | |
288 | if utils::check_dimension_overflow( |
289 | decoder.dimensions().0, |
290 | decoder.dimensions().1, |
291 | decoder.color_type().bytes_per_pixel(), |
292 | ) { |
293 | return Err(ImageError::Unsupported( |
294 | UnsupportedError::from_format_and_kind( |
295 | ImageFormat::Pnm.into(), |
296 | UnsupportedErrorKind::GenericFeature(format!( |
297 | "Image dimensions ( {}x {}) are too large" , |
298 | decoder.dimensions().0, |
299 | decoder.dimensions().1 |
300 | )), |
301 | ), |
302 | )); |
303 | } |
304 | |
305 | Ok(decoder) |
306 | } |
307 | |
308 | /// Extract the reader and header after an image has been read. |
309 | pub fn into_inner(self) -> (R, PnmHeader) { |
310 | (self.reader, self.header) |
311 | } |
312 | |
313 | fn read_bitmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> { |
314 | let header = reader.read_bitmap_header(encoding)?; |
315 | Ok(PnmDecoder { |
316 | reader, |
317 | tuple: TupleType::PbmBit, |
318 | header: PnmHeader { |
319 | decoded: HeaderRecord::Bitmap(header), |
320 | encoded: None, |
321 | }, |
322 | }) |
323 | } |
324 | |
325 | fn read_graymap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> { |
326 | let header = reader.read_graymap_header(encoding)?; |
327 | let tuple_type = header.tuple_type()?; |
328 | Ok(PnmDecoder { |
329 | reader, |
330 | tuple: tuple_type, |
331 | header: PnmHeader { |
332 | decoded: HeaderRecord::Graymap(header), |
333 | encoded: None, |
334 | }, |
335 | }) |
336 | } |
337 | |
338 | fn read_pixmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> { |
339 | let header = reader.read_pixmap_header(encoding)?; |
340 | let tuple_type = header.tuple_type()?; |
341 | Ok(PnmDecoder { |
342 | reader, |
343 | tuple: tuple_type, |
344 | header: PnmHeader { |
345 | decoded: HeaderRecord::Pixmap(header), |
346 | encoded: None, |
347 | }, |
348 | }) |
349 | } |
350 | |
351 | fn read_arbitrary_header(mut reader: R) -> ImageResult<PnmDecoder<R>> { |
352 | let header = reader.read_arbitrary_header()?; |
353 | let tuple_type = header.tuple_type()?; |
354 | Ok(PnmDecoder { |
355 | reader, |
356 | tuple: tuple_type, |
357 | header: PnmHeader { |
358 | decoded: HeaderRecord::Arbitrary(header), |
359 | encoded: None, |
360 | }, |
361 | }) |
362 | } |
363 | } |
364 | |
365 | trait HeaderReader: BufRead { |
366 | /// Reads the two magic constant bytes |
367 | fn read_magic_constant(&mut self) -> ImageResult<[u8; 2]> { |
368 | let mut magic: [u8; 2] = [0, 0]; |
369 | self.read_exact(&mut magic)?; |
370 | Ok(magic) |
371 | } |
372 | |
373 | /// Reads a string as well as a single whitespace after it, ignoring comments |
374 | fn read_next_string(&mut self) -> ImageResult<String> { |
375 | let mut bytes = Vec::new(); |
376 | |
377 | // pair input bytes with a bool mask to remove comments |
378 | let mark_comments = self.bytes().scan(true, |partof, read| { |
379 | let byte = match read { |
380 | Err(err) => return Some((*partof, Err(err))), |
381 | Ok(byte) => byte, |
382 | }; |
383 | let cur_enabled = *partof && byte != b'#' ; |
384 | let next_enabled = cur_enabled || (byte == b' \r' || byte == b' \n' ); |
385 | *partof = next_enabled; |
386 | Some((cur_enabled, Ok(byte))) |
387 | }); |
388 | |
389 | for (_, byte) in mark_comments.filter(|e| e.0) { |
390 | match byte { |
391 | Ok(b' \t' ) | Ok(b' \n' ) | Ok(b' \x0b' ) | Ok(b' \x0c' ) | Ok(b' \r' ) | Ok(b' ' ) => { |
392 | if !bytes.is_empty() { |
393 | break; // We're done as we already have some content |
394 | } |
395 | } |
396 | Ok(byte) if !byte.is_ascii() => { |
397 | return Err(DecoderError::NonAsciiByteInHeader(byte).into()) |
398 | } |
399 | Ok(byte) => { |
400 | bytes.push(byte); |
401 | } |
402 | Err(_) => break, |
403 | } |
404 | } |
405 | |
406 | if bytes.is_empty() { |
407 | return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into())); |
408 | } |
409 | |
410 | if !bytes.as_slice().is_ascii() { |
411 | // We have only filled the buffer with characters for which `byte.is_ascii()` holds. |
412 | unreachable!("Non-ASCII character should have returned sooner" ) |
413 | } |
414 | |
415 | let string = String::from_utf8(bytes) |
416 | // We checked the precondition ourselves a few lines before, `bytes.as_slice().is_ascii()`. |
417 | .unwrap_or_else(|_| unreachable!("Only ASCII characters should be decoded" )); |
418 | |
419 | Ok(string) |
420 | } |
421 | |
422 | fn read_next_u32(&mut self) -> ImageResult<u32> { |
423 | let s = self.read_next_string()?; |
424 | s.parse::<u32>() |
425 | .map_err(|err| DecoderError::UnparsableValue(ErrorDataSource::Preamble, s, err).into()) |
426 | } |
427 | |
428 | fn read_bitmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<BitmapHeader> { |
429 | let width = self.read_next_u32()?; |
430 | let height = self.read_next_u32()?; |
431 | Ok(BitmapHeader { |
432 | encoding, |
433 | width, |
434 | height, |
435 | }) |
436 | } |
437 | |
438 | fn read_graymap_header(&mut self, encoding: SampleEncoding) -> ImageResult<GraymapHeader> { |
439 | self.read_pixmap_header(encoding).map( |
440 | |PixmapHeader { |
441 | encoding, |
442 | width, |
443 | height, |
444 | maxval, |
445 | }| GraymapHeader { |
446 | encoding, |
447 | width, |
448 | height, |
449 | maxwhite: maxval, |
450 | }, |
451 | ) |
452 | } |
453 | |
454 | fn read_pixmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<PixmapHeader> { |
455 | let width = self.read_next_u32()?; |
456 | let height = self.read_next_u32()?; |
457 | let maxval = self.read_next_u32()?; |
458 | Ok(PixmapHeader { |
459 | encoding, |
460 | width, |
461 | height, |
462 | maxval, |
463 | }) |
464 | } |
465 | |
466 | fn read_arbitrary_header(&mut self) -> ImageResult<ArbitraryHeader> { |
467 | fn parse_single_value_line( |
468 | line_val: &mut Option<u32>, |
469 | rest: &str, |
470 | line: PnmHeaderLine, |
471 | ) -> ImageResult<()> { |
472 | if line_val.is_some() { |
473 | Err(DecoderError::HeaderLineDuplicated(line).into()) |
474 | } else { |
475 | let v = rest.trim().parse().map_err(|err| { |
476 | DecoderError::UnparsableValue(ErrorDataSource::Line(line), rest.to_owned(), err) |
477 | })?; |
478 | *line_val = Some(v); |
479 | Ok(()) |
480 | } |
481 | } |
482 | |
483 | match self.bytes().next() { |
484 | None => return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into())), |
485 | Some(Err(io)) => return Err(ImageError::IoError(io)), |
486 | Some(Ok(b' \n' )) => (), |
487 | Some(Ok(c)) => return Err(DecoderError::NotNewlineAfterP7Magic(c).into()), |
488 | } |
489 | |
490 | let mut line = String::new(); |
491 | let mut height: Option<u32> = None; |
492 | let mut width: Option<u32> = None; |
493 | let mut depth: Option<u32> = None; |
494 | let mut maxval: Option<u32> = None; |
495 | let mut tupltype: Option<String> = None; |
496 | loop { |
497 | line.truncate(0); |
498 | let len = self.read_line(&mut line)?; |
499 | if len == 0 { |
500 | return Err(DecoderError::UnexpectedPnmHeaderEnd.into()); |
501 | } |
502 | if line.as_bytes()[0] == b'#' { |
503 | continue; |
504 | } |
505 | if !line.is_ascii() { |
506 | return Err(DecoderError::NonAsciiLineInPamHeader.into()); |
507 | } |
508 | #[allow (deprecated)] |
509 | let (identifier, rest) = line |
510 | .trim_left() |
511 | .split_at(line.find(char::is_whitespace).unwrap_or(line.len())); |
512 | match identifier { |
513 | "ENDHDR" => break, |
514 | "HEIGHT" => parse_single_value_line(&mut height, rest, PnmHeaderLine::Height)?, |
515 | "WIDTH" => parse_single_value_line(&mut width, rest, PnmHeaderLine::Width)?, |
516 | "DEPTH" => parse_single_value_line(&mut depth, rest, PnmHeaderLine::Depth)?, |
517 | "MAXVAL" => parse_single_value_line(&mut maxval, rest, PnmHeaderLine::Maxval)?, |
518 | "TUPLTYPE" => { |
519 | let identifier = rest.trim(); |
520 | if tupltype.is_some() { |
521 | let appended = tupltype.take().map(|mut v| { |
522 | v.push(' ' ); |
523 | v.push_str(identifier); |
524 | v |
525 | }); |
526 | tupltype = appended; |
527 | } else { |
528 | tupltype = Some(identifier.to_string()); |
529 | } |
530 | } |
531 | _ => return Err(DecoderError::HeaderLineUnknown(identifier.to_string()).into()), |
532 | } |
533 | } |
534 | |
535 | let (h, w, d, m) = match (height, width, depth, maxval) { |
536 | (Some(h), Some(w), Some(d), Some(m)) => (h, w, d, m), |
537 | _ => { |
538 | return Err(DecoderError::HeaderLineMissing { |
539 | height, |
540 | width, |
541 | depth, |
542 | maxval, |
543 | } |
544 | .into()) |
545 | } |
546 | }; |
547 | |
548 | let tupltype = match tupltype { |
549 | None => None, |
550 | Some(ref t) if t == "BLACKANDWHITE" => Some(ArbitraryTuplType::BlackAndWhite), |
551 | Some(ref t) if t == "BLACKANDWHITE_ALPHA" => { |
552 | Some(ArbitraryTuplType::BlackAndWhiteAlpha) |
553 | } |
554 | Some(ref t) if t == "GRAYSCALE" => Some(ArbitraryTuplType::Grayscale), |
555 | Some(ref t) if t == "GRAYSCALE_ALPHA" => Some(ArbitraryTuplType::GrayscaleAlpha), |
556 | Some(ref t) if t == "RGB" => Some(ArbitraryTuplType::RGB), |
557 | Some(ref t) if t == "RGB_ALPHA" => Some(ArbitraryTuplType::RGBAlpha), |
558 | Some(other) => Some(ArbitraryTuplType::Custom(other)), |
559 | }; |
560 | |
561 | Ok(ArbitraryHeader { |
562 | height: h, |
563 | width: w, |
564 | depth: d, |
565 | maxval: m, |
566 | tupltype, |
567 | }) |
568 | } |
569 | } |
570 | |
571 | impl<R> HeaderReader for R where R: BufRead {} |
572 | |
573 | /// Wrapper struct around a `Cursor<Vec<u8>>` |
574 | pub struct PnmReader<R>(Cursor<Vec<u8>>, PhantomData<R>); |
575 | impl<R> Read for PnmReader<R> { |
576 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
577 | self.0.read(buf) |
578 | } |
579 | fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { |
580 | if self.0.position() == 0 && buf.is_empty() { |
581 | mem::swap(x:buf, self.0.get_mut()); |
582 | Ok(buf.len()) |
583 | } else { |
584 | self.0.read_to_end(buf) |
585 | } |
586 | } |
587 | } |
588 | |
589 | impl<'a, R: 'a + Read> ImageDecoder<'a> for PnmDecoder<R> { |
590 | type Reader = PnmReader<R>; |
591 | |
592 | fn dimensions(&self) -> (u32, u32) { |
593 | (self.header.width(), self.header.height()) |
594 | } |
595 | |
596 | fn color_type(&self) -> ColorType { |
597 | match self.tuple { |
598 | TupleType::PbmBit => ColorType::L8, |
599 | TupleType::BWBit => ColorType::L8, |
600 | TupleType::GrayU8 => ColorType::L8, |
601 | TupleType::GrayU16 => ColorType::L16, |
602 | TupleType::RGBU8 => ColorType::Rgb8, |
603 | TupleType::RGBU16 => ColorType::Rgb16, |
604 | } |
605 | } |
606 | |
607 | fn original_color_type(&self) -> ExtendedColorType { |
608 | match self.tuple { |
609 | TupleType::PbmBit => ExtendedColorType::L1, |
610 | TupleType::BWBit => ExtendedColorType::L1, |
611 | TupleType::GrayU8 => ExtendedColorType::L8, |
612 | TupleType::GrayU16 => ExtendedColorType::L16, |
613 | TupleType::RGBU8 => ExtendedColorType::Rgb8, |
614 | TupleType::RGBU16 => ExtendedColorType::Rgb16, |
615 | } |
616 | } |
617 | |
618 | fn into_reader(self) -> ImageResult<Self::Reader> { |
619 | Ok(PnmReader( |
620 | Cursor::new(image::decoder_to_vec(self)?), |
621 | PhantomData, |
622 | )) |
623 | } |
624 | |
625 | fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> { |
626 | assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes())); |
627 | match self.tuple { |
628 | TupleType::PbmBit => self.read_samples::<PbmBit>(1, buf), |
629 | TupleType::BWBit => self.read_samples::<BWBit>(1, buf), |
630 | TupleType::RGBU8 => self.read_samples::<U8>(3, buf), |
631 | TupleType::RGBU16 => self.read_samples::<U16>(3, buf), |
632 | TupleType::GrayU8 => self.read_samples::<U8>(1, buf), |
633 | TupleType::GrayU16 => self.read_samples::<U16>(1, buf), |
634 | } |
635 | } |
636 | } |
637 | |
638 | impl<R: Read> PnmDecoder<R> { |
639 | fn read_samples<S: Sample>(&mut self, components: u32, buf: &mut [u8]) -> ImageResult<()> { |
640 | match self.subtype().sample_encoding() { |
641 | SampleEncoding::Binary => { |
642 | let width = self.header.width(); |
643 | let height = self.header.height(); |
644 | let bytecount = S::bytelen(width, height, components)?; |
645 | |
646 | let mut bytes = vec![]; |
647 | self.reader |
648 | .by_ref() |
649 | // This conversion is potentially lossy but unlikely and in that case we error |
650 | // later anyways. |
651 | .take(bytecount as u64) |
652 | .read_to_end(&mut bytes)?; |
653 | if bytes.len() != bytecount { |
654 | return Err(DecoderError::InputTooShort.into()); |
655 | } |
656 | |
657 | let width: usize = width.try_into().map_err(|_| DecoderError::Overflow)?; |
658 | let components: usize = |
659 | components.try_into().map_err(|_| DecoderError::Overflow)?; |
660 | let row_size = width |
661 | .checked_mul(components) |
662 | .ok_or(DecoderError::Overflow)?; |
663 | |
664 | S::from_bytes(&bytes, row_size, buf)?; |
665 | } |
666 | SampleEncoding::Ascii => { |
667 | self.read_ascii::<S>(buf)?; |
668 | } |
669 | }; |
670 | |
671 | // Scale samples if 8bit or 16bit is not saturated |
672 | let current_sample_max = self.header.maximal_sample(); |
673 | let target_sample_max = 256_u32.pow(S::sample_size()) - 1; |
674 | |
675 | if current_sample_max != target_sample_max { |
676 | let factor = target_sample_max as f32 / current_sample_max as f32; |
677 | |
678 | if S::sample_size() == 1 { |
679 | buf.iter_mut().for_each(|v| { |
680 | *v = (*v as f32 * factor).round() as u8; |
681 | }) |
682 | } else if S::sample_size() == 2 { |
683 | for chunk in buf.chunks_exact_mut(2) { |
684 | let v = NativeEndian::read_u16(chunk); |
685 | NativeEndian::write_u16(chunk, (v as f32 * factor).round() as u16); |
686 | } |
687 | } |
688 | } |
689 | |
690 | Ok(()) |
691 | } |
692 | |
693 | fn read_ascii<Basic: Sample>(&mut self, output_buf: &mut [u8]) -> ImageResult<()> { |
694 | Basic::from_ascii(&mut self.reader, output_buf) |
695 | } |
696 | |
697 | /// Get the pnm subtype, depending on the magic constant contained in the header |
698 | pub fn subtype(&self) -> PnmSubtype { |
699 | self.header.subtype() |
700 | } |
701 | } |
702 | |
703 | fn read_separated_ascii<T: FromStr<Err = ParseIntError>>(reader: &mut dyn Read) -> ImageResult<T> |
704 | where |
705 | T::Err: Display, |
706 | { |
707 | let is_separator: impl Fn(&u8) -> bool = |v: &u8| matches! { *v, b' \t' | b' \n' | b' \x0b' | b' \x0c' | b' \r' | b' ' }; |
708 | |
709 | let token: Vec = readerimpl Iterator- >
|
710 | .bytes() |
711 | .skip_while(|v: &Result| v.as_ref().ok().map(is_separator).unwrap_or(default:false)) |
712 | .take_while(|v: &Result| v.as_ref().ok().map(|c| !is_separator(c)).unwrap_or(default:false)) |
713 | .collect::<Result<Vec<u8>, _>>()?; |
714 | |
715 | if !token.is_ascii() { |
716 | return Err(DecoderError::NonAsciiSample.into()); |
717 | } |
718 | |
719 | let string: &str = str::from_utf8(&token) |
720 | // We checked the precondition ourselves a few lines before with `token.is_ascii()`. |
721 | .unwrap_or_else(|_| unreachable!("Only ASCII characters should be decoded" )); |
722 | |
723 | string.parse().map_err(|err: ParseIntError| { |
724 | DecoderError::UnparsableValue(ErrorDataSource::Sample, string.to_owned(), err).into() |
725 | }) |
726 | } |
727 | |
728 | impl Sample for U8 { |
729 | type Representation = u8; |
730 | |
731 | fn from_bytes(bytes: &[u8], _row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> { |
732 | output_buf.copy_from_slice(src:bytes); |
733 | Ok(()) |
734 | } |
735 | |
736 | fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> { |
737 | for b: &mut u8 in output_buf { |
738 | *b = read_separated_ascii(reader)?; |
739 | } |
740 | Ok(()) |
741 | } |
742 | } |
743 | |
744 | impl Sample for U16 { |
745 | type Representation = u16; |
746 | |
747 | fn from_bytes(bytes: &[u8], _row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> { |
748 | output_buf.copy_from_slice(src:bytes); |
749 | for chunk: &mut [u8] in output_buf.chunks_exact_mut(chunk_size:2) { |
750 | let v: u16 = BigEndian::read_u16(buf:chunk); |
751 | NativeEndian::write_u16(buf:chunk, n:v); |
752 | } |
753 | Ok(()) |
754 | } |
755 | |
756 | fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> { |
757 | for chunk: &mut [u8] in output_buf.chunks_exact_mut(chunk_size:2) { |
758 | let v: u16 = read_separated_ascii::<u16>(reader)?; |
759 | NativeEndian::write_u16(buf:chunk, n:v); |
760 | } |
761 | Ok(()) |
762 | } |
763 | } |
764 | |
765 | // The image is encoded in rows of bits, high order bits first. Any bits beyond the row bits should |
766 | // be ignored. Also, contrary to rgb, black pixels are encoded as a 1 while white is 0. This will |
767 | // need to be reversed for the grayscale output. |
768 | impl Sample for PbmBit { |
769 | type Representation = u8; |
770 | |
771 | fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> { |
772 | let count = width * samples; |
773 | let linelen = (count / 8) + ((count % 8) != 0) as u32; |
774 | Ok((linelen * height) as usize) |
775 | } |
776 | |
777 | fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> { |
778 | let mut expanded = utils::expand_bits(1, row_size.try_into().unwrap(), bytes); |
779 | for b in expanded.iter_mut() { |
780 | *b = !*b; |
781 | } |
782 | output_buf.copy_from_slice(&expanded); |
783 | Ok(()) |
784 | } |
785 | |
786 | fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> { |
787 | let mut bytes = reader.bytes(); |
788 | for b in output_buf { |
789 | loop { |
790 | let byte = bytes |
791 | .next() |
792 | .ok_or_else::<ImageError, _>(|| DecoderError::InputTooShort.into())??; |
793 | match byte { |
794 | b' \t' | b' \n' | b' \x0b' | b' \x0c' | b' \r' | b' ' => continue, |
795 | b'0' => *b = 255, |
796 | b'1' => *b = 0, |
797 | c => return Err(DecoderError::UnexpectedByteInRaster(c).into()), |
798 | } |
799 | break; |
800 | } |
801 | } |
802 | |
803 | Ok(()) |
804 | } |
805 | } |
806 | |
807 | // Encoded just like a normal U8 but we check the values. |
808 | impl Sample for BWBit { |
809 | type Representation = u8; |
810 | |
811 | fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> { |
812 | U8::from_bytes(bytes, row_size, output_buf)?; |
813 | if let Some(val: &u8) = output_buf.iter().find(|&val: &u8| *val > 1) { |
814 | return Err(DecoderError::SampleOutOfBounds(*val).into()); |
815 | } |
816 | Ok(()) |
817 | } |
818 | |
819 | fn from_ascii(_reader: &mut dyn Read, _output_buf: &mut [u8]) -> ImageResult<()> { |
820 | unreachable!("BW bits from anymaps are never encoded as ASCII" ) |
821 | } |
822 | } |
823 | |
824 | impl DecodableImageHeader for BitmapHeader { |
825 | fn tuple_type(&self) -> ImageResult<TupleType> { |
826 | Ok(TupleType::PbmBit) |
827 | } |
828 | } |
829 | |
830 | impl DecodableImageHeader for GraymapHeader { |
831 | fn tuple_type(&self) -> ImageResult<TupleType> { |
832 | match self.maxwhite { |
833 | 0 => Err(DecoderError::MaxvalZero.into()), |
834 | v: u32 if v <= 0xFF => Ok(TupleType::GrayU8), |
835 | v: u32 if v <= 0xFFFF => Ok(TupleType::GrayU16), |
836 | _ => Err(DecoderError::MaxvalTooBig(self.maxwhite).into()), |
837 | } |
838 | } |
839 | } |
840 | |
841 | impl DecodableImageHeader for PixmapHeader { |
842 | fn tuple_type(&self) -> ImageResult<TupleType> { |
843 | match self.maxval { |
844 | 0 => Err(DecoderError::MaxvalZero.into()), |
845 | v: u32 if v <= 0xFF => Ok(TupleType::RGBU8), |
846 | v: u32 if v <= 0xFFFF => Ok(TupleType::RGBU16), |
847 | _ => Err(DecoderError::MaxvalTooBig(self.maxval).into()), |
848 | } |
849 | } |
850 | } |
851 | |
852 | impl DecodableImageHeader for ArbitraryHeader { |
853 | fn tuple_type(&self) -> ImageResult<TupleType> { |
854 | match self.tupltype { |
855 | _ if self.maxval == 0 => Err(DecoderError::MaxvalZero.into()), |
856 | None if self.depth == 1 => Ok(TupleType::GrayU8), |
857 | None if self.depth == 2 => Err(ImageError::Unsupported( |
858 | UnsupportedError::from_format_and_kind( |
859 | ImageFormat::Pnm.into(), |
860 | UnsupportedErrorKind::Color(ExtendedColorType::La8), |
861 | ), |
862 | )), |
863 | None if self.depth == 3 => Ok(TupleType::RGBU8), |
864 | None if self.depth == 4 => Err(ImageError::Unsupported( |
865 | UnsupportedError::from_format_and_kind( |
866 | ImageFormat::Pnm.into(), |
867 | UnsupportedErrorKind::Color(ExtendedColorType::Rgba8), |
868 | ), |
869 | )), |
870 | |
871 | Some(ArbitraryTuplType::BlackAndWhite) if self.maxval == 1 && self.depth == 1 => { |
872 | Ok(TupleType::BWBit) |
873 | } |
874 | Some(ArbitraryTuplType::BlackAndWhite) => Err(DecoderError::InvalidDepthOrMaxval { |
875 | tuple_type: ArbitraryTuplType::BlackAndWhite, |
876 | maxval: self.maxval, |
877 | depth: self.depth, |
878 | } |
879 | .into()), |
880 | |
881 | Some(ArbitraryTuplType::Grayscale) if self.depth == 1 && self.maxval <= 0xFF => { |
882 | Ok(TupleType::GrayU8) |
883 | } |
884 | Some(ArbitraryTuplType::Grayscale) if self.depth <= 1 && self.maxval <= 0xFFFF => { |
885 | Ok(TupleType::GrayU16) |
886 | } |
887 | Some(ArbitraryTuplType::Grayscale) => Err(DecoderError::InvalidDepthOrMaxval { |
888 | tuple_type: ArbitraryTuplType::Grayscale, |
889 | maxval: self.maxval, |
890 | depth: self.depth, |
891 | } |
892 | .into()), |
893 | |
894 | Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFF => { |
895 | Ok(TupleType::RGBU8) |
896 | } |
897 | Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFFFF => { |
898 | Ok(TupleType::RGBU16) |
899 | } |
900 | Some(ArbitraryTuplType::RGB) => Err(DecoderError::InvalidDepth { |
901 | tuple_type: ArbitraryTuplType::RGB, |
902 | depth: self.depth, |
903 | } |
904 | .into()), |
905 | |
906 | Some(ArbitraryTuplType::BlackAndWhiteAlpha) => Err(ImageError::Unsupported( |
907 | UnsupportedError::from_format_and_kind( |
908 | ImageFormat::Pnm.into(), |
909 | UnsupportedErrorKind::GenericFeature(format!( |
910 | "Color type {}" , |
911 | ArbitraryTuplType::BlackAndWhiteAlpha.name() |
912 | )), |
913 | ), |
914 | )), |
915 | Some(ArbitraryTuplType::GrayscaleAlpha) => Err(ImageError::Unsupported( |
916 | UnsupportedError::from_format_and_kind( |
917 | ImageFormat::Pnm.into(), |
918 | UnsupportedErrorKind::Color(ExtendedColorType::La8), |
919 | ), |
920 | )), |
921 | Some(ArbitraryTuplType::RGBAlpha) => Err(ImageError::Unsupported( |
922 | UnsupportedError::from_format_and_kind( |
923 | ImageFormat::Pnm.into(), |
924 | UnsupportedErrorKind::Color(ExtendedColorType::Rgba8), |
925 | ), |
926 | )), |
927 | Some(ArbitraryTuplType::Custom(ref custom)) => Err(ImageError::Unsupported( |
928 | UnsupportedError::from_format_and_kind( |
929 | ImageFormat::Pnm.into(), |
930 | UnsupportedErrorKind::GenericFeature(format!("Tuple type {:?}" , custom)), |
931 | ), |
932 | )), |
933 | None => Err(DecoderError::TupleTypeUnrecognised.into()), |
934 | } |
935 | } |
936 | } |
937 | |
938 | #[cfg (test)] |
939 | mod tests { |
940 | use super::*; |
941 | /// Tests reading of a valid blackandwhite pam |
942 | #[test ] |
943 | fn pam_blackandwhite() { |
944 | let pamdata = b"P7 |
945 | WIDTH 4 |
946 | HEIGHT 4 |
947 | DEPTH 1 |
948 | MAXVAL 1 |
949 | TUPLTYPE BLACKANDWHITE |
950 | # Comment line |
951 | ENDHDR |
952 | \x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01" ; |
953 | let decoder = PnmDecoder::new(&pamdata[..]).unwrap(); |
954 | assert_eq!(decoder.color_type(), ColorType::L8); |
955 | assert_eq!(decoder.original_color_type(), ExtendedColorType::L1); |
956 | assert_eq!(decoder.dimensions(), (4, 4)); |
957 | assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap); |
958 | |
959 | let mut image = vec![0; decoder.total_bytes() as usize]; |
960 | decoder.read_image(&mut image).unwrap(); |
961 | assert_eq!( |
962 | image, |
963 | vec![ |
964 | 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, |
965 | 0x00, 0xFF |
966 | ] |
967 | ); |
968 | match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() { |
969 | ( |
970 | _, |
971 | PnmHeader { |
972 | decoded: |
973 | HeaderRecord::Arbitrary(ArbitraryHeader { |
974 | width: 4, |
975 | height: 4, |
976 | maxval: 1, |
977 | depth: 1, |
978 | tupltype: Some(ArbitraryTuplType::BlackAndWhite), |
979 | }), |
980 | encoded: _, |
981 | }, |
982 | ) => (), |
983 | _ => panic!("Decoded header is incorrect" ), |
984 | } |
985 | } |
986 | |
987 | /// Tests reading of a valid grayscale pam |
988 | #[test ] |
989 | fn pam_grayscale() { |
990 | let pamdata = b"P7 |
991 | WIDTH 4 |
992 | HEIGHT 4 |
993 | DEPTH 1 |
994 | MAXVAL 255 |
995 | TUPLTYPE GRAYSCALE |
996 | # Comment line |
997 | ENDHDR |
998 | \xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef" ; |
999 | let decoder = PnmDecoder::new(&pamdata[..]).unwrap(); |
1000 | assert_eq!(decoder.color_type(), ColorType::L8); |
1001 | assert_eq!(decoder.dimensions(), (4, 4)); |
1002 | assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap); |
1003 | |
1004 | let mut image = vec![0; decoder.total_bytes() as usize]; |
1005 | decoder.read_image(&mut image).unwrap(); |
1006 | assert_eq!( |
1007 | image, |
1008 | vec![ |
1009 | 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, |
1010 | 0xbe, 0xef |
1011 | ] |
1012 | ); |
1013 | match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() { |
1014 | ( |
1015 | _, |
1016 | PnmHeader { |
1017 | decoded: |
1018 | HeaderRecord::Arbitrary(ArbitraryHeader { |
1019 | width: 4, |
1020 | height: 4, |
1021 | depth: 1, |
1022 | maxval: 255, |
1023 | tupltype: Some(ArbitraryTuplType::Grayscale), |
1024 | }), |
1025 | encoded: _, |
1026 | }, |
1027 | ) => (), |
1028 | _ => panic!("Decoded header is incorrect" ), |
1029 | } |
1030 | } |
1031 | |
1032 | /// Tests reading of a valid rgb pam |
1033 | #[test ] |
1034 | fn pam_rgb() { |
1035 | let pamdata = b"P7 |
1036 | # Comment line |
1037 | MAXVAL 255 |
1038 | TUPLTYPE RGB |
1039 | DEPTH 3 |
1040 | WIDTH 2 |
1041 | HEIGHT 2 |
1042 | ENDHDR |
1043 | \xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef" ; |
1044 | let decoder = PnmDecoder::new(&pamdata[..]).unwrap(); |
1045 | assert_eq!(decoder.color_type(), ColorType::Rgb8); |
1046 | assert_eq!(decoder.dimensions(), (2, 2)); |
1047 | assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap); |
1048 | |
1049 | let mut image = vec![0; decoder.total_bytes() as usize]; |
1050 | decoder.read_image(&mut image).unwrap(); |
1051 | assert_eq!( |
1052 | image, |
1053 | vec![0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef] |
1054 | ); |
1055 | match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() { |
1056 | ( |
1057 | _, |
1058 | PnmHeader { |
1059 | decoded: |
1060 | HeaderRecord::Arbitrary(ArbitraryHeader { |
1061 | maxval: 255, |
1062 | tupltype: Some(ArbitraryTuplType::RGB), |
1063 | depth: 3, |
1064 | width: 2, |
1065 | height: 2, |
1066 | }), |
1067 | encoded: _, |
1068 | }, |
1069 | ) => (), |
1070 | _ => panic!("Decoded header is incorrect" ), |
1071 | } |
1072 | } |
1073 | |
1074 | #[test ] |
1075 | fn pbm_binary() { |
1076 | // The data contains two rows of the image (each line is padded to the full byte). For |
1077 | // comments on its format, see documentation of `impl SampleType for PbmBit`. |
1078 | let pbmbinary = [&b"P4 6 2 \n" [..], &[0b01101100_u8, 0b10110111]].concat(); |
1079 | let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap(); |
1080 | assert_eq!(decoder.color_type(), ColorType::L8); |
1081 | assert_eq!(decoder.original_color_type(), ExtendedColorType::L1); |
1082 | assert_eq!(decoder.dimensions(), (6, 2)); |
1083 | assert_eq!( |
1084 | decoder.subtype(), |
1085 | PnmSubtype::Bitmap(SampleEncoding::Binary) |
1086 | ); |
1087 | let mut image = vec![0; decoder.total_bytes() as usize]; |
1088 | decoder.read_image(&mut image).unwrap(); |
1089 | assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]); |
1090 | match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() { |
1091 | ( |
1092 | _, |
1093 | PnmHeader { |
1094 | decoded: |
1095 | HeaderRecord::Bitmap(BitmapHeader { |
1096 | encoding: SampleEncoding::Binary, |
1097 | width: 6, |
1098 | height: 2, |
1099 | }), |
1100 | encoded: _, |
1101 | }, |
1102 | ) => (), |
1103 | _ => panic!("Decoded header is incorrect" ), |
1104 | } |
1105 | } |
1106 | |
1107 | /// A previous infinite loop. |
1108 | #[test ] |
1109 | fn pbm_binary_ascii_termination() { |
1110 | use std::io::{BufReader, Cursor, Error, ErrorKind, Read, Result}; |
1111 | struct FailRead(Cursor<&'static [u8]>); |
1112 | |
1113 | impl Read for FailRead { |
1114 | fn read(&mut self, buf: &mut [u8]) -> Result<usize> { |
1115 | match self.0.read(buf) { |
1116 | Ok(n) if n > 0 => Ok(n), |
1117 | _ => Err(Error::new( |
1118 | ErrorKind::BrokenPipe, |
1119 | "Simulated broken pipe error" , |
1120 | )), |
1121 | } |
1122 | } |
1123 | } |
1124 | |
1125 | let pbmbinary = BufReader::new(FailRead(Cursor::new(b"P1 1 1 \n" ))); |
1126 | |
1127 | let decoder = PnmDecoder::new(pbmbinary).unwrap(); |
1128 | let mut image = vec![0; decoder.total_bytes() as usize]; |
1129 | decoder |
1130 | .read_image(&mut image) |
1131 | .expect_err("Image is malformed" ); |
1132 | } |
1133 | |
1134 | #[test ] |
1135 | fn pbm_ascii() { |
1136 | // The data contains two rows of the image (each line is padded to the full byte). For |
1137 | // comments on its format, see documentation of `impl SampleType for PbmBit`. Tests all |
1138 | // whitespace characters that should be allowed (the 6 characters according to POSIX). |
1139 | let pbmbinary = b"P1 6 2 \n 0 1 1 0 1 1 \n1 0 1 1 0 \t\n\x0b\x0c\r1" ; |
1140 | let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap(); |
1141 | assert_eq!(decoder.color_type(), ColorType::L8); |
1142 | assert_eq!(decoder.original_color_type(), ExtendedColorType::L1); |
1143 | assert_eq!(decoder.dimensions(), (6, 2)); |
1144 | assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii)); |
1145 | |
1146 | let mut image = vec![0; decoder.total_bytes() as usize]; |
1147 | decoder.read_image(&mut image).unwrap(); |
1148 | assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]); |
1149 | match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() { |
1150 | ( |
1151 | _, |
1152 | PnmHeader { |
1153 | decoded: |
1154 | HeaderRecord::Bitmap(BitmapHeader { |
1155 | encoding: SampleEncoding::Ascii, |
1156 | width: 6, |
1157 | height: 2, |
1158 | }), |
1159 | encoded: _, |
1160 | }, |
1161 | ) => (), |
1162 | _ => panic!("Decoded header is incorrect" ), |
1163 | } |
1164 | } |
1165 | |
1166 | #[test ] |
1167 | fn pbm_ascii_nospace() { |
1168 | // The data contains two rows of the image (each line is padded to the full byte). Notably, |
1169 | // it is completely within specification for the ascii data not to contain separating |
1170 | // whitespace for the pbm format or any mix. |
1171 | let pbmbinary = b"P1 6 2 \n011011101101" ; |
1172 | let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap(); |
1173 | assert_eq!(decoder.color_type(), ColorType::L8); |
1174 | assert_eq!(decoder.original_color_type(), ExtendedColorType::L1); |
1175 | assert_eq!(decoder.dimensions(), (6, 2)); |
1176 | assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii)); |
1177 | |
1178 | let mut image = vec![0; decoder.total_bytes() as usize]; |
1179 | decoder.read_image(&mut image).unwrap(); |
1180 | assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]); |
1181 | match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() { |
1182 | ( |
1183 | _, |
1184 | PnmHeader { |
1185 | decoded: |
1186 | HeaderRecord::Bitmap(BitmapHeader { |
1187 | encoding: SampleEncoding::Ascii, |
1188 | width: 6, |
1189 | height: 2, |
1190 | }), |
1191 | encoded: _, |
1192 | }, |
1193 | ) => (), |
1194 | _ => panic!("Decoded header is incorrect" ), |
1195 | } |
1196 | } |
1197 | |
1198 | #[test ] |
1199 | fn pgm_binary() { |
1200 | // The data contains two rows of the image (each line is padded to the full byte). For |
1201 | // comments on its format, see documentation of `impl SampleType for PbmBit`. |
1202 | let elements = (0..16).collect::<Vec<_>>(); |
1203 | let pbmbinary = [&b"P5 4 4 255 \n" [..], &elements].concat(); |
1204 | let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap(); |
1205 | assert_eq!(decoder.color_type(), ColorType::L8); |
1206 | assert_eq!(decoder.dimensions(), (4, 4)); |
1207 | assert_eq!( |
1208 | decoder.subtype(), |
1209 | PnmSubtype::Graymap(SampleEncoding::Binary) |
1210 | ); |
1211 | let mut image = vec![0; decoder.total_bytes() as usize]; |
1212 | decoder.read_image(&mut image).unwrap(); |
1213 | assert_eq!(image, elements); |
1214 | match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() { |
1215 | ( |
1216 | _, |
1217 | PnmHeader { |
1218 | decoded: |
1219 | HeaderRecord::Graymap(GraymapHeader { |
1220 | encoding: SampleEncoding::Binary, |
1221 | width: 4, |
1222 | height: 4, |
1223 | maxwhite: 255, |
1224 | }), |
1225 | encoded: _, |
1226 | }, |
1227 | ) => (), |
1228 | _ => panic!("Decoded header is incorrect" ), |
1229 | } |
1230 | } |
1231 | |
1232 | #[test ] |
1233 | fn pgm_ascii() { |
1234 | // The data contains two rows of the image (each line is padded to the full byte). For |
1235 | // comments on its format, see documentation of `impl SampleType for PbmBit`. |
1236 | let pbmbinary = b"P2 4 4 255 \n 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15" ; |
1237 | let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap(); |
1238 | assert_eq!(decoder.color_type(), ColorType::L8); |
1239 | assert_eq!(decoder.dimensions(), (4, 4)); |
1240 | assert_eq!( |
1241 | decoder.subtype(), |
1242 | PnmSubtype::Graymap(SampleEncoding::Ascii) |
1243 | ); |
1244 | let mut image = vec![0; decoder.total_bytes() as usize]; |
1245 | decoder.read_image(&mut image).unwrap(); |
1246 | assert_eq!(image, (0..16).collect::<Vec<_>>()); |
1247 | match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() { |
1248 | ( |
1249 | _, |
1250 | PnmHeader { |
1251 | decoded: |
1252 | HeaderRecord::Graymap(GraymapHeader { |
1253 | encoding: SampleEncoding::Ascii, |
1254 | width: 4, |
1255 | height: 4, |
1256 | maxwhite: 255, |
1257 | }), |
1258 | encoded: _, |
1259 | }, |
1260 | ) => (), |
1261 | _ => panic!("Decoded header is incorrect" ), |
1262 | } |
1263 | } |
1264 | |
1265 | #[test ] |
1266 | fn ppm_ascii() { |
1267 | let ascii = b"P3 1 1 2000 \n0 1000 2000" ; |
1268 | let decoder = PnmDecoder::new(&ascii[..]).unwrap(); |
1269 | let mut image = vec![0; decoder.total_bytes() as usize]; |
1270 | decoder.read_image(&mut image).unwrap(); |
1271 | assert_eq!( |
1272 | image, |
1273 | [ |
1274 | 0_u16.to_ne_bytes(), |
1275 | (u16::MAX / 2 + 1).to_ne_bytes(), |
1276 | u16::MAX.to_ne_bytes() |
1277 | ] |
1278 | .into_iter() |
1279 | .flatten() |
1280 | .collect::<Vec<_>>() |
1281 | ); |
1282 | } |
1283 | |
1284 | #[test ] |
1285 | fn dimension_overflow() { |
1286 | let pamdata = b"P7 |
1287 | # Comment line |
1288 | MAXVAL 255 |
1289 | TUPLTYPE RGB |
1290 | DEPTH 3 |
1291 | WIDTH 4294967295 |
1292 | HEIGHT 4294967295 |
1293 | ENDHDR |
1294 | \xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef" ; |
1295 | |
1296 | assert!(PnmDecoder::new(&pamdata[..]).is_err()); |
1297 | } |
1298 | |
1299 | #[test ] |
1300 | fn issue_1508() { |
1301 | let _ = crate::load_from_memory(b"P391919 16999 1 1 9 919 16999 1 9999 999* 99999 N" ); |
1302 | } |
1303 | |
1304 | #[test ] |
1305 | fn issue_1616_overflow() { |
1306 | let data = [ |
1307 | 80, 54, 10, 52, 50, 57, 52, 56, 50, 57, 52, 56, 35, 56, 10, 52, 10, 48, 10, 12, 12, 56, |
1308 | ]; |
1309 | // Validate: we have a header. Note: we might already calculate that this will fail but |
1310 | // then we could not return information about the header to the caller. |
1311 | let decoder = PnmDecoder::new(&data[..]).unwrap(); |
1312 | let mut image = vec![0; decoder.total_bytes() as usize]; |
1313 | let _ = decoder.read_image(&mut image); |
1314 | } |
1315 | } |
1316 | |