image-encoder 0.1.0

simple image encoder for png encoding
Documentation
mod zlib;

use crc::{crc32, Hasher32};
use std::io;
use zlib::compress;

pub struct Image<'a> {
    pub width: u32,
    pub height: u32,
    data: &'a [u8],
}

impl<'a> Image<'a> {
    pub fn new(data: &'a [u8], width: u32, height: u32) -> Image<'a> {
        Image {
            data,
            width,
            height,
        }
    }

    pub fn encode_into<W: io::Write>(&self, buffer: &mut W) -> io::Result<()> {
        write_header(buffer)?;
        write_chunks(buffer, self.data, self.width, self.height)
    }
}

pub fn write_header<W: io::Write>(buffer: &mut W) -> io::Result<()> {
    buffer.write_all(b"\x89PNG\r\n\x1A\n")
}

pub fn write_chunks<W: io::Write>(
    buffer: &mut W,
    screen_data: &[u8],
    width: u32,
    height: u32,
) -> io::Result<()> {
    write_ihdr(buffer, width, height, 8u8, 6u8, 0u8, 0u8, 0u8)?;
    write_idat(buffer, screen_data, width, height)?;
    write_iend(buffer)?;

    Ok(())
}

fn write_ihdr<W: io::Write>(
    buffer: &mut W,
    width: u32,
    height: u32,
    bit_depth: u8,
    color_type: u8,
    compression_method: u8,
    filter_method: u8,
    interlace_method: u8,
) -> io::Result<()> {
    let wb = width.to_be_bytes();
    let hb = height.to_be_bytes();
    let mut data = [
        wb[0],
        wb[1],
        wb[2],
        wb[3],
        hb[0],
        hb[1],
        hb[2],
        hb[3],
        bit_depth,
        color_type,
        compression_method,
        filter_method,
        interlace_method,
    ];
    write_chunk(buffer, b"IHDR", &mut data)
}

fn write_idat<W: io::Write>(
    buffer: &mut W,
    screen_data: &[u8],
    width: u32,
    height: u32,
) -> io::Result<()> {
    let mut filtered = vec![];
    for y in 0..height {
        let start = (y * width * 4) as usize;
        let end = start + (width as usize) * 4;
        filtered.push(0);
        let data = &screen_data[start..end];
        filtered.extend_from_slice(data);
    }
    let compressed = compress(&filtered);

    write_chunk(buffer, b"IDAT", &compressed)
}

fn write_iend<W: io::Write>(buffer: &mut W) -> io::Result<()> {
    write_chunk(buffer, b"IEND", &[])
}

fn write_chunk<W: io::Write>(buffer: &mut W, kind: &[u8; 4], data: &[u8]) -> io::Result<()> {
    buffer.write_all(&(data.len() as u32).to_be_bytes())?;
    buffer.write_all(kind)?;
    buffer.write_all(data)?;

    let mut digest = crc32::Digest::new(crc32::IEEE);
    digest.write(kind);
    digest.write(data);
    buffer.write_all(&digest.sum32().to_be_bytes())?;

    Ok(())
}