png_codec 0.1.1

A minimal pure Rust PNG encoder
Documentation
use crate::{
    chunk::{ColorType, ImageData, ImageEnd, ImageHeader},
    consts,
    encode::{filter, Error as EncoderError, Result},
    encoder::Enc,
    Rgba,
};

/// Frame Encoder for PNG files.
#[derive(Debug)]
pub(crate) struct StepEnc {
    pub(crate) encoder: Enc,
    // FIXME
    #[allow(dead_code)]
    coldepth: Option<(ColorType, u32)>,
    #[allow(dead_code)]
    header: Option<ImageHeader>,
}

impl StepEnc {
    pub(crate) fn new(encoder: Enc) -> Self {
        Self {
            encoder,
            coldepth: None,
            header: None,
        }
    }

    pub(crate) fn encode(
        &mut self,
        pixels: &[u8],
        width: u32,
        height: u32,
        palette: &[Rgba],
    ) -> Result<()> {
        let image_header = ImageHeader {
            width,
            height,
            color_type: ColorType::Palette,
            bit_depth: 8,
            interlace: false,
        };

        encode(&mut self.encoder, pixels, &image_header, palette)
    }
}

pub(super) fn encode(
    enc: &mut Enc,
    image: &[u8],
    header: &ImageHeader,
    palette: &[Rgba],
) -> Result<()> {
    enc.raw(&consts::PNG_SIGNATURE);

    // let transparency = Transparency::Palette(transparency.to_vec());

    if header.color_type == ColorType::Palette
        && (palette.is_empty() || palette.len() > 256)
    {
        eprintln!("{}", palette.len());
        return Err(EncoderError::BadPalette);
    }

    let data = pre_process_scanlines(image, header);

    header.write(enc);

    if header.color_type == ColorType::Palette {
        enc.prepare(palette.len() * 3, consts::PALETTE);
        for p in palette.iter() {
            enc.u8(p.r);
            enc.u8(p.g);
            enc.u8(p.b);
        }
        enc.write_crc();
    }
    if header.color_type == ColorType::Palette {
        enc.prepare(palette.len(), consts::TRANSPARENCY);
        for alpha in palette.iter() {
            enc.u8(alpha.a);
        }
        enc.write_crc();
    }
    ImageData::with_data(data).write(enc)?;
    ImageEnd.write(enc);
    Ok(())
}

// Out must be buffer big enough to contain uncompressed IDAT chunk data, and in
// must contain the full image.
fn pre_process_scanlines(inp: &[u8], header: &ImageHeader) -> Vec<u8> {
    let width = header.width;
    let height = header.height;
    let bit_depth = header.bit_depth;
    let color_type = header.color_type;
    let h = height as usize;
    let w = width as usize;
    let bpp = color_type.bpp(bit_depth);
    let bpp = bpp as usize;
    let outsize = h + (h * ((w * bpp + 7) / 8));
    let mut out = vec![0u8; outsize];
    filter::filter(&mut out, inp, w, h, header);
    out
}