use std::convert::TryFrom;
use std::error;
use std::io::{self, BufRead, BufReader, Cursor, Read};
use std::str::{self, FromStr};
use std::fmt::{self, Display};
use std::marker::PhantomData;
use std::mem;
use std::num::ParseIntError;
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),
MaxvalTooBig(u32),
InvalidDepthOrMaxval {
tuple_type: ArbitraryTuplType,
depth: u32,
maxval: u32,
},
InvalidDepth {
tuple_type: ArbitraryTuplType,
depth: u32,
},
TupleTypeUnrecognised,
}
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::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"),
}
}
}
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 {
fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize>;
fn from_bytes(bytes: &[u8], width: u32, height: u32, samples: u32)
-> ImageResult<Vec<u8>>;
fn from_ascii(reader: &mut dyn Read, width: u32, height: u32, samples: u32)
-> ImageResult<Vec<u8>>;
}
struct U8;
struct U16;
struct PbmBit;
struct BWBit;
trait DecodableImageHeader {
fn tuple_type(&self) -> ImageResult<TupleType>;
}
pub struct PnmDecoder<R> {
reader: BufReader<R>,
header: PnmHeader,
tuple: TupleType,
}
impl<R: Read> PnmDecoder<R> {
pub fn new(read: R) -> ImageResult<PnmDecoder<R>> {
let mut buf = BufReader::new(read);
let magic = buf.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()),
};
match subtype {
PNMSubtype::Bitmap(enc) => PnmDecoder::read_bitmap_header(buf, enc),
PNMSubtype::Graymap(enc) => PnmDecoder::read_graymap_header(buf, enc),
PNMSubtype::Pixmap(enc) => PnmDecoder::read_pixmap_header(buf, enc),
PNMSubtype::ArbitraryMap => PnmDecoder::read_arbitrary_header(buf),
}
}
pub fn into_inner(self) -> (R, PnmHeader) {
(self.reader.into_inner(), self.header)
}
fn read_bitmap_header(
mut reader: BufReader<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: BufReader<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: BufReader<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: BufReader<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(|ref 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_line(&mut self) -> ImageResult<String> {
let mut buffer = String::new();
self.read_line(&mut buffer)?;
Ok(buffer)
}
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_else(|| 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: Read> HeaderReader for BufReader<R> {}
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()));
buf.copy_from_slice(&match self.tuple {
TupleType::PbmBit => self.read_samples::<PbmBit>(1),
TupleType::BWBit => self.read_samples::<BWBit>(1),
TupleType::RGBU8 => self.read_samples::<U8>(3),
TupleType::RGBU16 => self.read_samples::<U16>(3),
TupleType::GrayU8 => self.read_samples::<U8>(1),
TupleType::GrayU16 => self.read_samples::<U16>(1),
}?);
Ok(())
}
}
impl<R: Read> PnmDecoder<R> {
fn read_samples<S: Sample>(&mut self, components: u32) -> ImageResult<Vec<u8>> {
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 samples = S::from_bytes(&bytes, width, height, components)?;
Ok(samples)
}
SampleEncoding::Ascii => {
let samples = self.read_ascii::<S>(components)?;
Ok(samples)
}
}
}
fn read_ascii<Basic: Sample>(&mut self, components: u32) -> ImageResult<Vec<u8>> {
Basic::from_ascii(&mut self.reader, self.header.width(), self.header.height(), components)
}
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| match *v {
b'\t' | b'\n' | b'\x0b' | b'\x0c' | b'\r' | b' ' => true,
_ => false,
};
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 {
fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
Ok((width * height * samples) as usize)
}
fn from_bytes(
bytes: &[u8],
width: u32,
height: u32,
samples: u32,
) -> ImageResult<Vec<u8>> {
assert_eq!(bytes.len(), Self::bytelen(width, height, samples).unwrap());
Ok(bytes.to_vec())
}
fn from_ascii(
reader: &mut dyn Read,
width: u32,
height: u32,
samples: u32,
) -> ImageResult<Vec<u8>> {
(0..width*height*samples)
.map(|_| read_separated_ascii(reader))
.collect()
}
}
impl Sample for U16 {
fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
Ok((width * height * samples * 2) as usize)
}
fn from_bytes(
bytes: &[u8],
width: u32,
height: u32,
samples: u32,
) -> ImageResult<Vec<u8>> {
assert_eq!(bytes.len(), Self::bytelen(width, height, samples).unwrap());
let mut buffer = bytes.to_vec();
for chunk in buffer.chunks_mut(2) {
let v = BigEndian::read_u16(chunk);
NativeEndian::write_u16(chunk, v);
}
Ok(buffer)
}
fn from_ascii(
reader: &mut dyn Read,
width: u32,
height: u32,
samples: u32,
) -> ImageResult<Vec<u8>> {
let mut buffer = vec![0; (width * height * samples * 2) as usize];
for i in 0..(width*height*samples) as usize {
let v = read_separated_ascii::<u16>(reader)?;
NativeEndian::write_u16(&mut buffer[2*i..][..2], v);
}
Ok(buffer)
}
}
impl Sample for PbmBit {
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],
width: u32,
height: u32,
samples: u32,
) -> ImageResult<Vec<u8>> {
assert_eq!(bytes.len(), Self::bytelen(width, height, samples).unwrap());
let mut expanded = utils::expand_bits(1, width * samples, bytes);
for b in expanded.iter_mut() {
*b = !*b;
}
Ok(expanded)
}
fn from_ascii(
reader: &mut dyn Read,
width: u32,
height: u32,
samples: u32,
) -> ImageResult<Vec<u8>> {
let count = (width*height*samples) as usize;
let raw_samples = reader.bytes()
.filter_map(|ascii| match ascii {
Ok(b'0') => Some(Ok(255)),
Ok(b'1') => Some(Ok(0)),
Err(err) => Some(Err(ImageError::IoError(err))),
Ok(b'\t')
| Ok(b'\n')
| Ok(b'\x0b')
| Ok(b'\x0c')
| Ok(b'\r')
| Ok(b' ') => None,
Ok(c) => Some(Err(DecoderError::UnexpectedByteInRaster(c).into())),
})
.take(count)
.collect::<ImageResult<Vec<u8>>>()?;
if raw_samples.len() < count {
return Err(DecoderError::InputTooShort.into());
}
Ok(raw_samples)
}
}
impl Sample for BWBit {
fn bytelen(width: u32, height: u32, samples: u32) -> ImageResult<usize> {
U8::bytelen(width, height, samples)
}
fn from_bytes(
bytes: &[u8],
width: u32,
height: u32,
samples: u32,
) -> ImageResult<Vec<u8>> {
assert_eq!(bytes.len(), Self::bytelen(width, height, samples).unwrap());
let values = U8::from_bytes(bytes, width, height, samples)?;
if let Some(val) = values.iter().find(|&val| *val > 1) {
return Err(DecoderError::SampleOutOfBounds(*val).into());
}
Ok(values)
}
fn from_ascii(
_reader: &mut dyn Read,
_width: u32,
_height: u32,
_samples: u32,
) -> ImageResult<Vec<u8>> {
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 {
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 {
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 {
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![0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00,
0x00, 0x01]
);
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 as 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::{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 = 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"),
}
}
}