use std::error;
use std::fmt::{self, Display};
use std::io::{self, BufRead, Cursor, Read};
use std::marker::PhantomData;
use std::mem;
use std::num::ParseIntError;
use std::str::{self, FromStr};
use super::{ArbitraryHeader, ArbitraryTuplType, BitmapHeader, GraymapHeader, PixmapHeader};
use super::{HeaderRecord, PnmHeader, PnmSubtype, SampleEncoding};
use crate::color::{ColorType, ExtendedColorType};
use crate::error::{
DecodingError, ImageError, ImageResult, UnsupportedError, UnsupportedErrorKind,
};
use crate::image::{self, ImageDecoder, ImageFormat};
use crate::utils;
use byteorder::{BigEndian, ByteOrder, NativeEndian};
#[derive(Debug, Clone)]
enum DecoderError {
PnmMagicInvalid([u8; 2]),
UnparsableValue(ErrorDataSource, String, ParseIntError),
NonAsciiByteInHeader(u8),
NonAsciiLineInPamHeader,
NonAsciiSample,
NotNewlineAfterP7Magic(u8),
UnexpectedPnmHeaderEnd,
HeaderLineDuplicated(PnmHeaderLine),
HeaderLineUnknown(String),
#[allow(missing_docs)]
HeaderLineMissing {
height: Option<u32>,
width: Option<u32>,
depth: Option<u32>,
maxval: Option<u32>,
},
InputTooShort,
UnexpectedByteInRaster(u8),
SampleOutOfBounds(u8),
MaxvalZero,
MaxvalTooBig(u32),
InvalidDepthOrMaxval {
tuple_type: ArbitraryTuplType,
depth: u32,
maxval: u32,
},
InvalidDepth {
tuple_type: ArbitraryTuplType,
depth: u32,
},
TupleTypeUnrecognised,
Overflow,
}
impl Display for DecoderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DecoderError::PnmMagicInvalid(magic) => f.write_fmt(format_args!(
"Expected magic constant for PNM: P1..P7, got [{:#04X?}, {:#04X?}]",
magic[0], magic[1]
)),
DecoderError::UnparsableValue(src, data, err) => {
f.write_fmt(format_args!("Error parsing {:?} as {}: {}", data, src, err))
}
DecoderError::NonAsciiByteInHeader(c) => {
f.write_fmt(format_args!("Non-ASCII character {:#04X?} in header", c))
}
DecoderError::NonAsciiLineInPamHeader => f.write_str("Non-ASCII line in PAM header"),
DecoderError::NonAsciiSample => {
f.write_str("Non-ASCII character where sample value was expected")
}
DecoderError::NotNewlineAfterP7Magic(c) => f.write_fmt(format_args!(
"Expected newline after P7 magic, got {:#04X?}",
c
)),
DecoderError::UnexpectedPnmHeaderEnd => f.write_str("Unexpected end of PNM header"),
DecoderError::HeaderLineDuplicated(line) => {
f.write_fmt(format_args!("Duplicate {} line", line))
}
DecoderError::HeaderLineUnknown(identifier) => f.write_fmt(format_args!(
"Unknown header line with identifier {:?}",
identifier
)),
DecoderError::HeaderLineMissing {
height,
width,
depth,
maxval,
} => f.write_fmt(format_args!(
"Missing header line: have height={:?}, width={:?}, depth={:?}, maxval={:?}",
height, width, depth, maxval
)),
DecoderError::InputTooShort => {
f.write_str("Not enough data was provided to the Decoder to decode the image")
}
DecoderError::UnexpectedByteInRaster(c) => f.write_fmt(format_args!(
"Unexpected character {:#04X?} within sample raster",
c
)),
DecoderError::SampleOutOfBounds(val) => {
f.write_fmt(format_args!("Sample value {} outside of bounds", val))
}
DecoderError::MaxvalZero => f.write_str("Image MAXVAL is zero"),
DecoderError::MaxvalTooBig(maxval) => {
f.write_fmt(format_args!("Image MAXVAL exceeds {}: {}", 0xFFFF, maxval))
}
DecoderError::InvalidDepthOrMaxval {
tuple_type,
depth,
maxval,
} => f.write_fmt(format_args!(
"Invalid depth ({}) or maxval ({}) for tuple type {}",
depth,
maxval,
tuple_type.name()
)),
DecoderError::InvalidDepth { tuple_type, depth } => f.write_fmt(format_args!(
"Invalid depth ({}) for tuple type {}",
depth,
tuple_type.name()
)),
DecoderError::TupleTypeUnrecognised => f.write_str("Tuple type not recognized"),
DecoderError::Overflow => f.write_str("Overflow when parsing value"),
}
}
}
impl From<DecoderError> for ImageError {
fn from(e: DecoderError) -> ImageError {
ImageError::Decoding(DecodingError::new(ImageFormat::Pnm.into(), e))
}
}
impl error::Error for DecoderError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
DecoderError::UnparsableValue(_, _, err) => Some(err),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum PnmHeaderLine {
Height,
Width,
Depth,
Maxval,
}
impl Display for PnmHeaderLine {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
PnmHeaderLine::Height => "HEIGHT",
PnmHeaderLine::Width => "WIDTH",
PnmHeaderLine::Depth => "DEPTH",
PnmHeaderLine::Maxval => "MAXVAL",
})
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum ErrorDataSource {
Line(PnmHeaderLine),
Preamble,
Sample,
}
impl Display for ErrorDataSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorDataSource::Line(l) => l.fmt(f),
ErrorDataSource::Preamble => f.write_str("number in preamble"),
ErrorDataSource::Sample => f.write_str("sample"),
}
}
}
#[derive(Clone, Copy)]
enum TupleType {
PbmBit,
BWBit,
GrayU8,
GrayU16,
RGBU8,
RGBU16,
}
trait Sample {
type Representation;
fn sample_size() -> u32 {
std::mem::size_of::<Self::Representation>() as u32
}
fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
Ok((width * height * samples * Self::sample_size()) as usize)
}
fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()>;
fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()>;
}
struct U8;
struct U16;
struct PbmBit;
struct BWBit;
trait DecodableImageHeader {
fn tuple_type(&self) -> ImageResult<TupleType>;
}
pub struct PnmDecoder<R> {
reader: R,
header: PnmHeader,
tuple: TupleType,
}
impl<R: BufRead> PnmDecoder<R> {
pub fn new(mut buffered_read: R) -> ImageResult<PnmDecoder<R>> {
let magic = buffered_read.read_magic_constant()?;
let subtype = match magic {
[b'P', b'1'] => PnmSubtype::Bitmap(SampleEncoding::Ascii),
[b'P', b'2'] => PnmSubtype::Graymap(SampleEncoding::Ascii),
[b'P', b'3'] => PnmSubtype::Pixmap(SampleEncoding::Ascii),
[b'P', b'4'] => PnmSubtype::Bitmap(SampleEncoding::Binary),
[b'P', b'5'] => PnmSubtype::Graymap(SampleEncoding::Binary),
[b'P', b'6'] => PnmSubtype::Pixmap(SampleEncoding::Binary),
[b'P', b'7'] => PnmSubtype::ArbitraryMap,
_ => return Err(DecoderError::PnmMagicInvalid(magic).into()),
};
let decoder = match subtype {
PnmSubtype::Bitmap(enc) => PnmDecoder::read_bitmap_header(buffered_read, enc),
PnmSubtype::Graymap(enc) => PnmDecoder::read_graymap_header(buffered_read, enc),
PnmSubtype::Pixmap(enc) => PnmDecoder::read_pixmap_header(buffered_read, enc),
PnmSubtype::ArbitraryMap => PnmDecoder::read_arbitrary_header(buffered_read),
}?;
if utils::check_dimension_overflow(
decoder.dimensions().0,
decoder.dimensions().1,
decoder.color_type().bytes_per_pixel(),
) {
return Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Pnm.into(),
UnsupportedErrorKind::GenericFeature(format!(
"Image dimensions ({}x{}) are too large",
decoder.dimensions().0,
decoder.dimensions().1
)),
),
));
}
Ok(decoder)
}
pub fn into_inner(self) -> (R, PnmHeader) {
(self.reader, self.header)
}
fn read_bitmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
let header = reader.read_bitmap_header(encoding)?;
Ok(PnmDecoder {
reader,
tuple: TupleType::PbmBit,
header: PnmHeader {
decoded: HeaderRecord::Bitmap(header),
encoded: None,
},
})
}
fn read_graymap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
let header = reader.read_graymap_header(encoding)?;
let tuple_type = header.tuple_type()?;
Ok(PnmDecoder {
reader,
tuple: tuple_type,
header: PnmHeader {
decoded: HeaderRecord::Graymap(header),
encoded: None,
},
})
}
fn read_pixmap_header(mut reader: R, encoding: SampleEncoding) -> ImageResult<PnmDecoder<R>> {
let header = reader.read_pixmap_header(encoding)?;
let tuple_type = header.tuple_type()?;
Ok(PnmDecoder {
reader,
tuple: tuple_type,
header: PnmHeader {
decoded: HeaderRecord::Pixmap(header),
encoded: None,
},
})
}
fn read_arbitrary_header(mut reader: R) -> ImageResult<PnmDecoder<R>> {
let header = reader.read_arbitrary_header()?;
let tuple_type = header.tuple_type()?;
Ok(PnmDecoder {
reader,
tuple: tuple_type,
header: PnmHeader {
decoded: HeaderRecord::Arbitrary(header),
encoded: None,
},
})
}
}
trait HeaderReader: BufRead {
fn read_magic_constant(&mut self) -> ImageResult<[u8; 2]> {
let mut magic: [u8; 2] = [0, 0];
self.read_exact(&mut magic)?;
Ok(magic)
}
fn read_next_string(&mut self) -> ImageResult<String> {
let mut bytes = Vec::new();
let mark_comments = self.bytes().scan(true, |partof, read| {
let byte = match read {
Err(err) => return Some((*partof, Err(err))),
Ok(byte) => byte,
};
let cur_enabled = *partof && byte != b'#';
let next_enabled = cur_enabled || (byte == b'\r' || byte == b'\n');
*partof = next_enabled;
Some((cur_enabled, Ok(byte)))
});
for (_, byte) in mark_comments.filter(|e| e.0) {
match byte {
Ok(b'\t') | Ok(b'\n') | Ok(b'\x0b') | Ok(b'\x0c') | Ok(b'\r') | Ok(b' ') => {
if !bytes.is_empty() {
break; }
}
Ok(byte) if !byte.is_ascii() => {
return Err(DecoderError::NonAsciiByteInHeader(byte).into())
}
Ok(byte) => {
bytes.push(byte);
}
Err(_) => break,
}
}
if bytes.is_empty() {
return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into()));
}
if !bytes.as_slice().is_ascii() {
unreachable!("Non-ASCII character should have returned sooner")
}
let string = String::from_utf8(bytes)
.unwrap_or_else(|_| unreachable!("Only ASCII characters should be decoded"));
Ok(string)
}
fn read_next_u32(&mut self) -> ImageResult<u32> {
let s = self.read_next_string()?;
s.parse::<u32>()
.map_err(|err| DecoderError::UnparsableValue(ErrorDataSource::Preamble, s, err).into())
}
fn read_bitmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<BitmapHeader> {
let width = self.read_next_u32()?;
let height = self.read_next_u32()?;
Ok(BitmapHeader {
encoding,
width,
height,
})
}
fn read_graymap_header(&mut self, encoding: SampleEncoding) -> ImageResult<GraymapHeader> {
self.read_pixmap_header(encoding).map(
|PixmapHeader {
encoding,
width,
height,
maxval,
}| GraymapHeader {
encoding,
width,
height,
maxwhite: maxval,
},
)
}
fn read_pixmap_header(&mut self, encoding: SampleEncoding) -> ImageResult<PixmapHeader> {
let width = self.read_next_u32()?;
let height = self.read_next_u32()?;
let maxval = self.read_next_u32()?;
Ok(PixmapHeader {
encoding,
width,
height,
maxval,
})
}
fn read_arbitrary_header(&mut self) -> ImageResult<ArbitraryHeader> {
fn parse_single_value_line(
line_val: &mut Option<u32>,
rest: &str,
line: PnmHeaderLine,
) -> ImageResult<()> {
if line_val.is_some() {
Err(DecoderError::HeaderLineDuplicated(line).into())
} else {
let v = rest.trim().parse().map_err(|err| {
DecoderError::UnparsableValue(ErrorDataSource::Line(line), rest.to_owned(), err)
})?;
*line_val = Some(v);
Ok(())
}
}
match self.bytes().next() {
None => return Err(ImageError::IoError(io::ErrorKind::UnexpectedEof.into())),
Some(Err(io)) => return Err(ImageError::IoError(io)),
Some(Ok(b'\n')) => (),
Some(Ok(c)) => return Err(DecoderError::NotNewlineAfterP7Magic(c).into()),
}
let mut line = String::new();
let mut height: Option<u32> = None;
let mut width: Option<u32> = None;
let mut depth: Option<u32> = None;
let mut maxval: Option<u32> = None;
let mut tupltype: Option<String> = None;
loop {
line.truncate(0);
let len = self.read_line(&mut line)?;
if len == 0 {
return Err(DecoderError::UnexpectedPnmHeaderEnd.into());
}
if line.as_bytes()[0] == b'#' {
continue;
}
if !line.is_ascii() {
return Err(DecoderError::NonAsciiLineInPamHeader.into());
}
#[allow(deprecated)]
let (identifier, rest) = line
.trim_left()
.split_at(line.find(char::is_whitespace).unwrap_or(line.len()));
match identifier {
"ENDHDR" => break,
"HEIGHT" => parse_single_value_line(&mut height, rest, PnmHeaderLine::Height)?,
"WIDTH" => parse_single_value_line(&mut width, rest, PnmHeaderLine::Width)?,
"DEPTH" => parse_single_value_line(&mut depth, rest, PnmHeaderLine::Depth)?,
"MAXVAL" => parse_single_value_line(&mut maxval, rest, PnmHeaderLine::Maxval)?,
"TUPLTYPE" => {
let identifier = rest.trim();
if tupltype.is_some() {
let appended = tupltype.take().map(|mut v| {
v.push(' ');
v.push_str(identifier);
v
});
tupltype = appended;
} else {
tupltype = Some(identifier.to_string());
}
}
_ => return Err(DecoderError::HeaderLineUnknown(identifier.to_string()).into()),
}
}
let (h, w, d, m) = match (height, width, depth, maxval) {
(Some(h), Some(w), Some(d), Some(m)) => (h, w, d, m),
_ => {
return Err(DecoderError::HeaderLineMissing {
height,
width,
depth,
maxval,
}
.into())
}
};
let tupltype = match tupltype {
None => None,
Some(ref t) if t == "BLACKANDWHITE" => Some(ArbitraryTuplType::BlackAndWhite),
Some(ref t) if t == "BLACKANDWHITE_ALPHA" => {
Some(ArbitraryTuplType::BlackAndWhiteAlpha)
}
Some(ref t) if t == "GRAYSCALE" => Some(ArbitraryTuplType::Grayscale),
Some(ref t) if t == "GRAYSCALE_ALPHA" => Some(ArbitraryTuplType::GrayscaleAlpha),
Some(ref t) if t == "RGB" => Some(ArbitraryTuplType::RGB),
Some(ref t) if t == "RGB_ALPHA" => Some(ArbitraryTuplType::RGBAlpha),
Some(other) => Some(ArbitraryTuplType::Custom(other)),
};
Ok(ArbitraryHeader {
height: h,
width: w,
depth: d,
maxval: m,
tupltype,
})
}
}
impl<R> HeaderReader for R where R: BufRead {}
pub struct PnmReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for PnmReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
if self.0.position() == 0 && buf.is_empty() {
mem::swap(buf, self.0.get_mut());
Ok(buf.len())
} else {
self.0.read_to_end(buf)
}
}
}
impl<'a, R: 'a + Read> ImageDecoder<'a> for PnmDecoder<R> {
type Reader = PnmReader<R>;
fn dimensions(&self) -> (u32, u32) {
(self.header.width(), self.header.height())
}
fn color_type(&self) -> ColorType {
match self.tuple {
TupleType::PbmBit => ColorType::L8,
TupleType::BWBit => ColorType::L8,
TupleType::GrayU8 => ColorType::L8,
TupleType::GrayU16 => ColorType::L16,
TupleType::RGBU8 => ColorType::Rgb8,
TupleType::RGBU16 => ColorType::Rgb16,
}
}
fn original_color_type(&self) -> ExtendedColorType {
match self.tuple {
TupleType::PbmBit => ExtendedColorType::L1,
TupleType::BWBit => ExtendedColorType::L1,
TupleType::GrayU8 => ExtendedColorType::L8,
TupleType::GrayU16 => ExtendedColorType::L16,
TupleType::RGBU8 => ExtendedColorType::Rgb8,
TupleType::RGBU16 => ExtendedColorType::Rgb16,
}
}
fn into_reader(self) -> ImageResult<Self::Reader> {
Ok(PnmReader(
Cursor::new(image::decoder_to_vec(self)?),
PhantomData,
))
}
fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
match self.tuple {
TupleType::PbmBit => self.read_samples::<PbmBit>(1, buf),
TupleType::BWBit => self.read_samples::<BWBit>(1, buf),
TupleType::RGBU8 => self.read_samples::<U8>(3, buf),
TupleType::RGBU16 => self.read_samples::<U16>(3, buf),
TupleType::GrayU8 => self.read_samples::<U8>(1, buf),
TupleType::GrayU16 => self.read_samples::<U16>(1, buf),
}
}
}
impl<R: Read> PnmDecoder<R> {
fn read_samples<S: Sample>(&mut self, components: u32, buf: &mut [u8]) -> ImageResult<()> {
match self.subtype().sample_encoding() {
SampleEncoding::Binary => {
let width = self.header.width();
let height = self.header.height();
let bytecount = S::bytelen(width, height, components)?;
let mut bytes = vec![];
self.reader
.by_ref()
.take(bytecount as u64)
.read_to_end(&mut bytes)?;
if bytes.len() != bytecount {
return Err(DecoderError::InputTooShort.into());
}
let width: usize = width.try_into().map_err(|_| DecoderError::Overflow)?;
let components: usize =
components.try_into().map_err(|_| DecoderError::Overflow)?;
let row_size = width
.checked_mul(components)
.ok_or(DecoderError::Overflow)?;
S::from_bytes(&bytes, row_size, buf)?;
}
SampleEncoding::Ascii => {
self.read_ascii::<S>(buf)?;
}
};
let current_sample_max = self.header.maximal_sample();
let target_sample_max = 256_u32.pow(S::sample_size()) - 1;
if current_sample_max != target_sample_max {
let factor = target_sample_max as f32 / current_sample_max as f32;
if S::sample_size() == 1 {
buf.iter_mut().for_each(|v| {
*v = (*v as f32 * factor).round() as u8;
})
} else if S::sample_size() == 2 {
for chunk in buf.chunks_exact_mut(2) {
let v = NativeEndian::read_u16(chunk);
NativeEndian::write_u16(chunk, (v as f32 * factor).round() as u16);
}
}
}
Ok(())
}
fn read_ascii<Basic: Sample>(&mut self, output_buf: &mut [u8]) -> ImageResult<()> {
Basic::from_ascii(&mut self.reader, output_buf)
}
pub fn subtype(&self) -> PnmSubtype {
self.header.subtype()
}
}
fn read_separated_ascii<T: FromStr<Err = ParseIntError>>(reader: &mut dyn Read) -> ImageResult<T>
where
T::Err: Display,
{
let is_separator = |v: &u8| matches! { *v, b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ' };
let token = reader
.bytes()
.skip_while(|v| v.as_ref().ok().map(is_separator).unwrap_or(false))
.take_while(|v| v.as_ref().ok().map(|c| !is_separator(c)).unwrap_or(false))
.collect::<Result<Vec<u8>, _>>()?;
if !token.is_ascii() {
return Err(DecoderError::NonAsciiSample.into());
}
let string = str::from_utf8(&token)
.unwrap_or_else(|_| unreachable!("Only ASCII characters should be decoded"));
string.parse().map_err(|err| {
DecoderError::UnparsableValue(ErrorDataSource::Sample, string.to_owned(), err).into()
})
}
impl Sample for U8 {
type Representation = u8;
fn from_bytes(bytes: &[u8], _row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
output_buf.copy_from_slice(bytes);
Ok(())
}
fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
for b in output_buf {
*b = read_separated_ascii(reader)?;
}
Ok(())
}
}
impl Sample for U16 {
type Representation = u16;
fn from_bytes(bytes: &[u8], _row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
output_buf.copy_from_slice(bytes);
for chunk in output_buf.chunks_exact_mut(2) {
let v = BigEndian::read_u16(chunk);
NativeEndian::write_u16(chunk, v);
}
Ok(())
}
fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
for chunk in output_buf.chunks_exact_mut(2) {
let v = read_separated_ascii::<u16>(reader)?;
NativeEndian::write_u16(chunk, v);
}
Ok(())
}
}
impl Sample for PbmBit {
type Representation = u8;
fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
let count = width * samples;
let linelen = (count / 8) + ((count % 8) != 0) as u32;
Ok((linelen * height) as usize)
}
fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
let mut expanded = utils::expand_bits(1, row_size.try_into().unwrap(), bytes);
for b in expanded.iter_mut() {
*b = !*b;
}
output_buf.copy_from_slice(&expanded);
Ok(())
}
fn from_ascii(reader: &mut dyn Read, output_buf: &mut [u8]) -> ImageResult<()> {
let mut bytes = reader.bytes();
for b in output_buf {
loop {
let byte = bytes
.next()
.ok_or_else::<ImageError, _>(|| DecoderError::InputTooShort.into())??;
match byte {
b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ' => continue,
b'0' => *b = 255,
b'1' => *b = 0,
c => return Err(DecoderError::UnexpectedByteInRaster(c).into()),
}
break;
}
}
Ok(())
}
}
impl Sample for BWBit {
type Representation = u8;
fn from_bytes(bytes: &[u8], row_size: usize, output_buf: &mut [u8]) -> ImageResult<()> {
U8::from_bytes(bytes, row_size, output_buf)?;
if let Some(val) = output_buf.iter().find(|&val| *val > 1) {
return Err(DecoderError::SampleOutOfBounds(*val).into());
}
Ok(())
}
fn from_ascii(_reader: &mut dyn Read, _output_buf: &mut [u8]) -> ImageResult<()> {
unreachable!("BW bits from anymaps are never encoded as ASCII")
}
}
impl DecodableImageHeader for BitmapHeader {
fn tuple_type(&self) -> ImageResult<TupleType> {
Ok(TupleType::PbmBit)
}
}
impl DecodableImageHeader for GraymapHeader {
fn tuple_type(&self) -> ImageResult<TupleType> {
match self.maxwhite {
0 => Err(DecoderError::MaxvalZero.into()),
v if v <= 0xFF => Ok(TupleType::GrayU8),
v if v <= 0xFFFF => Ok(TupleType::GrayU16),
_ => Err(DecoderError::MaxvalTooBig(self.maxwhite).into()),
}
}
}
impl DecodableImageHeader for PixmapHeader {
fn tuple_type(&self) -> ImageResult<TupleType> {
match self.maxval {
0 => Err(DecoderError::MaxvalZero.into()),
v if v <= 0xFF => Ok(TupleType::RGBU8),
v if v <= 0xFFFF => Ok(TupleType::RGBU16),
_ => Err(DecoderError::MaxvalTooBig(self.maxval).into()),
}
}
}
impl DecodableImageHeader for ArbitraryHeader {
fn tuple_type(&self) -> ImageResult<TupleType> {
match self.tupltype {
_ if self.maxval == 0 => Err(DecoderError::MaxvalZero.into()),
None if self.depth == 1 => Ok(TupleType::GrayU8),
None if self.depth == 2 => Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Pnm.into(),
UnsupportedErrorKind::Color(ExtendedColorType::La8),
),
)),
None if self.depth == 3 => Ok(TupleType::RGBU8),
None if self.depth == 4 => Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Pnm.into(),
UnsupportedErrorKind::Color(ExtendedColorType::Rgba8),
),
)),
Some(ArbitraryTuplType::BlackAndWhite) if self.maxval == 1 && self.depth == 1 => {
Ok(TupleType::BWBit)
}
Some(ArbitraryTuplType::BlackAndWhite) => Err(DecoderError::InvalidDepthOrMaxval {
tuple_type: ArbitraryTuplType::BlackAndWhite,
maxval: self.maxval,
depth: self.depth,
}
.into()),
Some(ArbitraryTuplType::Grayscale) if self.depth == 1 && self.maxval <= 0xFF => {
Ok(TupleType::GrayU8)
}
Some(ArbitraryTuplType::Grayscale) if self.depth <= 1 && self.maxval <= 0xFFFF => {
Ok(TupleType::GrayU16)
}
Some(ArbitraryTuplType::Grayscale) => Err(DecoderError::InvalidDepthOrMaxval {
tuple_type: ArbitraryTuplType::Grayscale,
maxval: self.maxval,
depth: self.depth,
}
.into()),
Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFF => {
Ok(TupleType::RGBU8)
}
Some(ArbitraryTuplType::RGB) if self.depth == 3 && self.maxval <= 0xFFFF => {
Ok(TupleType::RGBU16)
}
Some(ArbitraryTuplType::RGB) => Err(DecoderError::InvalidDepth {
tuple_type: ArbitraryTuplType::RGB,
depth: self.depth,
}
.into()),
Some(ArbitraryTuplType::BlackAndWhiteAlpha) => Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Pnm.into(),
UnsupportedErrorKind::GenericFeature(format!(
"Color type {}",
ArbitraryTuplType::BlackAndWhiteAlpha.name()
)),
),
)),
Some(ArbitraryTuplType::GrayscaleAlpha) => Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Pnm.into(),
UnsupportedErrorKind::Color(ExtendedColorType::La8),
),
)),
Some(ArbitraryTuplType::RGBAlpha) => Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Pnm.into(),
UnsupportedErrorKind::Color(ExtendedColorType::Rgba8),
),
)),
Some(ArbitraryTuplType::Custom(ref custom)) => Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Pnm.into(),
UnsupportedErrorKind::GenericFeature(format!("Tuple type {:?}", custom)),
),
)),
None => Err(DecoderError::TupleTypeUnrecognised.into()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pam_blackandwhite() {
let pamdata = b"P7
WIDTH 4
HEIGHT 4
DEPTH 1
MAXVAL 1
TUPLTYPE BLACKANDWHITE
# Comment line
ENDHDR
\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\x01";
let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
assert_eq!(decoder.color_type(), ColorType::L8);
assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
assert_eq!(decoder.dimensions(), (4, 4));
assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
let mut image = vec![0; decoder.total_bytes() as usize];
decoder.read_image(&mut image).unwrap();
assert_eq!(
image,
vec![
0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00,
0x00, 0xFF
]
);
match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
(
_,
PnmHeader {
decoded:
HeaderRecord::Arbitrary(ArbitraryHeader {
width: 4,
height: 4,
maxval: 1,
depth: 1,
tupltype: Some(ArbitraryTuplType::BlackAndWhite),
}),
encoded: _,
},
) => (),
_ => panic!("Decoded header is incorrect"),
}
}
#[test]
fn pam_grayscale() {
let pamdata = b"P7
WIDTH 4
HEIGHT 4
DEPTH 1
MAXVAL 255
TUPLTYPE GRAYSCALE
# Comment line
ENDHDR
\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
assert_eq!(decoder.color_type(), ColorType::L8);
assert_eq!(decoder.dimensions(), (4, 4));
assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
let mut image = vec![0; decoder.total_bytes() as usize];
decoder.read_image(&mut image).unwrap();
assert_eq!(
image,
vec![
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad,
0xbe, 0xef
]
);
match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
(
_,
PnmHeader {
decoded:
HeaderRecord::Arbitrary(ArbitraryHeader {
width: 4,
height: 4,
depth: 1,
maxval: 255,
tupltype: Some(ArbitraryTuplType::Grayscale),
}),
encoded: _,
},
) => (),
_ => panic!("Decoded header is incorrect"),
}
}
#[test]
fn pam_rgb() {
let pamdata = b"P7
# Comment line
MAXVAL 255
TUPLTYPE RGB
DEPTH 3
WIDTH 2
HEIGHT 2
ENDHDR
\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
let decoder = PnmDecoder::new(&pamdata[..]).unwrap();
assert_eq!(decoder.color_type(), ColorType::Rgb8);
assert_eq!(decoder.dimensions(), (2, 2));
assert_eq!(decoder.subtype(), PnmSubtype::ArbitraryMap);
let mut image = vec![0; decoder.total_bytes() as usize];
decoder.read_image(&mut image).unwrap();
assert_eq!(
image,
vec![0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef]
);
match PnmDecoder::new(&pamdata[..]).unwrap().into_inner() {
(
_,
PnmHeader {
decoded:
HeaderRecord::Arbitrary(ArbitraryHeader {
maxval: 255,
tupltype: Some(ArbitraryTuplType::RGB),
depth: 3,
width: 2,
height: 2,
}),
encoded: _,
},
) => (),
_ => panic!("Decoded header is incorrect"),
}
}
#[test]
fn pbm_binary() {
let pbmbinary = [&b"P4 6 2\n"[..], &[0b01101100_u8, 0b10110111]].concat();
let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
assert_eq!(decoder.color_type(), ColorType::L8);
assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
assert_eq!(decoder.dimensions(), (6, 2));
assert_eq!(
decoder.subtype(),
PnmSubtype::Bitmap(SampleEncoding::Binary)
);
let mut image = vec![0; decoder.total_bytes() as usize];
decoder.read_image(&mut image).unwrap();
assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
(
_,
PnmHeader {
decoded:
HeaderRecord::Bitmap(BitmapHeader {
encoding: SampleEncoding::Binary,
width: 6,
height: 2,
}),
encoded: _,
},
) => (),
_ => panic!("Decoded header is incorrect"),
}
}
#[test]
fn pbm_binary_ascii_termination() {
use std::io::{BufReader, Cursor, Error, ErrorKind, Read, Result};
struct FailRead(Cursor<&'static [u8]>);
impl Read for FailRead {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
match self.0.read(buf) {
Ok(n) if n > 0 => Ok(n),
_ => Err(Error::new(
ErrorKind::BrokenPipe,
"Simulated broken pipe error",
)),
}
}
}
let pbmbinary = BufReader::new(FailRead(Cursor::new(b"P1 1 1\n")));
let decoder = PnmDecoder::new(pbmbinary).unwrap();
let mut image = vec![0; decoder.total_bytes() as usize];
decoder
.read_image(&mut image)
.expect_err("Image is malformed");
}
#[test]
fn pbm_ascii() {
let pbmbinary = b"P1 6 2\n 0 1 1 0 1 1\n1 0 1 1 0\t\n\x0b\x0c\r1";
let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
assert_eq!(decoder.color_type(), ColorType::L8);
assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
assert_eq!(decoder.dimensions(), (6, 2));
assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
let mut image = vec![0; decoder.total_bytes() as usize];
decoder.read_image(&mut image).unwrap();
assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
(
_,
PnmHeader {
decoded:
HeaderRecord::Bitmap(BitmapHeader {
encoding: SampleEncoding::Ascii,
width: 6,
height: 2,
}),
encoded: _,
},
) => (),
_ => panic!("Decoded header is incorrect"),
}
}
#[test]
fn pbm_ascii_nospace() {
let pbmbinary = b"P1 6 2\n011011101101";
let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
assert_eq!(decoder.color_type(), ColorType::L8);
assert_eq!(decoder.original_color_type(), ExtendedColorType::L1);
assert_eq!(decoder.dimensions(), (6, 2));
assert_eq!(decoder.subtype(), PnmSubtype::Bitmap(SampleEncoding::Ascii));
let mut image = vec![0; decoder.total_bytes() as usize];
decoder.read_image(&mut image).unwrap();
assert_eq!(image, vec![255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0]);
match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
(
_,
PnmHeader {
decoded:
HeaderRecord::Bitmap(BitmapHeader {
encoding: SampleEncoding::Ascii,
width: 6,
height: 2,
}),
encoded: _,
},
) => (),
_ => panic!("Decoded header is incorrect"),
}
}
#[test]
fn pgm_binary() {
let elements = (0..16).collect::<Vec<_>>();
let pbmbinary = [&b"P5 4 4 255\n"[..], &elements].concat();
let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
assert_eq!(decoder.color_type(), ColorType::L8);
assert_eq!(decoder.dimensions(), (4, 4));
assert_eq!(
decoder.subtype(),
PnmSubtype::Graymap(SampleEncoding::Binary)
);
let mut image = vec![0; decoder.total_bytes() as usize];
decoder.read_image(&mut image).unwrap();
assert_eq!(image, elements);
match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
(
_,
PnmHeader {
decoded:
HeaderRecord::Graymap(GraymapHeader {
encoding: SampleEncoding::Binary,
width: 4,
height: 4,
maxwhite: 255,
}),
encoded: _,
},
) => (),
_ => panic!("Decoded header is incorrect"),
}
}
#[test]
fn pgm_ascii() {
let pbmbinary = b"P2 4 4 255\n 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15";
let decoder = PnmDecoder::new(&pbmbinary[..]).unwrap();
assert_eq!(decoder.color_type(), ColorType::L8);
assert_eq!(decoder.dimensions(), (4, 4));
assert_eq!(
decoder.subtype(),
PnmSubtype::Graymap(SampleEncoding::Ascii)
);
let mut image = vec![0; decoder.total_bytes() as usize];
decoder.read_image(&mut image).unwrap();
assert_eq!(image, (0..16).collect::<Vec<_>>());
match PnmDecoder::new(&pbmbinary[..]).unwrap().into_inner() {
(
_,
PnmHeader {
decoded:
HeaderRecord::Graymap(GraymapHeader {
encoding: SampleEncoding::Ascii,
width: 4,
height: 4,
maxwhite: 255,
}),
encoded: _,
},
) => (),
_ => panic!("Decoded header is incorrect"),
}
}
#[test]
fn ppm_ascii() {
let ascii = b"P3 1 1 2000\n0 1000 2000";
let decoder = PnmDecoder::new(&ascii[..]).unwrap();
let mut image = vec![0; decoder.total_bytes() as usize];
decoder.read_image(&mut image).unwrap();
assert_eq!(
image,
[
0_u16.to_ne_bytes(),
(u16::MAX / 2 + 1).to_ne_bytes(),
u16::MAX.to_ne_bytes()
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
);
}
#[test]
fn dimension_overflow() {
let pamdata = b"P7
# Comment line
MAXVAL 255
TUPLTYPE RGB
DEPTH 3
WIDTH 4294967295
HEIGHT 4294967295
ENDHDR
\xde\xad\xbe\xef\xde\xad\xbe\xef\xde\xad\xbe\xef";
assert!(PnmDecoder::new(&pamdata[..]).is_err());
}
#[test]
fn issue_1508() {
let _ = crate::load_from_memory(b"P391919 16999 1 1 9 919 16999 1 9999 999* 99999 N");
}
#[test]
fn issue_1616_overflow() {
let data = [
80, 54, 10, 52, 50, 57, 52, 56, 50, 57, 52, 56, 35, 56, 10, 52, 10, 48, 10, 12, 12, 56,
];
let decoder = PnmDecoder::new(&data[..]).unwrap();
let mut image = vec![0; decoder.total_bytes() as usize];
let _ = decoder.read_image(&mut image);
}
}