allow LZW to be done at Frame time rather than Encoder time
This commit is contained in:
parent
c4f91b5286
commit
312227220e
|
@ -4,7 +4,9 @@ extern crate color_quant;
|
|||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::io;
|
||||
|
||||
use crate::encoder::lzw_encode;
|
||||
/// Disposal method
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
|
@ -154,7 +156,9 @@ pub struct Frame<'a> {
|
|||
pub palette: Option<Vec<u8>>,
|
||||
/// Buffer containing the image data.
|
||||
/// Only indices unless configured differently.
|
||||
pub buffer: Cow<'a, [u8]>
|
||||
pub buffer: Cow<'a, [u8]>,
|
||||
/// Compressed at frame creation time such that at write-time we don't waste CPU
|
||||
pub precompressed: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl<'a> Default for Frame<'a> {
|
||||
|
@ -170,7 +174,8 @@ impl<'a> Default for Frame<'a> {
|
|||
height: 0,
|
||||
interlaced: false,
|
||||
palette: None,
|
||||
buffer: Cow::Borrowed(&[])
|
||||
buffer: Cow::Borrowed(&[]),
|
||||
precompressed: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -331,6 +336,15 @@ impl Frame<'static> {
|
|||
pub(crate) fn required_bytes(&self) -> usize {
|
||||
usize::from(self.width) * usize::from(self.height)
|
||||
}
|
||||
|
||||
/// For situations where we're writing the same Frame multiple times and don't want to burn CPU
|
||||
pub fn precompress(&mut self, temp: &mut Vec<u8>) -> io::Result<usize> {
|
||||
let mut compressed = Vec::new();
|
||||
lzw_encode(&self.buffer, &mut compressed, temp)?;
|
||||
let size = compressed.len();
|
||||
self.precompressed = Some(compressed);
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -214,35 +214,16 @@ impl<W: Write> Encoder<W> {
|
|||
writer.write_le(flags).map_err(Into::into)
|
||||
}
|
||||
}?;
|
||||
self.write_image_block(&frame.buffer)
|
||||
if let Some(compressed) = &frame.precompressed {
|
||||
self.w.as_mut().unwrap().write_all(compressed).map_err(Into::into)
|
||||
} else {
|
||||
self.write_image_block(&frame.buffer)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_image_block(&mut self, data: &[u8]) -> Result<(), EncodingError> {
|
||||
let writer = self.w.as_mut().unwrap();
|
||||
{
|
||||
let min_code_size: u8 = match flag_size(*data.iter().max().unwrap_or(&0) as usize + 1) + 1 {
|
||||
1 => 2, // As per gif spec: The minimal code size has to be >= 2
|
||||
n => n
|
||||
};
|
||||
writer.write_le(min_code_size)?;
|
||||
self.buffer.clear();
|
||||
let mut enc = LzwEncoder::new(BitOrder::Lsb, min_code_size);
|
||||
let len = enc.into_vec(&mut self.buffer).encode_all(data).consumed_out;
|
||||
|
||||
// Write blocks. `chunks_exact` seems to be slightly faster
|
||||
// than `chunks` according to both Rust docs and benchmark results.
|
||||
let mut iter = self.buffer[..len].chunks_exact(0xFF);
|
||||
while let Some(full_block) = iter.next() {
|
||||
writer.write_le(0xFFu8)?;
|
||||
writer.write_all(full_block)?;
|
||||
}
|
||||
let last_block = iter.remainder();
|
||||
if !last_block.is_empty() {
|
||||
writer.write_le(last_block.len() as u8)?;
|
||||
writer.write_all(last_block)?;
|
||||
}
|
||||
}
|
||||
writer.write_le(0u8).map_err(Into::into)
|
||||
lzw_encode(data, writer, &mut self.buffer).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn write_color_table(&mut self, table: &[u8]) -> Result<(), EncodingError> {
|
||||
|
@ -348,6 +329,32 @@ impl<W: Write> Encoder<W> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lzw_encode<W: Write>(data: &[u8], writer: &mut W, temp: &mut Vec<u8>) -> io::Result<()> {
|
||||
temp.clear();
|
||||
let min_code_size: u8 = match flag_size(*data.iter().max().unwrap_or(&0) as usize + 1) + 1 {
|
||||
1 => 2, // As per gif spec: The minimal code size has to be >= 2
|
||||
n => n
|
||||
};
|
||||
writer.write_le(min_code_size)?;
|
||||
let mut enc = LzwEncoder::new(BitOrder::Lsb, min_code_size);
|
||||
let len = enc.into_vec(temp).encode_all(data).consumed_out;
|
||||
|
||||
// Write blocks. `chunks_exact` seems to be slightly faster
|
||||
// than `chunks` according to both Rust docs and benchmark results.
|
||||
let mut iter = temp[..len].chunks_exact(0xFF);
|
||||
while let Some(full_block) = iter.next() {
|
||||
writer.write_le(0xFFu8)?;
|
||||
writer.write_all(full_block)?;
|
||||
}
|
||||
let last_block = iter.remainder();
|
||||
if !last_block.is_empty() {
|
||||
writer.write_le(last_block.len() as u8)?;
|
||||
writer.write_all(last_block)?;
|
||||
}
|
||||
writer.write_le(0u8)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// GIF encoder.
|
||||
pub struct Encoder<W: Write> {
|
||||
w: Option<W>,
|
||||
|
|
Loading…
Reference in New Issue