diff options
author | Genny <gen@nyble.dev> | 2022-11-24 21:16:14 -0600 |
---|---|---|
committer | Genny <gen@nyble.dev> | 2022-11-24 21:16:14 -0600 |
commit | 8d0ed482d292e0a8fe8ea7c0c83b0fdb2d786729 (patch) | |
tree | efd4ec78cb3a32eadf545595b96b62c42dbf511a | |
parent | bc2b83e09ee6c1ed2e75961ff72f2e85579284c4 (diff) | |
download | gifed-8d0ed482d292e0a8fe8ea7c0c83b0fdb2d786729.tar.gz gifed-8d0ed482d292e0a8fe8ea7c0c83b0fdb2d786729.zip |
GifBuilder work
-rw-r--r-- | gifed/src/block/indexedimage.rs | 11 | ||||
-rw-r--r-- | gifed/src/block/palette.rs | 10 | ||||
-rw-r--r-- | gifed/src/gif.rs | 24 | ||||
-rw-r--r-- | gifed/src/lib.rs | 17 | ||||
-rw-r--r-- | gifed/src/writer/gifbuilder.rs | 143 | ||||
-rw-r--r-- | gifed/src/writer/imagebuilder.rs | 33 | ||||
-rw-r--r-- | gifed/src/writer/mod.rs | 17 |
7 files changed, 130 insertions, 125 deletions
diff --git a/gifed/src/block/indexedimage.rs b/gifed/src/block/indexedimage.rs index ab8077f..25eadbe 100644 --- a/gifed/src/block/indexedimage.rs +++ b/gifed/src/block/indexedimage.rs @@ -1,7 +1,8 @@ use weezl::encode::Encoder; +use crate::EncodeError; + use super::{ImageDescriptor, Palette}; -use crate::writer::EncodeError; #[derive(Clone, Debug)] pub struct IndexedImage { @@ -33,11 +34,11 @@ impl IndexedImage { /// 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() { + let mcs = match self.local_color_table.as_ref() { + Some(palette) => palette.packed_len(), + None => match lzw_code_size { None => return Err(EncodeError::InvalidCodeSize { lzw_code_size: 0 }), - Some(lct) => lct.packed_len(), + Some(mcs) => mcs, }, }; diff --git a/gifed/src/block/palette.rs b/gifed/src/block/palette.rs index f43a67c..8f58b40 100644 --- a/gifed/src/block/palette.rs +++ b/gifed/src/block/palette.rs @@ -1,5 +1,5 @@ pub use crate::Color; -use crate::EncodingError; +use crate::EncodeError; use std::{ convert::{TryFrom, TryInto}, ops::Deref, @@ -91,11 +91,11 @@ impl TryFrom<&[u8]> for Palette { } impl TryFrom<Vec<Color>> for Palette { - type Error = EncodingError; + type Error = EncodeError; fn try_from(value: Vec<Color>) -> Result<Self, Self::Error> { if value.len() > 256 { - Err(EncodingError::TooManyColors) + Err(EncodeError::TooManyColors) } else { Ok(Self { table: value }) } @@ -103,11 +103,11 @@ impl TryFrom<Vec<Color>> for Palette { } impl TryFrom<Vec<(u8, u8, u8)>> for Palette { - type Error = EncodingError; + type Error = EncodeError; fn try_from(value: Vec<(u8, u8, u8)>) -> Result<Self, Self::Error> { if value.len() > 256 { - Err(EncodingError::TooManyColors) + Err(EncodeError::TooManyColors) } else { Ok(Self { table: value.into_iter().map(|c| c.into()).collect(), diff --git a/gifed/src/gif.rs b/gifed/src/gif.rs index 816b10b..5ee6906 100644 --- a/gifed/src/gif.rs +++ b/gifed/src/gif.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, fs::File, io::Write, path::Path, time::Duration}; +use std::{fs::File, io::Write, path::Path, time::Duration}; use crate::{ block::{ @@ -7,9 +7,7 @@ use crate::{ packed::ImagePacked, Block, Palette, ScreenDescriptor, Version, }, - reader::DecodeError, writer::GifBuilder, - Color, }; pub struct Gif { pub header: Version, @@ -23,7 +21,7 @@ impl Gif { GifBuilder::new(width, height) } - pub fn to_vec(&self) -> Vec<u8> { + pub fn as_bytes(&self) -> Vec<u8> { let mut out = vec![]; out.extend_from_slice((&self.header).into()); @@ -52,7 +50,7 @@ impl Gif { } pub fn save<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()> { - File::create(path.as_ref())?.write_all(&self.to_vec()) + File::create(path.as_ref())?.write_all(&self.as_bytes()) } pub fn images<'a>(&'a self) -> ImageIterator<'a> { @@ -271,11 +269,12 @@ pub mod gif { .image( ImageBuilder::new(4, 4) .palette(colortable.try_into().unwrap()) - .indicies(&indicies), + .build(indicies.clone()) + .unwrap(), ) - .image(ImageBuilder::new(4, 4).indicies(&indicies)); + .image(ImageBuilder::new(4, 4).build(indicies).unwrap()); - let bytes = actual.build().unwrap().to_vec(); + let bytes = actual.build().unwrap().as_bytes(); assert_eq!(bytes, expected_out); } @@ -296,14 +295,15 @@ pub mod gif { .image( ImageBuilder::new(4, 4) .palette(colortable.try_into().unwrap()) - .indicies(&indicies) .disposal_method(DisposalMethod::RestoreBackground) - .delay(64), + .delay(64) + .build(indicies.clone()) + .unwrap(), ) - .image(ImageBuilder::new(4, 4).indicies(&indicies)) + .image(ImageBuilder::new(4, 4).build(indicies).unwrap()) .build() .unwrap() - .to_vec(); + .as_bytes(); std::fs::File::create("ah.gif") .unwrap() diff --git a/gifed/src/lib.rs b/gifed/src/lib.rs index 0b48018..f2dfba5 100644 --- a/gifed/src/lib.rs +++ b/gifed/src/lib.rs @@ -25,25 +25,24 @@ pub(crate) fn packed_to_color_table_length(packed: u8) -> usize { //bottom of page 24 in 89a #[derive(Clone, Copy, Debug)] -pub enum EncodingError { +pub enum EncodeError { TooManyColors, - NoColorTable, IndicieSizeMismatch { expected: usize, got: usize }, + InvalidCodeSize { lzw_code_size: u8 }, } -impl fmt::Display for EncodingError { +impl fmt::Display for EncodeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::TooManyColors => write!(f, "A palette is limited to 256 colors"), - Self::NoColorTable => write!( - f, - "Refusing to set the background color index when no color table is set!" - ), Self::IndicieSizeMismatch { expected, got } => { - write!(f, "Expected to have {} indicies but got {}", expected, got) + write!(f, "Expected to have {expected} indicies but got {got}") + } + Self::InvalidCodeSize { lzw_code_size } => { + write!(f, "InvalidCodeSize => {lzw_code_size}") } } } } -impl Error for EncodingError {} +impl Error for EncodeError {} diff --git a/gifed/src/writer/gifbuilder.rs b/gifed/src/writer/gifbuilder.rs index a6f5604..3e26c24 100644 --- a/gifed/src/writer/gifbuilder.rs +++ b/gifed/src/writer/gifbuilder.rs @@ -1,9 +1,21 @@ -use std::convert::TryInto; - -use crate::block::packed::ScreenPacked; -use crate::block::{Block, LoopCount, Palette, ScreenDescriptor, Version}; -use crate::writer::ImageBuilder; -use crate::{EncodingError, Gif}; +use std::io::Write; + +use crate::{ + block::{ + packed::ScreenPacked, Block, CompressedImage, IndexedImage, Palette, ScreenDescriptor, + Version, + }, + EncodeError, Gif, +}; + +use super::imagebuilder::BuiltImage; + +// We want to be able to gold [IndexedImage] as well as [CompressedImage], +// but [Block] does not allow that, so +enum BuildBlock { + Indexed(IndexedImage), + Block(Block), +} pub struct GifBuilder { version: Version, @@ -11,8 +23,7 @@ pub struct GifBuilder { height: u16, background_color_index: u8, global_color_table: Option<Palette>, - blocks: Vec<Block>, - error: Option<EncodingError>, + blocks: Vec<BuildBlock>, } impl GifBuilder { @@ -24,7 +35,6 @@ impl GifBuilder { background_color_index: 0, global_color_table: None, blocks: vec![], - error: None, } } @@ -34,74 +44,85 @@ impl GifBuilder { } pub fn background_index(mut self, ind: u8) -> Self { - if self.error.is_some() { - return self; - } - - if self.global_color_table.is_none() { - self.error = Some(EncodingError::NoColorTable); - } else { - self.background_color_index = ind; - } + self.background_color_index = ind; self } - pub fn image(mut self, ib: ImageBuilder) -> Self { - if self.error.is_some() { - return self; - } - - if ib.required_version() == Version::Gif89a { - self.version = Version::Gif89a; - } - - if let Some(gce) = ib.get_graphic_control() { - self.blocks.push(Block::GraphicControlExtension(gce)); + pub fn image<I: Into<EncodeImage>>(mut self, img: I) -> Self { + match img.into() { + EncodeImage::CompressedImage(ci) => self + .blocks + .push(BuildBlock::Block(Block::CompressedImage(ci))), + EncodeImage::IndexedImage(ii) => self.blocks.push(BuildBlock::Indexed(ii)), + EncodeImage::BuiltImage(BuiltImage { image, gce }) => { + self.blocks.push(BuildBlock::Indexed(image)); + + if let Some(gce) = gce { + self.version = Version::Gif89a; + + self.blocks + .push(BuildBlock::Block(Block::GraphicControlExtension(gce))); + } + } } - //FIXME - /* - match ib.build() { - Ok(image) => self.blocks.push(Block::IndexedImage(image)), - Err(e) => self.error = Some(e), - }*/ - self } - /*pub fn extension(mut self, ext: Extension) -> Self { - self.blocks.push(Block::Extension(ext)); - self - }*/ - - pub fn repeat(mut self, count: LoopCount) -> Self { - self.blocks.push(Block::LoopingExtension(count)); - self - } - - pub fn build(self) -> Result<Gif, EncodingError> { - if let Some(error) = self.error { - return Err(error); - } - - let mut lsd = ScreenDescriptor { + pub fn build(self) -> Result<Gif, EncodeError> { + let mut screen_descriptor = ScreenDescriptor { width: self.width, height: self.height, packed: ScreenPacked { raw: 0 }, // Set later background_color_index: self.background_color_index, - pixel_aspect_ratio: 0, //TODO: Allow configuring + pixel_aspect_ratio: 0, //TODO }; - if let Some(gct) = &self.global_color_table { - println!("build {}", gct.len()); - lsd.set_color_table_metadata(Some(gct)); - } + screen_descriptor.set_color_table_metadata(self.global_color_table.as_ref()); - Ok(Gif { + let mut gif = Gif { header: self.version, - screen_descriptor: lsd, + screen_descriptor, global_color_table: self.global_color_table, - blocks: self.blocks, - }) + blocks: vec![], + }; + + let lzw_gct_size = gif.global_color_table.as_ref().map(|ct| ct.packed_len()); + + for block in self.blocks { + match block { + BuildBlock::Indexed(indexed) => { + let compressed = indexed.compress(lzw_gct_size)?; + gif.blocks.push(Block::CompressedImage(compressed)); + } + BuildBlock::Block(block) => gif.blocks.push(block), + } + } + + Ok(gif) + } +} + +pub enum EncodeImage { + CompressedImage(CompressedImage), + IndexedImage(IndexedImage), + BuiltImage(BuiltImage), +} + +impl From<CompressedImage> for EncodeImage { + fn from(ci: CompressedImage) -> Self { + EncodeImage::CompressedImage(ci) + } +} + +impl From<IndexedImage> for EncodeImage { + fn from(ii: IndexedImage) -> Self { + EncodeImage::IndexedImage(ii) + } +} + +impl From<BuiltImage> for EncodeImage { + fn from(bi: BuiltImage) -> Self { + EncodeImage::BuiltImage(bi) } } diff --git a/gifed/src/writer/imagebuilder.rs b/gifed/src/writer/imagebuilder.rs index 1f1d67c..66d9fdb 100644 --- a/gifed/src/writer/imagebuilder.rs +++ b/gifed/src/writer/imagebuilder.rs @@ -4,7 +4,7 @@ use crate::{ packed::ImagePacked, ImageDescriptor, IndexedImage, Palette, Version, }, - EncodingError, + EncodeError, }; pub struct ImageBuilder { @@ -98,20 +98,16 @@ impl ImageBuilder { } } - pub fn indicies(mut self, indicies: &[u8]) -> Self { - self.indicies = indicies.to_vec(); - self - } - - pub fn build(self) -> Result<IndexedImage, EncodingError> { + pub fn build(self, indicies: Vec<u8>) -> Result<BuiltImage, EncodeError> { let expected_len = self.width as usize * self.height as usize; - if self.indicies.len() != expected_len { - return Err(EncodingError::IndicieSizeMismatch { + if indicies.len() != expected_len { + return Err(EncodeError::IndicieSizeMismatch { expected: expected_len, - got: self.indicies.len(), + got: indicies.len(), }); } + let gce = self.get_graphic_control(); let mut imgdesc = ImageDescriptor { left: self.left_offset, top: self.top_offset, @@ -120,14 +116,19 @@ impl ImageBuilder { packed: ImagePacked { raw: 0 }, // Set later }; - if let Some(lct) = &self.color_table { - imgdesc.set_color_table_metadata(Some(lct)); - } + imgdesc.set_color_table_metadata(self.color_table.as_ref()); - Ok(IndexedImage { + let image = IndexedImage { image_descriptor: imgdesc, local_color_table: self.color_table, - indicies: self.indicies, - }) + indicies, + }; + + Ok(BuiltImage { image, gce }) } } + +pub struct BuiltImage { + pub image: IndexedImage, + pub gce: Option<GraphicControl>, +} diff --git a/gifed/src/writer/mod.rs b/gifed/src/writer/mod.rs index 4a12a76..88311fc 100644 --- a/gifed/src/writer/mod.rs +++ b/gifed/src/writer/mod.rs @@ -1,22 +1,5 @@ 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}") - } - } - } -} |