about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--gifed/examples/read.rs30
-rw-r--r--gifed/examples/write.rs43
-rw-r--r--gifed/src/block/indexedimage.rs8
-rw-r--r--gifed/src/block/mod.rs10
-rw-r--r--gifed/src/block/palette.rs4
-rw-r--r--gifed/src/block/screendescriptor.rs16
-rw-r--r--gifed/src/block/version.rs6
-rw-r--r--gifed/src/gif.rs18
-rw-r--r--gifed/src/writer/gifbuilder.rs12
9 files changed, 89 insertions, 58 deletions
diff --git a/gifed/examples/read.rs b/gifed/examples/read.rs
deleted file mode 100644
index 2c8b08d..0000000
--- a/gifed/examples/read.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use std::fs::File;
-
-use gifed::{
-	reader::{self, GifReader},
-	writer::ImageBuilder,
-	Gif,
-};
-
-fn main() {
-	let reader = GifReader::file("/home/gen/Downloads/emilia_bar.gif").unwrap();
-
-	// Create the directory we're we'll dump all the PNGs
-	std::fs::create_dir_all("examples/read/").unwrap();
-
-	for (frame_number, image) in reader.images().enumerate() {
-		let filename = format!("examples/read/simulation_{}.png", frame_number);
-		let file = File::create(filename).unwrap();
-
-		let mut encoder = png::Encoder::new(file, image.width as u32, image.height as u32);
-		encoder.set_color(png::ColorType::Indexed);
-		encoder.set_palette(image.palette.as_bytes());
-
-		if let Some(trns) = image.png_trns() {
-			encoder.set_trns(trns);
-		}
-
-		let mut writer = encoder.write_header().unwrap();
-		writer.write_image_data(&image.indicies).unwrap();
-	}
-}
diff --git a/gifed/examples/write.rs b/gifed/examples/write.rs
new file mode 100644
index 0000000..ff8809f
--- /dev/null
+++ b/gifed/examples/write.rs
@@ -0,0 +1,43 @@
+use gifed::{
+	block::{LoopCount, Palette},
+	writer::ImageBuilder,
+	Color, Gif,
+};
+
+fn main() {
+	let gif_path = match std::env::args().nth(1) {
+		None => {
+			eprintln!("Expected a path to output the gif to");
+			std::process::exit(-1);
+		}
+		Some(path) => path,
+	};
+
+	let mut palette = Palette::new();
+
+	// Fill the palette with every gray
+	for gray in 0..=255 {
+		palette.push(Color::new(gray, gray, gray));
+	}
+
+	let mut image = vec![0; 128 * 128];
+
+	let mut builder = Gif::builder(128, 128).palette(palette);
+	for idx in 0..=255 {
+		image.fill(idx);
+
+		builder = builder.image(
+			ImageBuilder::new(128, 128)
+				.delay(3)
+				.build(image.clone())
+				.unwrap(),
+		);
+	}
+
+	builder
+		.repeat(LoopCount::Forever)
+		.build()
+		.unwrap()
+		.save(gif_path)
+		.unwrap();
+}
diff --git a/gifed/src/block/indexedimage.rs b/gifed/src/block/indexedimage.rs
index 25eadbe..d27d463 100644
--- a/gifed/src/block/indexedimage.rs
+++ b/gifed/src/block/indexedimage.rs
@@ -33,9 +33,13 @@ impl IndexedImage {
 	/// 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?
+		// gen- The old code had a +1 here. Why?
+		// In the spec, under the section for the Logical Screen Descriptor, it
+		// mentions that the size in the packed field is calculated with
+		// 2 ^ (packed + 1) and the code size is supposed to be the "number
+		// of color bits", which I guess is the exponent?
 		let mcs = match self.local_color_table.as_ref() {
-			Some(palette) => palette.packed_len(),
+			Some(palette) => palette.lzw_code_size(),
 			None => match lzw_code_size {
 				None => return Err(EncodeError::InvalidCodeSize { lzw_code_size: 0 }),
 				Some(mcs) => mcs,
diff --git a/gifed/src/block/mod.rs b/gifed/src/block/mod.rs
index 99075cf..5eb8a78 100644
--- a/gifed/src/block/mod.rs
+++ b/gifed/src/block/mod.rs
@@ -38,7 +38,15 @@ pub enum LoopCount {
 	Number(u16),
 }
 
-pub(crate) fn encode_block(mcs: u8, block: &Block) -> Vec<u8> {
+impl LoopCount {
+	/// Set a fixed loop count. A value of 0 means forever, which you should
+	/// probably use [LoopCount::Forever] for.
+	pub fn count(count: u16) -> Self {
+		Self::Number(count)
+	}
+}
+
+pub(crate) fn encode_block(block: &Block) -> Vec<u8> {
 	match block {
 		Block::CompressedImage(img) => img.as_bytes(),
 		Block::GraphicControlExtension(_) => encode_extension(block),
diff --git a/gifed/src/block/palette.rs b/gifed/src/block/palette.rs
index 8f58b40..f2f4de3 100644
--- a/gifed/src/block/palette.rs
+++ b/gifed/src/block/palette.rs
@@ -25,6 +25,10 @@ impl Palette {
 		((self.table.len() as f32).log2().ceil() - 1f32) as u8
 	}
 
+	pub fn lzw_code_size(&self) -> u8 {
+		self.packed_len() + 1
+	}
+
 	/// Returns the number of items in the table
 	pub fn len(&self) -> usize {
 		self.table.len()
diff --git a/gifed/src/block/screendescriptor.rs b/gifed/src/block/screendescriptor.rs
index 766ad66..aaeea53 100644
--- a/gifed/src/block/screendescriptor.rs
+++ b/gifed/src/block/screendescriptor.rs
@@ -42,18 +42,16 @@ impl ScreenDescriptor {
 	pub fn color_table_len(&self) -> usize {
 		crate::packed_to_color_table_length(self.packed.color_table_size())
 	}
-}
 
-impl From<&ScreenDescriptor> for Box<[u8]> {
-	fn from(lsd: &ScreenDescriptor) -> Self {
+	pub fn as_bytes(&self) -> Vec<u8> {
 		let mut vec = vec![];
-		vec.extend_from_slice(&lsd.width.to_le_bytes());
-		vec.extend_from_slice(&lsd.height.to_le_bytes());
-		vec.push(lsd.packed.raw);
-		vec.push(lsd.background_color_index);
-		vec.push(lsd.pixel_aspect_ratio);
+		vec.extend_from_slice(&self.width.to_le_bytes());
+		vec.extend_from_slice(&self.height.to_le_bytes());
+		vec.push(self.packed.raw);
+		vec.push(self.background_color_index);
+		vec.push(self.pixel_aspect_ratio);
 
-		vec.into_boxed_slice()
+		vec
 	}
 }
 
diff --git a/gifed/src/block/version.rs b/gifed/src/block/version.rs
index 0171ad4..c26ebb5 100644
--- a/gifed/src/block/version.rs
+++ b/gifed/src/block/version.rs
@@ -6,6 +6,12 @@ pub enum Version {
 	Gif89a,
 }
 
+impl Version {
+	pub fn as_bytes(&self) -> &[u8] {
+		self.into()
+	}
+}
+
 impl From<&Version> for &[u8] {
 	fn from(version: &Version) -> Self {
 		match version {
diff --git a/gifed/src/gif.rs b/gifed/src/gif.rs
index 5ee6906..fd3a322 100644
--- a/gifed/src/gif.rs
+++ b/gifed/src/gif.rs
@@ -24,23 +24,15 @@ impl Gif {
 	pub fn as_bytes(&self) -> Vec<u8> {
 		let mut out = vec![];
 
-		out.extend_from_slice((&self.header).into());
+		out.extend_from_slice(&self.header.as_bytes());
+		out.extend_from_slice(&self.screen_descriptor.as_bytes());
 
-		let mut boxed: Box<[u8]> = (&self.screen_descriptor).into();
-		out.extend_from_slice(&*boxed);
-
-		// While we output the color table, grab it's length to use when
-		// outputting the image, or 0 if we don't have a GCT
-		let mcs = if let Some(gct) = &self.global_color_table {
+		if let Some(gct) = &self.global_color_table {
 			out.extend_from_slice(&gct.as_bytes());
-
-			gct.packed_len()
-		} else {
-			0
-		};
+		}
 
 		for block in self.blocks.iter() {
-			out.extend_from_slice(&encode_block(mcs, block));
+			out.extend_from_slice(&encode_block(block));
 		}
 
 		// Write Trailer
diff --git a/gifed/src/writer/gifbuilder.rs b/gifed/src/writer/gifbuilder.rs
index 3e26c24..4ea88ec 100644
--- a/gifed/src/writer/gifbuilder.rs
+++ b/gifed/src/writer/gifbuilder.rs
@@ -2,8 +2,8 @@ use std::io::Write;
 
 use crate::{
 	block::{
-		packed::ScreenPacked, Block, CompressedImage, IndexedImage, Palette, ScreenDescriptor,
-		Version,
+		packed::ScreenPacked, Block, CompressedImage, IndexedImage, LoopCount, Palette,
+		ScreenDescriptor, Version,
 	},
 	EncodeError, Gif,
 };
@@ -48,6 +48,12 @@ impl GifBuilder {
 		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
@@ -87,7 +93,7 @@ impl GifBuilder {
 			blocks: vec![],
 		};
 
-		let lzw_gct_size = gif.global_color_table.as_ref().map(|ct| ct.packed_len());
+		let lzw_gct_size = gif.global_color_table.as_ref().map(|ct| ct.lzw_code_size());
 
 		for block in self.blocks {
 			match block {