about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgennyble <gen@nyble.dev>2023-11-21 16:07:12 -0600
committergennyble <gen@nyble.dev>2023-11-21 16:07:12 -0600
commit077e8385c9bb4bb0ac5dfe2279903d4dab11d063 (patch)
treecf06df720eacab4dc327cd736916522e5aaab5f8
parent56418912c5308d15215c38cd29836d8912783a8f (diff)
downloadgifed-077e8385c9bb4bb0ac5dfe2279903d4dab11d063.tar.gz
gifed-077e8385c9bb4bb0ac5dfe2279903d4dab11d063.zip
remove gifbuilder and work towards improving api
-rw-r--r--gifed/src/gif.rs200
-rw-r--r--gifed/src/reader/mod.rs6
-rw-r--r--gifed/src/writer/gifbuilder.rs137
-rw-r--r--gifed/src/writer/mod.rs33
4 files changed, 71 insertions, 305 deletions
diff --git a/gifed/src/gif.rs b/gifed/src/gif.rs
index 46807a9..81aea28 100644
--- a/gifed/src/gif.rs
+++ b/gifed/src/gif.rs
@@ -1,40 +1,50 @@
 use std::{fs::File, io::Write, path::Path, time::Duration};
 
-use crate::{
-	block::{
-		encode_block,
-		extension::{DisposalMethod, GraphicControl},
-		Block, CompressedImage, IndexedImage, Palette, ScreenDescriptor, Version,
-	},
-	writer::GifBuilder,
+use crate::block::{
+	encode_block,
+	extension::{DisposalMethod, GraphicControl},
+	Block, CompressedImage, IndexedImage, Palette, ScreenDescriptor, Version,
 };
 
 #[derive(Clone, Debug)]
 pub struct Gif {
-	pub header: Version,
-	pub screen_descriptor: ScreenDescriptor,
-	pub global_color_table: Option<Palette>,
+	/// Usually [Version::Gif89a], but might be [Version::Gif87a] for very
+	/// simple images.
+	pub version: Version,
+	pub descriptor: ScreenDescriptor,
+	pub palette: Option<Palette>,
 	pub blocks: Vec<Block>, // Trailer at the end of this struct is 0x3B //
 }
 
 impl Gif {
-	pub fn builder(width: u16, height: u16) -> GifBuilder {
-		GifBuilder::new(width, height)
+	pub fn set_width(&mut self, width: u16) {
+		self.descriptor.width = width;
 	}
 
-	pub fn width(&self) -> usize {
-		self.screen_descriptor.width as usize
+	pub fn width(&self) -> u16 {
+		self.descriptor.width
+	}
+
+	pub fn set_height(&mut self, height: u16) {
+		self.descriptor.height = height;
+	}
+
+	pub fn height(&self) -> u16 {
+		self.descriptor.height
 	}
 
-	pub fn height(&self) -> usize {
-		self.screen_descriptor.height as usize
+	pub fn set_background_color(&mut self, idx: u8) {
+		self.descriptor.background_color_index = idx;
 	}
 
 	pub fn background_color(&self) -> Option<u8> {
-		// vii) Background Color Index - If the Global Color Table Flag is set
-		// to (zero), this field should be zero and should be ignored.
-		if self.screen_descriptor.has_color_table() {
-			Some(self.screen_descriptor.background_color_index)
+		// vii) Background Color Index - Index into the Global Color Table for
+		// the Background Color. The Background Color is the color used for
+		// those pixels on the screen that are not covered by an image. If the
+		// Global Color Table Flag is set to (zero), this field should be zero
+		// and should be ignored.
+		if self.descriptor.has_color_table() {
+			Some(self.descriptor.background_color_index)
 		} else {
 			None
 		}
@@ -43,10 +53,10 @@ impl Gif {
 	pub fn as_bytes(&self) -> Vec<u8> {
 		let mut out = vec![];
 
-		out.extend_from_slice(self.header.as_bytes());
-		out.extend_from_slice(&self.screen_descriptor.as_bytes());
+		out.extend_from_slice(self.version.as_bytes());
+		out.extend_from_slice(&self.descriptor.as_bytes());
 
-		if let Some(gct) = &self.global_color_table {
+		if let Some(gct) = &self.palette {
 			out.extend_from_slice(&gct.as_bytes());
 		}
 
@@ -64,6 +74,7 @@ impl Gif {
 		File::create(path.as_ref())?.write_all(&self.as_bytes())
 	}
 
+	/// An iterator over the discrete images in the gif.
 	pub fn images(&self) -> ImageIterator<'_> {
 		ImageIterator {
 			gif: self,
@@ -97,7 +108,7 @@ impl<'a> Iterator for ImageIterator<'a> {
 
 		Some(Image {
 			compressed: img,
-			global_palette: self.gif.global_color_table.as_ref(),
+			global_palette: self.gif.palette.as_ref(),
 			blocks: &self.gif.blocks[starting_block..self.block_index],
 		})
 	}
@@ -169,11 +180,14 @@ impl<'a> Image<'a> {
 		if let Some(plt) = self.compressed.local_color_table.as_ref() {
 			plt
 		} else {
-			//FIXME: Maybe don't panic here
+			//FIXME: Maybe don't panic here.
+			// images can lack a palette entirely. in that case it's up to the
+			// decoder to pick one.
 			self.global_palette.unwrap()
 		}
 	}
 
+	/// Make a tRNS block for PNG files.
 	pub fn png_trns(&self) -> Option<Vec<u8>> {
 		let palette = self.palette();
 		if let Some(trans_idx) = self.transparent_index() {
@@ -195,7 +209,7 @@ impl<'a> Image<'a> {
 
 	/// Clones the CompressedImage and decompresses it.
 	pub fn decompess(&self) -> IndexedImage {
-		//FIXME: unwrap
+		//FIXME: remove unwrap
 		self.compressed.clone().decompress().unwrap()
 	}
 }
@@ -205,137 +219,3 @@ pub enum FrameControl {
 	Input,
 	InputOrDelay(Duration),
 }
-
-#[cfg(test)]
-pub mod gif_test {
-	use std::convert::TryInto;
-	use std::io::Write;
-
-	use crate::block::extension::DisposalMethod;
-	use crate::writer::{GifBuilder, ImageBuilder};
-	use crate::Color;
-
-	#[test]
-	fn to_vec_gif87a() {
-		let gct = vec![Color::new(1, 2, 3), Color::new(253, 254, 255)];
-		let colortable = vec![Color::new(0, 0, 0), Color::new(128, 0, 255)];
-		let indicies = vec![0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0];
-
-		let expected_out = vec![
-			0x47,
-			0x49,
-			0x46,
-			0x38,
-			0x37,
-			0x61, // Version - GIF87a
-			0x04,
-			0x00,
-			0x04,
-			0x00,
-			0b1000_0000,
-			0x00,
-			0x00, // Logical Screen Descriptor
-			1,
-			2,
-			3,
-			253,
-			254,
-			255, // Global Color Table
-			0x2C,
-			0x00,
-			0x00,
-			0x00,
-			0x00,
-			0x04,
-			0x00,
-			0x04,
-			0x00,
-			0b1000_0000, // Image Descriptor 1
-			0,
-			0,
-			0,
-			128,
-			0,
-			255, // Color Table
-			0x02,
-			0x05,
-			0x84,
-			0x1D,
-			0x81,
-			0x7A,
-			0x50,
-			0x00, // Image Data 1
-			0x2C,
-			0x00,
-			0x00,
-			0x00,
-			0x00,
-			0x04,
-			0x00,
-			0x04,
-			0x00,
-			0b0000_0000, // Image Descriptor 2
-			0x02,
-			0x05,
-			0x84,
-			0x1D,
-			0x81,
-			0x7A,
-			0x50,
-			0x00, // Image Data 2
-			0x3B, // Trailer
-		];
-
-		let actual = GifBuilder::new(4, 4)
-			.palette(gct.try_into().unwrap())
-			.image(
-				ImageBuilder::new(4, 4)
-					.palette(colortable.try_into().unwrap())
-					.build(indicies.clone())
-					.unwrap(),
-			)
-			.image(ImageBuilder::new(4, 4).build(indicies).unwrap());
-
-		let bytes = actual.build().unwrap().as_bytes();
-		assert_eq!(bytes, expected_out);
-	}
-
-	#[test]
-	fn to_vec_gif89a() {
-		let gct = vec![Color::new(1, 2, 3), Color::new(253, 254, 255)];
-		let colortable = vec![Color::new(0, 0, 0), Color::new(128, 0, 255)];
-		let indicies = vec![0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0];
-
-		let expected_out = vec![
-			71, 73, 70, 56, 57, 97, 4, 0, 4, 0, 128, 0, 0, 1, 2, 3, 253, 254, 255, 33, 249, 4, 8,
-			64, 0, 0, 0, 44, 0, 0, 0, 0, 4, 0, 4, 0, 128, 0, 0, 0, 128, 0, 255, 2, 5, 132, 29, 129,
-			122, 80, 0, 44, 0, 0, 0, 0, 4, 0, 4, 0, 0, 2, 5, 132, 29, 129, 122, 80, 0, 59,
-		];
-
-		let actual_out = GifBuilder::new(4, 4)
-			.palette(gct.try_into().unwrap())
-			.image(
-				ImageBuilder::new(4, 4)
-					.palette(colortable.try_into().unwrap())
-					.disposal_method(DisposalMethod::RestoreBackground)
-					.delay(64)
-					.build(indicies.clone())
-					.unwrap(),
-			)
-			.image(ImageBuilder::new(4, 4).build(indicies).unwrap())
-			.build()
-			.unwrap()
-			.as_bytes();
-
-		std::fs::File::create("ah.gif")
-			.unwrap()
-			.write_all(&actual_out)
-			.unwrap();
-		std::fs::File::create("ah_hand.gif")
-			.unwrap()
-			.write_all(&expected_out)
-			.unwrap();
-
-		assert_eq!(actual_out, expected_out);
-	}
-}
diff --git a/gifed/src/reader/mod.rs b/gifed/src/reader/mod.rs
index 763f34e..672b4ab 100644
--- a/gifed/src/reader/mod.rs
+++ b/gifed/src/reader/mod.rs
@@ -66,9 +66,9 @@ impl<R: Read> Decoder<R> {
 		}
 
 		Ok(Gif {
-			header: decoder.version,
-			screen_descriptor: decoder.screen_descriptor,
-			global_color_table: decoder.palette,
+			version: decoder.version,
+			descriptor: decoder.screen_descriptor,
+			palette: decoder.palette,
 			blocks,
 		})
 	}
diff --git a/gifed/src/writer/gifbuilder.rs b/gifed/src/writer/gifbuilder.rs
deleted file mode 100644
index 3d2dc23..0000000
--- a/gifed/src/writer/gifbuilder.rs
+++ /dev/null
@@ -1,137 +0,0 @@
-use crate::{
-	block::{
-		packed::ScreenPacked, Block, CompressedImage, IndexedImage, LoopCount, 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,
-	width: u16,
-	height: u16,
-	background_color_index: u8,
-	global_color_table: Option<Palette>,
-	blocks: Vec<BuildBlock>,
-}
-
-impl GifBuilder {
-	pub fn new(width: u16, height: u16) -> Self {
-		Self {
-			version: Version::Gif87a,
-			width,
-			height,
-			background_color_index: 0,
-			global_color_table: None,
-			blocks: vec![],
-		}
-	}
-
-	pub fn palette(mut self, palette: Palette) -> Self {
-		self.global_color_table = Some(palette);
-		self
-	}
-
-	pub fn background_index(mut self, ind: u8) -> Self {
-		self.background_color_index = ind;
-		self
-	}
-
-	pub fn block(mut self, block: Block) -> Self {
-		self.blocks.push(BuildBlock::Block(block));
-		self
-	}
-
-	pub fn repeat(mut self, count: LoopCount) -> Self {
-		self.blocks
-			.push(BuildBlock::Block(Block::LoopingExtension(count)));
-		self
-	}
-
-	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 }) => {
-				if let Some(gce) = gce {
-					self.version = Version::Gif89a;
-
-					self.blocks
-						.push(BuildBlock::Block(Block::GraphicControlExtension(gce)));
-				}
-
-				self.blocks.push(BuildBlock::Indexed(image));
-			}
-		}
-
-		self
-	}
-
-	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
-		};
-
-		screen_descriptor.set_color_table_metadata(self.global_color_table.as_ref());
-
-		let mut gif = Gif {
-			header: self.version,
-			screen_descriptor,
-			global_color_table: self.global_color_table,
-			blocks: vec![],
-		};
-
-		let lzw_gct_size = gif.global_color_table.as_ref().map(|ct| ct.lzw_code_size());
-
-		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/mod.rs b/gifed/src/writer/mod.rs
index c2e8382..e115ce8 100644
--- a/gifed/src/writer/mod.rs
+++ b/gifed/src/writer/mod.rs
@@ -1,14 +1,13 @@
-mod gifbuilder;
 mod imagebuilder;
 
 use std::{error::Error, fmt, io::Write};
 
-pub use gifbuilder::GifBuilder;
 pub use imagebuilder::{BuiltImage, ImageBuilder};
 
-use crate::block::{encode_block, Block, LoopCount, Palette, ScreenDescriptor, Version};
-
-use self::gifbuilder::EncodeImage;
+use crate::block::{
+	encode_block, Block, CompressedImage, IndexedImage, LoopCount, Palette, ScreenDescriptor,
+	Version,
+};
 
 pub struct Writer<W: Write> {
 	writer: W,
@@ -125,3 +124,27 @@ impl From<std::io::Error> for EncodeError {
 		EncodeError::IoError { error }
 	}
 }
+
+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)
+	}
+}