webp-rust 0.2.1

Pure Rust implementation of a WebP encoder and decoder
Documentation
//! Boolean arithmetic writer used by the lossy `VP8` encoder.

use bin_rs::io::write_byte;

#[derive(Debug, Clone)]
pub(crate) struct Vp8BoolWriter {
    range: i32,
    value: i32,
    run: usize,
    nb_bits: i32,
    bytes: Vec<u8>,
}

impl Vp8BoolWriter {
    /// Creates a new boolean arithmetic writer with a reserved output size.
    pub(crate) fn new(expected_size: usize) -> Self {
        Self {
            range: 255 - 1,
            value: 0,
            run: 0,
            nb_bits: -8,
            bytes: Vec::with_capacity(expected_size),
        }
    }

    /// Flushes completed bytes from the arithmetic coder state.
    fn flush(&mut self) {
        let shift = 8 + self.nb_bits;
        let bits = self.value >> shift;
        self.value -= bits << shift;
        self.nb_bits -= 8;
        if (bits & 0xff) != 0xff {
            let pos = self.bytes.len();
            if (bits & 0x100) != 0 && pos > 0 {
                self.bytes[pos - 1] = self.bytes[pos - 1].wrapping_add(1);
            }
            if self.run > 0 {
                let value = if (bits & 0x100) != 0 { 0x00 } else { 0xff };
                for _ in 0..self.run {
                    write_byte(value, &mut self.bytes);
                }
                self.run = 0;
            }
            write_byte((bits & 0xff) as u8, &mut self.bytes);
        } else {
            self.run += 1;
        }
    }

    /// Encodes one probability-weighted bit.
    pub(crate) fn put_bit(&mut self, bit: bool, prob: u8) -> bool {
        let split = (self.range * prob as i32) >> 8;
        if bit {
            self.value += split + 1;
            self.range -= split + 1;
        } else {
            self.range = split;
        }
        if self.range < 127 {
            let shift = 7 - ((self.range + 1) as u32).ilog2() as i32;
            self.range = ((self.range + 1) << shift) - 1;
            self.value <<= shift;
            self.nb_bits += shift;
            if self.nb_bits > 0 {
                self.flush();
            }
        }
        bit
    }

    /// Encodes one unbiased bit.
    pub(crate) fn put_bit_uniform(&mut self, bit: bool) -> bool {
        let split = self.range >> 1;
        if bit {
            self.value += split + 1;
            self.range -= split + 1;
        } else {
            self.range = split;
        }
        if self.range < 127 {
            self.range = ((self.range + 1) << 1) - 1;
            self.value <<= 1;
            self.nb_bits += 1;
            if self.nb_bits > 0 {
                self.flush();
            }
        }
        bit
    }

    /// Encodes a fixed-width unsigned value in MSB-first order.
    pub(crate) fn put_bits(&mut self, value: u32, num_bits: usize) {
        for shift in (0..num_bits).rev() {
            self.put_bit_uniform(((value >> shift) & 1) != 0);
        }
    }

    /// Encodes a signed magnitude value using the VP8 boolean coding layout.
    pub(crate) fn put_signed_bits(&mut self, value: i32, num_bits: usize) {
        if !self.put_bit_uniform(value != 0) {
            return;
        }
        if value < 0 {
            self.put_bits(((-value as u32) << 1) | 1, num_bits + 1);
        } else {
            self.put_bits((value as u32) << 1, num_bits + 1);
        }
    }

    /// Flushes the remaining coder state and returns the final byte stream.
    pub(crate) fn finish(mut self) -> Vec<u8> {
        self.put_bits(0, (9 - self.nb_bits) as usize);
        self.nb_bits = 0;
        self.flush();
        self.bytes
    }
}

#[cfg(test)]
mod tests {
    use super::Vp8BoolWriter;
    use crate::decoder::vp8::Vp8BoolDecoder;

    #[test]
    /// Verifies that the encoder round-trips against the decoder.
    fn round_trips_boolean_stream() {
        let mut writer = Vp8BoolWriter::new(128);
        let mut expected = Vec::new();
        for i in 0..512usize {
            let prob = ((i * 73) % 255 + 1) as u8;
            let bit = ((i * 37 + 11) & 3) != 0;
            writer.put_bit(bit, prob);
            expected.push((bit, prob));
        }
        let bytes = writer.finish();

        let mut reader = Vp8BoolDecoder::new(&bytes);
        for (bit, prob) in expected {
            assert_eq!(reader.get_bit(prob) == 1, bit);
        }
    }
}