use std::{fmt, io};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SampleEncoding {
Binary,
Ascii,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PNMSubtype {
Bitmap(SampleEncoding),
Graymap(SampleEncoding),
Pixmap(SampleEncoding),
ArbitraryMap,
}
#[allow(dead_code)]
pub type PnmSubtype = PNMSubtype;
pub struct PnmHeader {
pub(crate) decoded: HeaderRecord,
pub(crate) encoded: Option<Vec<u8>>,
}
#[allow(dead_code)]
#[deprecated(note = "Use `PnmHeader` instead")]
pub type PNMHeader = PnmHeader;
pub(crate) enum HeaderRecord {
Bitmap(BitmapHeader),
Graymap(GraymapHeader),
Pixmap(PixmapHeader),
Arbitrary(ArbitraryHeader),
}
#[derive(Clone, Copy, Debug)]
pub struct BitmapHeader {
pub encoding: SampleEncoding,
pub height: u32,
pub width: u32,
}
#[derive(Clone, Copy, Debug)]
pub struct GraymapHeader {
pub encoding: SampleEncoding,
pub height: u32,
pub width: u32,
pub maxwhite: u32,
}
#[derive(Clone, Copy, Debug)]
pub struct PixmapHeader {
pub encoding: SampleEncoding,
pub height: u32,
pub width: u32,
pub maxval: u32,
}
#[derive(Clone, Debug)]
pub struct ArbitraryHeader {
pub height: u32,
pub width: u32,
pub depth: u32,
pub maxval: u32,
pub tupltype: Option<ArbitraryTuplType>,
}
#[derive(Clone, Debug)]
pub enum ArbitraryTuplType {
BlackAndWhite,
BlackAndWhiteAlpha,
Grayscale,
GrayscaleAlpha,
RGB,
RGBAlpha,
Custom(String),
}
impl ArbitraryTuplType {
pub(crate) fn name(&self) -> &str {
match self {
ArbitraryTuplType::BlackAndWhite => "BLACKANDWHITE",
ArbitraryTuplType::BlackAndWhiteAlpha => "BLACKANDWHITE_ALPHA",
ArbitraryTuplType::Grayscale => "GRAYSCALE",
ArbitraryTuplType::GrayscaleAlpha => "GRAYSCALE_ALPHA",
ArbitraryTuplType::RGB => "RGB",
ArbitraryTuplType::RGBAlpha => "RGB_ALPHA",
ArbitraryTuplType::Custom(custom) => custom,
}
}
}
impl PNMSubtype {
pub fn magic_constant(self) -> &'static [u8; 2] {
match self {
PNMSubtype::Bitmap(SampleEncoding::Ascii) => b"P1",
PNMSubtype::Graymap(SampleEncoding::Ascii) => b"P2",
PNMSubtype::Pixmap(SampleEncoding::Ascii) => b"P3",
PNMSubtype::Bitmap(SampleEncoding::Binary) => b"P4",
PNMSubtype::Graymap(SampleEncoding::Binary) => b"P5",
PNMSubtype::Pixmap(SampleEncoding::Binary) => b"P6",
PNMSubtype::ArbitraryMap => b"P7",
}
}
pub fn sample_encoding(self) -> SampleEncoding {
match self {
PNMSubtype::ArbitraryMap => SampleEncoding::Binary,
PNMSubtype::Bitmap(enc) => enc,
PNMSubtype::Graymap(enc) => enc,
PNMSubtype::Pixmap(enc) => enc,
}
}
}
impl PnmHeader {
pub fn subtype(&self) -> PNMSubtype {
match self.decoded {
HeaderRecord::Bitmap(BitmapHeader { encoding, .. }) => PNMSubtype::Bitmap(encoding),
HeaderRecord::Graymap(GraymapHeader { encoding, .. }) => PNMSubtype::Graymap(encoding),
HeaderRecord::Pixmap(PixmapHeader { encoding, .. }) => PNMSubtype::Pixmap(encoding),
HeaderRecord::Arbitrary(ArbitraryHeader { .. }) => PNMSubtype::ArbitraryMap,
}
}
pub fn width(&self) -> u32 {
match self.decoded {
HeaderRecord::Bitmap(BitmapHeader { width, .. }) => width,
HeaderRecord::Graymap(GraymapHeader { width, .. }) => width,
HeaderRecord::Pixmap(PixmapHeader { width, .. }) => width,
HeaderRecord::Arbitrary(ArbitraryHeader { width, .. }) => width,
}
}
pub fn height(&self) -> u32 {
match self.decoded {
HeaderRecord::Bitmap(BitmapHeader { height, .. }) => height,
HeaderRecord::Graymap(GraymapHeader { height, .. }) => height,
HeaderRecord::Pixmap(PixmapHeader { height, .. }) => height,
HeaderRecord::Arbitrary(ArbitraryHeader { height, .. }) => height,
}
}
pub fn maximal_sample(&self) -> u32 {
match self.decoded {
HeaderRecord::Bitmap(BitmapHeader { .. }) => 1,
HeaderRecord::Graymap(GraymapHeader { maxwhite, .. }) => maxwhite,
HeaderRecord::Pixmap(PixmapHeader { maxval, .. }) => maxval,
HeaderRecord::Arbitrary(ArbitraryHeader { maxval, .. }) => maxval,
}
}
pub fn as_bitmap(&self) -> Option<&BitmapHeader> {
match self.decoded {
HeaderRecord::Bitmap(ref bitmap) => Some(bitmap),
_ => None,
}
}
pub fn as_graymap(&self) -> Option<&GraymapHeader> {
match self.decoded {
HeaderRecord::Graymap(ref graymap) => Some(graymap),
_ => None,
}
}
pub fn as_pixmap(&self) -> Option<&PixmapHeader> {
match self.decoded {
HeaderRecord::Pixmap(ref pixmap) => Some(pixmap),
_ => None,
}
}
pub fn as_arbitrary(&self) -> Option<&ArbitraryHeader> {
match self.decoded {
HeaderRecord::Arbitrary(ref arbitrary) => Some(arbitrary),
_ => None,
}
}
pub fn write(&self, writer: &mut dyn io::Write) -> io::Result<()> {
writer.write_all(self.subtype().magic_constant())?;
match *self {
PnmHeader {
encoded: Some(ref content),
..
} => writer.write_all(content),
PnmHeader {
decoded:
HeaderRecord::Bitmap(BitmapHeader {
encoding: _encoding,
width,
height,
}),
..
} => writeln!(writer, "\n{} {}", width, height),
PnmHeader {
decoded:
HeaderRecord::Graymap(GraymapHeader {
encoding: _encoding,
width,
height,
maxwhite,
}),
..
} => writeln!(writer, "\n{} {} {}", width, height, maxwhite),
PnmHeader {
decoded:
HeaderRecord::Pixmap(PixmapHeader {
encoding: _encoding,
width,
height,
maxval,
}),
..
} => writeln!(writer, "\n{} {} {}", width, height, maxval),
PnmHeader {
decoded:
HeaderRecord::Arbitrary(ArbitraryHeader {
width,
height,
depth,
maxval,
ref tupltype,
}),
..
} => {
struct TupltypeWriter<'a>(&'a Option<ArbitraryTuplType>);
impl<'a> fmt::Display for TupltypeWriter<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Some(tt) => writeln!(f, "TUPLTYPE {}", tt.name()),
None => Ok(()),
}
}
}
writeln!(
writer,
"\nWIDTH {}\nHEIGHT {}\nDEPTH {}\nMAXVAL {}\n{}ENDHDR",
width, height, depth, maxval, TupltypeWriter(tupltype)
)
}
}
}
}
impl From<BitmapHeader> for PnmHeader {
fn from(header: BitmapHeader) -> Self {
PnmHeader {
decoded: HeaderRecord::Bitmap(header),
encoded: None,
}
}
}
impl From<GraymapHeader> for PnmHeader {
fn from(header: GraymapHeader) -> Self {
PnmHeader {
decoded: HeaderRecord::Graymap(header),
encoded: None,
}
}
}
impl From<PixmapHeader> for PnmHeader {
fn from(header: PixmapHeader) -> Self {
PnmHeader {
decoded: HeaderRecord::Pixmap(header),
encoded: None,
}
}
}
impl From<ArbitraryHeader> for PnmHeader {
fn from(header: ArbitraryHeader) -> Self {
PnmHeader {
decoded: HeaderRecord::Arbitrary(header),
encoded: None,
}
}
}