about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--gifed/src/block/indexedimage.rs11
-rw-r--r--gifed/src/block/palette.rs10
-rw-r--r--gifed/src/gif.rs24
-rw-r--r--gifed/src/lib.rs17
-rw-r--r--gifed/src/writer/gifbuilder.rs143
-rw-r--r--gifed/src/writer/imagebuilder.rs33
-rw-r--r--gifed/src/writer/mod.rs17
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}")
-			}
-		}
-	}
-}