#![warn(missing_docs)]
#![doc(html_logo_url="https://plopgrizzly.com/images/plopgrizzly-splash.png",
html_favicon_url="https://plopgrizzly.com/images/plopgrizzly-splash.png")]
extern crate afi;
extern crate byteorder;
use std::io::{ Write, Read, Seek, Cursor, SeekFrom };
use afi::*;
pub struct PnmEncoder {
wh: (u16,u16),
format: ColorChannels,
}
impl EncoderV for PnmEncoder {
fn new(video: &Video) -> PnmEncoder {
let wh = video.wh();
let format = video.format();
PnmEncoder {
wh, format
}
}
fn run(&mut self, frame: &VFrame) -> Vec<u8> {
let mut out = Vec::new();
out.write_fmt(format_args!("P6\n{}\n{}\n255\n", self.wh.0,
self.wh.1)).unwrap();
for i in 0..((self.wh.0 as usize) * (self.wh.1 as usize)) {
let [r, g, b, _] = frame.sample_rgba(self.format, i);
out.write_all(&[r, g, b]).unwrap();
}
out
}
fn end(self) -> Vec<u8> {
vec![]
}
}
pub struct PnmDecoder<T: Read + Seek> {
#[allow(unused)] data: T,
channels: ColorChannels,
wh: (u16, u16),
n_frames: u32,
}
impl<T> Decoder<T> for PnmDecoder<T> where T: Read + Seek {
fn new(mut data: T, channels: afi::ColorChannels)
-> Option<PnmDecoder<T>>
{
let mut header = [0u8; 2];
data.read(&mut header).unwrap();
match &header {
b"P6" => { }
_ => {
return None;
}
}
let mut optional_comment = [0u8; 1];
data.read(&mut optional_comment).unwrap();
match &optional_comment {
b"#" => { skip(&mut data); }
_ => { data.seek(SeekFrom::Current(-1)).unwrap(); }
}
let width = utf8_to_u16(&mut data)?;
let height = utf8_to_u16(&mut data)?;
let max = utf8_to_u16(&mut data)?;
let wh = (width, height);
if max != 255 {
println!("WARNING: PNM decoder requires 255 max");
return None;
}
let n_frames = 1;
Some(PnmDecoder { data, channels, wh, n_frames })
}
fn run(&mut self, audio: &mut Option<Audio>, video: &mut Option<Video>)
-> Option<bool>
{
if audio.is_none() && video.is_none() {
*video = Some(Video::new(
self.channels, self.wh, self.n_frames
));
Some(true)
} else {
let ch = self.channels.n_channels();
let size = self.wh.0 as usize * self.wh.1 as usize * ch;
let mut out: Vec<u8> = Vec::with_capacity(size);
for _ in 0..size {
let mut rgb = [0u8; 3];
self.data.read(&mut rgb).unwrap();
let rgba = self.channels.from(Rgba,
[rgb[0], rgb[1], rgb[2], 255u8]);
for i in 0..ch {
out.push(rgba[i]);
}
}
let video = video.as_mut().unwrap(); video.add(VFrame(out));
Some(false)
}
}
fn get(&self) -> Index {
Index(0)
}
fn set(&mut self, _index: Index) {
}
}
pub fn decode(file: &[u8], channels: ColorChannels) -> Option<Video> {
let mut image = None;
let mut decoder = PnmDecoder::new(Cursor::new(file), channels)?;
decoder.run(&mut None, &mut image).unwrap();
decoder.run(&mut None, &mut image).unwrap();
image
}
fn skip<T>(data: &mut T) where T: Read + Seek {
loop {
let mut character = [0u8; 1];
data.read(&mut character).unwrap();
match &character {
b"\n" | b" " | b"\t" => break,
_ => { }
}
}
}
fn utf8_to_u16<T>(data: &mut T) -> Option<u16> where T: Read + Seek {
let mut number = 0;
let zero = b'0';
loop {
let mut character = [0u8; 1];
data.read(&mut character).unwrap();
match &character {
b"\n" | b" " | b"\t" => break,
digit => {
let digit = digit[0];
number *= 10;
if digit != zero {
if digit < zero || digit > zero + 9 {
return None;
}
number += (digit - zero) as u16;
}
}
}
}
Some(number)
}