png_codec 0.1.1

A minimal pure Rust PNG encoder
Documentation
use crate::{
    consts,
    encode::{FilterStrategy, StepEnc},
};

/// Chunk encoder.
#[derive(Debug)]
pub(crate) struct Enc {
    /// Encoder
    pub(crate) encode: Encoder,
    /// CRC32
    pub(crate) chksum: u32,
}

impl Enc {
    /// Prepare a chunk for writing (reset checksum).
    pub(crate) fn prepare(&mut self, len: usize, name: [u8; 4]) {
        assert!(len <= consts::MAX_CHUNK_SIZE);
        let len: u32 = len.try_into().unwrap();
        self.encode.writer.extend_from_slice(&len.to_be_bytes());
        self.chksum = consts::CRC32_INIT;
        for c in name.iter().cloned() {
            self.u8(c);
        }
    }

    /// Write a u8
    pub(crate) fn u8(&mut self, value: u8) {
        self.encode.writer.push(value);
        let index: usize = (self.chksum as u8 ^ value).into();
        self.chksum = consts::CRC32_LOOKUP[index] ^ (self.chksum >> 8);
    }

    // /// Write a u16
    // pub(crate) fn u16(&mut self, value: u16) {
    //     let bytes = value.to_be_bytes();
    //     for byte in bytes.iter().cloned() {
    //         self.u8(byte);
    //     }
    // }

    /// Write a u32
    pub(crate) fn u32(&mut self, value: u32) {
        let bytes = value.to_be_bytes();
        for byte in bytes.iter().cloned() {
            self.u8(byte)
        }
    }

    // /// Write a string
    // pub(crate) fn string(&mut self, value: &str) -> Result<()> {
    //     for byte in value.bytes() {
    //         self.u8(byte)?;
    //     }
    //     Ok(())
    // }

    // /// Write a null-terminated string
    // pub(crate) fn str(&mut self, value: &str) -> Result<()> {
    //     self.string(value)?;
    //     self.u8(0)
    // }

    /// Write raw data
    pub(crate) fn raw(&mut self, raw: &[u8]) {
        for byte in raw.iter() {
            self.u8(*byte);
        }
    }

    /// Calculate and write Chunk CRC, ending the chunk.
    pub(crate) fn write_crc(&mut self) {
        let crc = self.chksum ^ consts::CRC32_INIT;
        self.encode.writer.extend_from_slice(&crc.to_be_bytes());
    }

    // /// Get the chosen filter strategy
    // pub(crate) fn filter_strategy(&self) -> Option<FilterStrategy> {
    //     self.encode.filter_strategy
    // }

    /// Get the compression level.
    pub(crate) fn level(&self) -> u8 {
        self.encode.level
    }
}

/// PNG file encoder
///
/// Can be converted into one of two encoders:
/// - [into_step_enc] for high-level [Step]s
/// - [into_chunk_enc] for low-level [Chunk]s
///
/// [into_iter]: struct.Decoder.html#method.into_iter
/// [into_step_enc]: struct.Decoder.html#method.into_step_enc
/// [into_chunk_enc]: struct.Decoder.html#method.into_chunk_enc
/// [Step]: struct.Step.html
/// [Chunk]: struct.Chunk.html
#[derive(Debug)]
pub struct Encoder {
    filter_strategy: Option<FilterStrategy>,
    level: u8,
    pub writer: Vec<u8>,
}

impl Encoder {
    /// Create a new PNG encoder.
    pub fn new() -> Self {
        Encoder {
            writer: Vec::default(),
            filter_strategy: None,
            level: 6,
        }
    }

    /// Set a specific filter strategy.  If this is never called, than png_pong
    /// attempts to choose the best (compromise speed / compression) filter
    /// strategy.
    pub fn filter_strategy(mut self, strategy: FilterStrategy) -> Self {
        self.filter_strategy = Some(strategy);
        self
    }

    /// Set the compression level (default: 6).  Must be between 0 and 10.
    pub fn compression_level(mut self, level: u8) -> Self {
        assert!(level <= 10);
        self.level = level;
        self
    }

    /// Convert into a step encoder.
    pub(crate) fn into_step_enc(self) -> StepEnc {
        StepEnc::new(self.into_enc())
    }

    fn into_enc(self) -> Enc {
        Enc {
            encode: self,
            chksum: 0,
        }
    }
}