diff options
-rw-r--r-- | gifed/src/block/imagedescriptor.rs | 19 | ||||
-rw-r--r-- | gifed/src/block/indexedimage.rs | 71 | ||||
-rw-r--r-- | gifed/src/block/mod.rs | 16 | ||||
-rw-r--r-- | gifed/src/gif.rs | 11 | ||||
-rw-r--r-- | gifed/src/reader/mod.rs | 62 | ||||
-rw-r--r-- | gifed/src/writer/gifbuilder.rs | 4 | ||||
-rw-r--r-- | gifed/src/writer/mod.rs | 17 |
7 files changed, 113 insertions, 87 deletions
diff --git a/gifed/src/block/imagedescriptor.rs b/gifed/src/block/imagedescriptor.rs index 6bc0792..ce20240 100644 --- a/gifed/src/block/imagedescriptor.rs +++ b/gifed/src/block/imagedescriptor.rs @@ -2,6 +2,7 @@ use std::convert::TryInto; use super::{packed::ImagePacked, Palette}; +#[derive(Clone, Debug)] pub struct ImageDescriptor { pub left: u16, pub top: u16, @@ -32,20 +33,18 @@ impl ImageDescriptor { pub fn color_table_size(&self) -> usize { crate::packed_to_color_table_length(self.packed.color_table_size()) } -} -impl From<&ImageDescriptor> for Box<[u8]> { - fn from(desc: &ImageDescriptor) -> Self { + pub fn as_bytes(&self) -> Vec<u8> { let mut vec = vec![]; vec.push(0x2C); // Image Seperator - vec.extend_from_slice(&desc.left.to_le_bytes()); - vec.extend_from_slice(&desc.top.to_le_bytes()); - vec.extend_from_slice(&desc.width.to_le_bytes()); - vec.extend_from_slice(&desc.height.to_le_bytes()); - vec.push(desc.packed.raw); + vec.extend_from_slice(&self.left.to_le_bytes()); + vec.extend_from_slice(&self.top.to_le_bytes()); + vec.extend_from_slice(&self.width.to_le_bytes()); + vec.extend_from_slice(&self.height.to_le_bytes()); + vec.push(self.packed.raw); - vec.into_boxed_slice() + vec } } @@ -66,5 +65,3 @@ impl From<[u8; 9]> for ImageDescriptor { } } } - -//TODO: Impl to allow changing the packed field easier diff --git a/gifed/src/block/indexedimage.rs b/gifed/src/block/indexedimage.rs index 4e72ea0..ab8077f 100644 --- a/gifed/src/block/indexedimage.rs +++ b/gifed/src/block/indexedimage.rs @@ -1,10 +1,9 @@ -use std::convert::TryFrom; - use weezl::encode::Encoder; use super::{ImageDescriptor, Palette}; -use crate::LZW; +use crate::writer::EncodeError; +#[derive(Clone, Debug)] pub struct IndexedImage { pub image_descriptor: ImageDescriptor, pub local_color_table: Option<Palette>, @@ -28,48 +27,62 @@ impl IndexedImage { self.image_descriptor.height } - pub fn as_boxed_slice(&self, minimum_code_size: u8) -> Box<[u8]> { - let mut out = vec![]; - - let mut boxed: Box<[u8]> = (&self.image_descriptor).into(); - out.extend_from_slice(&*boxed); - - // Get the mcs while we write out the color table - let mut mcs = if let Some(lct) = &self.local_color_table { - out.extend_from_slice(&lct.as_bytes()); - - lct.packed_len() + 1 - } else { - minimum_code_size + 1 + /// The `lzw_code_size` should be None if there is a local color table present. If + /// this image is using the Global Color Table, you must provide an + /// LZW Minimum Code Size here. It is equal to the value of [Palette::packed_len], but + /// must also be at least 2. + pub fn compress(self, lzw_code_size: Option<u8>) -> Result<CompressedImage, EncodeError> { + //TODO: gen- The old code had a +1 here. Why? + let mcs = match lzw_code_size { + Some(mcs) => mcs, + None => match self.local_color_table.as_ref() { + None => return Err(EncodeError::InvalidCodeSize { lzw_code_size: 0 }), + Some(lct) => lct.packed_len(), + }, }; - if mcs < 2 { - mcs = 2; // Must be true: 0 <= mcs <= 8 - } - - // First write out the MCS - out.push(mcs); - //FIXME: gen- This seems broken //let compressed = LZW::encode(mcs, &self.indicies); let compressed = Encoder::new(weezl::BitOrder::Lsb, mcs) .encode(&self.indicies) .unwrap(); + let mut blocks = vec![]; for chunk in compressed.chunks(255) { - out.push(chunk.len() as u8); - out.extend_from_slice(chunk); + blocks.push(chunk.to_vec()); } - // Data block length 0 to indicate an end - out.push(0x00); - out.into_boxed_slice() + Ok(CompressedImage { + image_descriptor: self.image_descriptor, + local_color_table: self.local_color_table, + lzw_code_size: mcs, + blocks, + }) } } pub struct CompressedImage { pub image_descriptor: ImageDescriptor, pub local_color_table: Option<Palette>, - pub lzw_minimum_code_size: u8, + pub lzw_code_size: u8, pub blocks: Vec<Vec<u8>>, } + +impl CompressedImage { + pub fn as_bytes(&self) -> Vec<u8> { + let mut ret = vec![]; + + ret.extend_from_slice(&self.image_descriptor.as_bytes()); + ret.push(self.lzw_code_size); + + for block in &self.blocks { + ret.push(block.len() as u8); + ret.extend_from_slice(block); + } + + // A zero length block indicates the end of the data stream + ret.push(0x00); + + ret + } +} diff --git a/gifed/src/block/mod.rs b/gifed/src/block/mod.rs index 06f3d9a..99075cf 100644 --- a/gifed/src/block/mod.rs +++ b/gifed/src/block/mod.rs @@ -19,7 +19,7 @@ use self::extension::Application; use self::extension::GraphicControl; pub enum Block { - IndexedImage(IndexedImage), + CompressedImage(CompressedImage), //TODO: Extension(Extension), GraphicControlExtension(GraphicControl), CommentExtension(Vec<u8>), @@ -38,9 +38,9 @@ pub enum LoopCount { Number(u16), } -pub(crate) fn encode_block(mcs: u8, block: &Block) -> Box<[u8]> { +pub(crate) fn encode_block(mcs: u8, block: &Block) -> Vec<u8> { match block { - Block::IndexedImage(img) => img.as_boxed_slice(mcs), + Block::CompressedImage(img) => img.as_bytes(), Block::GraphicControlExtension(_) => encode_extension(block), Block::CommentExtension(_) => encode_extension(block), Block::ApplicationExtension(_) => encode_extension(block), @@ -48,12 +48,12 @@ pub(crate) fn encode_block(mcs: u8, block: &Block) -> Box<[u8]> { } } -fn encode_extension(block: &Block) -> Box<[u8]> { +fn encode_extension(block: &Block) -> Vec<u8> { let mut vec = vec![]; vec.push(0x21); // Extension Introducer match block { - Block::IndexedImage(_) => unreachable!(), + Block::CompressedImage(_) => unreachable!(), Block::GraphicControlExtension(gce) => { vec.push(0xF9); // Graphic control label vec.push(0x04); // Block size for this extension is always 4 @@ -87,6 +87,8 @@ fn encode_extension(block: &Block) -> Box<[u8]> { } } - vec.push(0x00); // Zero length sub-block indicates end of extension - vec.into_boxed_slice() + // Zero length sub-block indicates end of extension + vec.push(0x00); + + vec } diff --git a/gifed/src/gif.rs b/gifed/src/gif.rs index d9af572..816b10b 100644 --- a/gifed/src/gif.rs +++ b/gifed/src/gif.rs @@ -7,7 +7,7 @@ use crate::{ packed::ImagePacked, Block, Palette, ScreenDescriptor, Version, }, - reader::DecodingError, + reader::DecodeError, writer::GifBuilder, Color, }; @@ -42,8 +42,7 @@ impl Gif { }; for block in self.blocks.iter() { - boxed = encode_block(mcs, block); - out.extend_from_slice(&*boxed); + out.extend_from_slice(&encode_block(mcs, block)); } // Write Trailer @@ -78,7 +77,7 @@ impl<'a> Iterator for ImageIterator<'a> { let img = loop { match self.gif.blocks.get(self.block_index) { Some(block) => match block { - Block::IndexedImage(img) => { + Block::CompressedImage(img) => { // Step over this image so we don't hit it next time self.block_index += 1; @@ -104,7 +103,7 @@ impl<'a> Iterator for ImageIterator<'a> { top_offset: img.image_descriptor.top, packed: img.image_descriptor.packed, palette, - indicies: &img.indicies, + image_blocks: &img.blocks, blocks: &self.gif.blocks[starting_block..self.block_index], }) } @@ -117,7 +116,7 @@ pub struct Image<'a> { pub top_offset: u16, pub packed: ImagePacked, pub palette: &'a Palette, - pub indicies: &'a [u8], + pub image_blocks: &'a [Vec<u8>], pub blocks: &'a [Block], } diff --git a/gifed/src/reader/mod.rs b/gifed/src/reader/mod.rs index bd7d08a..68226f9 100644 --- a/gifed/src/reader/mod.rs +++ b/gifed/src/reader/mod.rs @@ -4,22 +4,22 @@ use std::{ error::Error, fmt, fs::File, - io::{BufRead, BufReader, Read}, + io::Read, path::Path, }; use crate::{ block::{ extension::{Application, GraphicControl}, - Block, CompressedImage, ImageDescriptor, IndexedImage, Palette, ScreenDescriptor, Version, + Block, CompressedImage, ImageDescriptor, Palette, ScreenDescriptor, Version, }, - color, Gif, + Gif, }; pub struct GifReader {} impl GifReader { - pub fn file<P: AsRef<Path>>(path: P) -> Result<Gif, DecodingError> { + pub fn file<P: AsRef<Path>>(path: P) -> Result<Gif, DecodeError> { let mut file = File::open(path)?; let mut reader = SmartReader { inner: vec![], @@ -42,17 +42,17 @@ impl GifReader { } } - fn read_required(reader: &mut SmartReader) -> Result<Gif, DecodingError> { + fn read_required(reader: &mut SmartReader) -> Result<Gif, DecodeError> { let version = match reader.take_lossy_utf8(6).as_deref() { Some("GIF87a") => Version::Gif87a, Some("GIF89a") => Version::Gif89a, - _ => return Err(DecodingError::UnknownVersionString), + _ => return Err(DecodeError::UnknownVersionString), }; let mut lsd_buffer: [u8; 7] = [0; 7]; reader .read_exact(&mut lsd_buffer) - .ok_or(DecodingError::UnexpectedEof)?; + .ok_or(DecodeError::UnexpectedEof)?; let lsd = ScreenDescriptor::from(lsd_buffer); @@ -64,17 +64,17 @@ impl GifReader { }) } - fn read_color_table(reader: &mut SmartReader, size: usize) -> Result<Palette, DecodingError> { + fn read_color_table(reader: &mut SmartReader, size: usize) -> Result<Palette, DecodeError> { let buffer = reader .take(size as usize) - .ok_or(DecodingError::UnexpectedEof)?; + .ok_or(DecodeError::UnexpectedEof)?; // We get the size from the screen descriptor. This should never return Err Ok(Palette::try_from(&buffer[..]).unwrap()) } - fn read_block(reader: &mut SmartReader) -> Result<Option<Block>, DecodingError> { - let block_id = reader.u8().ok_or(DecodingError::UnexpectedEof)?; + fn read_block(reader: &mut SmartReader) -> Result<Option<Block>, DecodeError> { + let block_id = reader.u8().ok_or(DecodeError::UnexpectedEof)?; //TODO: remove panic match block_id { @@ -88,7 +88,7 @@ impl GifReader { } } - fn read_extension(reader: &mut SmartReader) -> Result<Block, DecodingError> { + fn read_extension(reader: &mut SmartReader) -> Result<Block, DecodeError> { let extension_id = reader.u8().expect("File ended early"); match extension_id { @@ -97,7 +97,7 @@ impl GifReader { let mut data = [0u8; 4]; reader .read_exact(&mut data) - .ok_or(DecodingError::UnexpectedEof)?; + .ok_or(DecodeError::UnexpectedEof)?; reader.skip(1); // Skip block terminator Ok(Block::GraphicControlExtension(GraphicControl::from(data))) @@ -124,11 +124,11 @@ impl GifReader { } } - fn read_image(mut reader: &mut SmartReader) -> Result<Block, DecodingError> { + fn read_image(mut reader: &mut SmartReader) -> Result<Block, DecodeError> { let mut buffer = [0u8; 9]; reader .read_exact(&mut buffer) - .ok_or(DecodingError::UnexpectedEof)?; + .ok_or(DecodeError::UnexpectedEof)?; let descriptor = ImageDescriptor::from(buffer); let color_table = if descriptor.has_color_table() { @@ -138,42 +138,38 @@ impl GifReader { None }; - let lzw_csize = reader.u8().ok_or(DecodingError::UnexpectedEof)?; + let lzw_csize = reader.u8().ok_or(DecodeError::UnexpectedEof)?; + let compressed_data = reader.take_data_subblocks(); - let compressed_data = reader.take_and_collapse_subblocks(); - - let mut decompress = weezl::decode::Decoder::new(weezl::BitOrder::Lsb, lzw_csize); - //TODO: remove unwrap - let mut decompressed_data = decompress.decode(&compressed_data).unwrap(); - - Ok(Block::IndexedImage(IndexedImage { + Ok(Block::CompressedImage(CompressedImage { image_descriptor: descriptor, local_color_table: color_table, - indicies: decompressed_data, + lzw_code_size: lzw_csize, + blocks: compressed_data, })) } } #[derive(Debug)] -pub enum DecodingError { +pub enum DecodeError { IoError(std::io::Error), UnknownVersionString, UnexpectedEof, ColorIndexOutOfBounds, } -impl Error for DecodingError {} -impl fmt::Display for DecodingError { +impl Error for DecodeError {} +impl fmt::Display for DecodeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - DecodingError::IoError(error) => write!(f, "{}", error), - DecodingError::UnknownVersionString => { + DecodeError::IoError(error) => write!(f, "{}", error), + DecodeError::UnknownVersionString => { write!(f, "File did not start with a valid header") } - DecodingError::UnexpectedEof => { + DecodeError::UnexpectedEof => { write!(f, "Found the end of the data at a weird spot") } - DecodingError::ColorIndexOutOfBounds => { + DecodeError::ColorIndexOutOfBounds => { write!( f, "The image contained an index not found in the color table" @@ -183,9 +179,9 @@ impl fmt::Display for DecodingError { } } -impl From<std::io::Error> for DecodingError { +impl From<std::io::Error> for DecodeError { fn from(ioerror: std::io::Error) -> Self { - DecodingError::IoError(ioerror) + DecodeError::IoError(ioerror) } } diff --git a/gifed/src/writer/gifbuilder.rs b/gifed/src/writer/gifbuilder.rs index f02862b..a6f5604 100644 --- a/gifed/src/writer/gifbuilder.rs +++ b/gifed/src/writer/gifbuilder.rs @@ -59,10 +59,12 @@ impl GifBuilder { self.blocks.push(Block::GraphicControlExtension(gce)); } + //FIXME + /* match ib.build() { Ok(image) => self.blocks.push(Block::IndexedImage(image)), Err(e) => self.error = Some(e), - } + }*/ self } diff --git a/gifed/src/writer/mod.rs b/gifed/src/writer/mod.rs index 88311fc..4a12a76 100644 --- a/gifed/src/writer/mod.rs +++ b/gifed/src/writer/mod.rs @@ -1,5 +1,22 @@ mod gifbuilder; mod imagebuilder; +use std::fmt; + pub use gifbuilder::GifBuilder; pub use imagebuilder::ImageBuilder; + +pub enum EncodeError { + InvalidCodeSize { lzw_code_size: u8 }, +} + +impl fmt::Display for EncodeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + //TODO: better error + EncodeError::InvalidCodeSize { lzw_code_size } => { + write!(f, "InvalidCodeSize => {lzw_code_size}") + } + } + } +} |