pub type Input<'a> = Image<&'a [u8], 3>;
pub type Uninit = fimg::uninit::Image<u8, 3>;
use std::num::NonZeroU32;
use crate::decode::{read_til, Error, Read, Result};
use crate::encode::{encodeu32, P};
use atools::Join;
use fimg::{DynImage, Image};
pub const MAGIC: u8 = 7;
pub fn encode(x: impl PAM) -> Vec<u8> {
x.encode()
}
#[doc(hidden)]
pub trait PAM {
fn encode(self) -> Vec<u8>;
#[doc = include_str!("encode_into.md")]
unsafe fn encode_into(x: Self, out: *mut u8) -> usize;
}
pub trait PAMBit {
fn encode_bitmap(self) -> Vec<u8>;
#[doc = include_str!("encode_into.md")]
unsafe fn encode_into(x: Self, out: *mut u8) -> usize;
}
impl<T: AsRef<[u8]>> PAM for Image<T, 1> {
fn encode(self) -> Vec<u8> {
let mut y = Vec::with_capacity(size(self.bytes()));
let n = unsafe { PAM::encode_into(self.as_ref(), y.as_mut_ptr()) };
unsafe { y.set_len(n) };
y
}
unsafe fn encode_into(x: Self, out: *mut u8) -> usize {
encode_into((x.bytes(), (x.width(), x.height())), out, b"GRAYSCALE", 1)
}
}
impl<T: AsRef<[bool]>> PAMBit for Image<T, 1> {
fn encode_bitmap(self) -> Vec<u8> {
let mut y = Vec::with_capacity(size(self.as_ref().buffer()));
let n = unsafe { PAMBit::encode_into(self.as_ref(), y.as_mut_ptr()) };
unsafe { y.set_len(n) };
y
}
unsafe fn encode_into(x: Self, out: *mut u8) -> usize {
let b = x.buffer().as_ref();
let b = std::slice::from_raw_parts(b.as_ptr() as *mut u8, b.len());
encode_into((b, (x.width(), x.height())), out, b"BLACKANDWHITE", 1)
}
}
impl<T: AsRef<[u8]>> PAM for Image<T, 2> {
fn encode(self) -> Vec<u8> {
let mut y = Vec::with_capacity(size(self.bytes()));
let n = unsafe { PAM::encode_into(self.as_ref(), y.as_mut_ptr()) };
unsafe { y.set_len(n) };
y
}
unsafe fn encode_into(x: Self, out: *mut u8) -> usize {
encode_into(
(x.bytes(), (x.width(), x.height())),
out,
b"GRAYSCALE_ALPHA",
2,
)
}
}
impl<T: AsRef<[u8]>> PAM for Image<T, 3> {
fn encode(self) -> Vec<u8> {
let mut y = Vec::with_capacity(size(self.bytes()));
let n = unsafe { PAM::encode_into(self.as_ref(), y.as_mut_ptr()) };
unsafe { y.set_len(n) };
y
}
unsafe fn encode_into(x: Self, out: *mut u8) -> usize {
encode_into((x.bytes(), (x.width(), x.height())), out, b"RGB", 3)
}
}
impl<T: AsRef<[u8]>> PAM for Image<T, 4> {
fn encode(self) -> Vec<u8> {
let mut y = Vec::with_capacity(size(self.bytes()));
let n = unsafe { PAM::encode_into(self.as_ref(), y.as_mut_ptr()) };
unsafe { y.set_len(n) };
y
}
unsafe fn encode_into(x: Self, out: *mut u8) -> usize {
encode_into((x.bytes(), (x.width(), x.height())), out, b"RGB_ALPHA", 2)
}
}
impl<T: AsRef<[u8]>> PAM for DynImage<T> {
fn encode(self) -> Vec<u8> {
super::e!(self, |x| encode(x))
}
unsafe fn encode_into(x: Self, out: *mut u8) -> usize {
super::e!(x, |x| PAM::encode_into(x, out))
}
}
#[inline]
unsafe fn encode_into<const N: usize>(
(buf, (w, h)): (&[u8], (u32, u32)),
out: *mut u8,
tupltype: &[u8; N],
depth: u8,
) -> usize {
let mut o = out;
o.put(b'P'.join(MAGIC + b'0'));
o.put(*b"\nWIDTH ");
encodeu32(w, &mut o);
o.put(*b"\nHEIGHT ");
encodeu32(h, &mut o);
o.put(*b"\nDEPTH ");
o.push(depth + b'0');
o.put(*b"\nMAXVAL 255\n");
o.put(*b"TUPLTYPE ");
o.put(*tupltype);
o.put(*b"\nENDHDR\n");
if tupltype[..] == *b"BLACKANDWHITE" {
for &x in buf {
o.push(x ^ 1)
}
o.sub_ptr(out)
} else {
o.copy_from(buf.as_ptr(), buf.len());
o.sub_ptr(out) + buf.len()
}
}
#[derive(Copy, Clone, Debug)]
pub struct PAMHeader {
pub width: NonZeroU32,
pub height: NonZeroU32,
pub depth: u8,
pub max: u8,
pub tupltype: Type,
}
#[derive(Copy, Clone, Debug)]
pub enum Type {
Bit,
Y,
RGB,
BitA,
YA,
RGBA,
}
impl Type {
const fn bytes(self) -> u8 {
use Type::*;
match self {
Bit | Y => 1,
BitA | YA => 2,
RGB => 3,
RGBA => 4,
}
}
}
pub fn decode(mut x: &[u8]) -> Result<DynImage<Vec<u8>>> {
crate::decode::magic(&mut x);
decode_wo_magic(x)
}
pub fn decode_wo_magic(mut x: &[u8]) -> Result<DynImage<Vec<u8>>> {
let header = decode_pam_header(&mut x)?;
let mut alloc = Vec::with_capacity(
header.tupltype.bytes() as usize
* header.width.get() as usize
* header.height.get() as usize,
);
let n = unsafe { decode_inner(x, alloc.as_mut_ptr(), header)? };
unsafe { alloc.set_len(n) };
Ok(match header.tupltype {
Type::Bit => unsafe { DynImage::Y(Image::new(header.width, header.height, alloc)) },
Type::Y => unsafe { DynImage::Y(Image::new(header.width, header.height, alloc)) },
Type::BitA => unsafe { DynImage::Ya(Image::new(header.width, header.height, alloc)) },
Type::YA => unsafe { DynImage::Ya(Image::new(header.width, header.height, alloc)) },
Type::RGB => unsafe { DynImage::Rgb(Image::new(header.width, header.height, alloc)) },
Type::RGBA => unsafe { DynImage::Rgba(Image::new(header.width, header.height, alloc)) },
})
}
pub unsafe fn decode_inner(x: &[u8], mut into: *mut u8, header: PAMHeader) -> Result<usize> {
let n = header.tupltype.bytes() as usize
* header.width.get() as usize
* header.height.get() as usize;
match header.tupltype {
Type::Bit => x
.iter()
.map(|&x| x * 0xff)
.take(n)
.for_each(|x| into.push(x)),
Type::BitA => x
.iter()
.array_chunks::<2>()
.take(n)
.map(|[&x, &a]| [x * 0xff, a])
.for_each(|x| into.put(x)),
Type::Y | Type::YA | Type::RGB | Type::RGBA => {
into.copy_from(x.as_ptr(), n);
}
}
Ok(n)
}
pub fn decode_pam_header(x: &mut &[u8]) -> Result<PAMHeader> {
macro_rules! test {
($for:literal else $e:ident) => {
if x.rd().ok_or(Error::$e)? != *$for {
return Err(Error::$e);
};
};
}
test![b"WIDTH " else MissingWidth];
let width = NonZeroU32::new(read_til(x)?).ok_or(Error::ZeroWidth)?;
test![b"HEIGHT " else MissingHeight];
let height = NonZeroU32::new(read_til(x)?).ok_or(Error::ZeroHeight)?;
width.checked_mul(height).ok_or(Error::TooLarge)?;
test![b"DEPTH " else MissingDepth];
let depth = read_til::<u8>(x)?;
test![b"MAXVAL " else MissingMax];
let max = read_til::<u8>(x)?;
test![b"TUPLTYPE " else MissingTupltype];
let end = x
.iter()
.position(|&x| x == b'\n')
.ok_or(Error::MissingTupltype)?;
let tupltype = match &x[..end] {
b"BLACKANDWHITE" => Type::Bit,
b"BLACKANDWHITE_ALPHA" => Type::BitA,
b"GRAYSCALE" => Type::Y,
b"GRAYSCALE_ALPHA" => Type::YA,
b"RGB" => Type::RGB,
b"RGB_ALPHA" => Type::RGBA,
_ => return Err(Error::MissingTupltype),
};
*x = &x[end..];
test![b"\nENDHDR\n" else MissingData];
Ok(PAMHeader {
width,
height,
depth,
max,
tupltype,
})
}
#[doc = include_str!("est.md")]
pub const fn size<T>(x: &[T]) -> usize {
92 + x.len()
}
#[test]
fn test_bit() {
assert_eq!(
PAMBit::encode_bitmap(
Image::<_, 1>::build(20, 15).buf(
include_bytes!("../tdata/fimg.imgbuf")
.iter()
.map(|&x| x <= 128)
.collect::<Vec<_>>(),
),
),
include_bytes!("../tdata/fimg.pam")
);
assert_eq!(
&**decode(include_bytes!("../tdata/fimg.pam"))
.unwrap()
.buffer(),
include_bytes!("../tdata/fimg.imgbuf")
);
}
#[test]
fn test_y() {
assert_eq!(
PAM::encode(
Image::<_, 1>::build(20, 15).buf(&include_bytes!("../tdata/fimg-gray.imgbuf")[..])
),
include_bytes!("../tdata/fimg-gray.pam")
);
assert_eq!(
&**decode(include_bytes!("../tdata/fimg-gray.pam"))
.unwrap()
.buffer(),
include_bytes!("../tdata/fimg-gray.imgbuf")
);
}
#[test]
fn test_ya() {
assert_eq!(
PAM::encode(
Image::<_, 2>::build(20, 15)
.buf(&include_bytes!("../tdata/fimg-transparent.imgbuf")[..])
),
include_bytes!("../tdata/fimg-transparent.pam")
);
assert_eq!(
&**decode(include_bytes!("../tdata/fimg-transparent.pam"))
.unwrap()
.buffer(),
include_bytes!("../tdata/fimg-transparent.imgbuf")
);
}
#[test]
fn test_rgb() {
assert_eq!(
PAM::encode(
Image::<_, 3>::build(20, 15).buf(&include_bytes!("../tdata/fimg-rainbow.imgbuf")[..])
),
include_bytes!("../tdata/fimg-rainbow.pam")
);
assert_eq!(
&**decode(include_bytes!("../tdata/fimg-rainbow.pam"))
.unwrap()
.buffer(),
include_bytes!("../tdata/fimg-rainbow.imgbuf")
);
}
#[test]
fn test_rgba() {
assert_eq!(
PAM::encode(
Image::<_, 4>::build(20, 15)
.buf(&include_bytes!("../tdata/fimg-rainbow-transparent.imgbuf")[..])
),
include_bytes!("../tdata/fimg-rainbow-transparent.pam")
);
assert_eq!(
&**decode(include_bytes!("../tdata/fimg-rainbow-transparent.pam"))
.unwrap()
.buffer(),
include_bytes!("../tdata/fimg-rainbow-transparent.imgbuf")
);
}