#![deny(missing_docs)]
extern crate byteorder;
extern crate flate2;
pub mod filter;
pub mod meta;
pub use options::{Compression, ColorFormat, Options};
mod compress;
mod options;
use byteorder::{ByteOrder, NetworkEndian, WriteBytesExt};
use compress::Compressor;
use filter::Filter;
use flate2::Crc;
use std::io::{self, Write};
pub fn encode<W: Write>(sink: W, width: u32, height: u32, image: &[u8])
-> io::Result<()>
{
let mut encoder = Options::smallest(width, height).build(sink)?;
encoder.write(&image)?;
encoder.finish()?;
Ok(())
}
pub struct Encoder<W, F> {
filter: F,
sink: W,
compress: Compressor,
stride: usize,
prior: Vec<u8>,
}
impl<W: Write, F> Encoder<W, F> {
pub fn new(opts: &Options, mut sink: W, filter: F) -> io::Result<Self> {
sink.write_all(&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])?;
let stride = opts.stride();
let mut this = Self {
compress: Compressor::new(opts.level, opts.buffer),
stride: stride,
prior: vec![0; stride],
filter: filter,
sink: sink,
};
let mut ihdr = [0; 13];
NetworkEndian::write_u32(&mut ihdr, opts.width);
NetworkEndian::write_u32(&mut ihdr[4..], opts.height);
ihdr[8] = opts.depth;
ihdr[9] = opts.format as u8;
ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0; this.chunk(b"IHDR", &ihdr)?;
Ok(this)
}
pub fn finish(self) -> io::Result<W> {
let Self { mut compress, mut sink, .. } = self;
compress::Writer::new(
&mut compress,
|bytes: &[u8]| Self::sinking_chunk(&mut sink, b"IDAT", bytes),
).finish()?;
Self::sinking_chunk(&mut sink, b"IEND", &[])?;
Ok(sink)
}
pub fn chunk(&mut self, kind: &[u8; 4], data: &[u8]) -> io::Result<()> {
Self::sinking_chunk(&mut self.sink, kind, data)
}
pub fn writer(&mut self) -> &mut W {
&mut self.sink
}
pub fn reset(&mut self) {
self.compress.reset();
for x in self.prior.iter_mut() { *x = 0; }
}
fn sinking_chunk(sink: &mut W, kind: &[u8; 4], data: &[u8])
-> io::Result<()>
{
let mut crc = Crc::new();
crc.update(kind);
crc.update(data);
sink.write_u32::<NetworkEndian>(data.len() as u32)?;
sink.write_all(kind)?;
sink.write_all(data)?;
sink.write_u32::<NetworkEndian>(crc.sum())?;
Ok(())
}
}
impl<W: Write, F: Filter> Encoder<W, F> {
pub fn write(&mut self, mut rows: &[u8]) -> io::Result<()> {
assert!(rows.len() % self.stride == 0);
let r = self.stride;
let Self {
ref mut sink,
ref mut filter,
ref mut compress,
ref mut prior,
..
} = *self;
if rows.len() == 0 {
return Ok(());
}
let mut writer = compress::Writer::new(
compress,
|bytes: &[u8]| Self::sinking_chunk(sink, b"IDAT", bytes),
);
filter.apply(&mut writer, &prior, &rows[..r])?;
prior.copy_from_slice(&rows[rows.len() - r..]);
while r < rows.len() {
filter.apply(&mut writer, &rows[..r], &rows[r..2*r])?;
rows = &rows[r..];
}
Ok(())
}
}