about summary refs log tree commit diff
path: root/src/writer
diff options
context:
space:
mode:
Diffstat (limited to 'src/writer')
-rw-r--r--src/writer/gifbuilder.rs208
-rw-r--r--src/writer/imagebuilder.rs165
-rw-r--r--src/writer/lzw.rs175
-rw-r--r--src/writer/mod.rs4
-rw-r--r--src/writer/outvec.rs64
5 files changed, 41 insertions, 575 deletions
diff --git a/src/writer/gifbuilder.rs b/src/writer/gifbuilder.rs
index ff98ba7..6ae55d5 100644
--- a/src/writer/gifbuilder.rs
+++ b/src/writer/gifbuilder.rs
@@ -1,12 +1,11 @@
-use crate::common::{Color, Version};
-use super::OutVec;
+use crate::components::{ColorTable, Gif, LogicalScreenDescriptor, Version};
 use super::ImageBuilder;
 
 pub struct GifBuilder {
 	version: Version,
 	width: u16,
 	height: u16,
-	global_color_table: Option<Vec<Color>>,
+	global_color_table: Option<ColorTable>,
 	background_color_index: u8,
 	imagebuilders: Vec<ImageBuilder>
 }
@@ -23,13 +22,8 @@ impl GifBuilder {
 		}
 	}
 
-	pub fn global_color_table(mut self, vec: Vec<Color>) -> Self {
-		if vec.len() == 0 || vec.len() > 256 {
-			//TODO: Throw error instead of panic
-			panic!("GCT has to be less than or 256 colors in size, and at least one");
-		}
-
-		self.global_color_table = Some(vec);
+	pub fn global_color_table(mut self, table: ColorTable) -> Self {
+		self.global_color_table = Some(table);
 
 		self
 	}
@@ -49,190 +43,30 @@ impl GifBuilder {
 		self
 	}
 
-	pub fn write_to_vec(&self) -> Vec<u8> {
-		let mut out = OutVec::new();
-
-		self
-			.write_header(&mut out)
-			.write_logical_screen_descriptor(&mut out)
-			.write_global_color_table(&mut out)
-			.write_images(&mut out)
-			.write_trailer(&mut out);
-
-		out.vec()
-	}
-
-	fn write_header(&self, out: &mut OutVec) -> &Self {
-		out.push_slice(b"GIF");
-
-		//TODO: Automatically detect version
-		match self.version {
-			Version::Gif87a => out.push_slice(b"87a"),
-			Version::Gif89a => out.push_slice(b"89a")
+	pub fn build(self) -> Gif {
+		let mut lsd = LogicalScreenDescriptor {
+			width: self.width,
+			height: self.height,
+			packed: 0, // Set later
+			background_color_index: self.background_color_index,
+			pixel_aspect_ratio: 0 //TODO: Allow configuring
 		};
 
-		self
-	}
-
-	fn write_logical_screen_descriptor(&self, out: &mut OutVec) -> &Self {
-		out.push_u16(self.width).push_u16(self.height);
-
-		let mut packed: u8 = 0;
-
 		if let Some(gct) = &self.global_color_table {
-			packed |= 0b1000_0000; // Set the GCT flag
-
-			// GCT size is calulated by raising two to this number plus one,
-			// so we have to work backwards.
-			let size = (gct.len() as f32).log2().ceil() - 1f32;
-			
-			packed |= size as u8;
+			lsd.color_table_present(true);
+			lsd.color_table_size(gct.len() as u8);
 		}
-		//TODO: Color Resolution and Sort Flag fields in packed.
-		out.push_u8(packed);
-
-		out.push_u8(self.background_color_index)
-			.push_u8(0x00); //TOOD: Allow setting pixel aspect ratio
 
-		self
-	}
-
-	fn write_global_color_table(&self, out: &mut OutVec) -> &Self {
-		if let Some(gct) = &self.global_color_table {
-			out.push_colors(&gct);
+		let mut images = vec![];
+		for builder in self.imagebuilders.into_iter() {
+			images.push(builder.build());
 		}
 
-		self
-	}
-
-	fn write_images(&self, out: &mut OutVec) -> &Self {
-		//TODO: Deduplicate color table size code
-		let mcs = if let Some(gct) = &self.global_color_table {
-			((gct.len() as f32).log2().ceil() - 1f32) as u8
-		} else {
-			0
-		};
-
-		for ib in &self.imagebuilders {
-			let image = ib.write_to_vec(mcs);
-			out.push_slice(&image);
+		Gif {
+			header: self.version,
+			logical_screen_descriptor: lsd,
+			global_color_table: self.global_color_table,
+			images
 		}
-
-		self
-	}
-
-	fn write_trailer(&self, out: &mut OutVec) {
-		/*
-			"This block is a single-field block indicating the end of the GIF Data Stream. 
-			It contains the fixed value 0x3B."
-		*/
-		out.push_u8(0x3B);
-	}
-}
-
-#[cfg(test)]
-pub mod gifbuilder_test {
-	use super::*;
-
-	#[test]
-	pub fn writer_header() {
-		let mut out = OutVec::new();
-		GifBuilder::new(Version::Gif87a, 0, 0).write_header(&mut out);
-
-		assert_eq!(out.vec(), b"GIF87a");
-	}
-
-	#[test]
-	pub fn write_logical_screen_descriptor() {
-		let mut out = OutVec::new();
-		GifBuilder::new(Version::Gif87a, 4, 4).write_logical_screen_descriptor(&mut out);
-
-		assert_eq!(out.vec(), vec![0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00]);
-
-		let mut out = OutVec::new();
-		let gct = vec![
-			Color::new(0, 0, 0), Color::new(0, 0, 0), Color::new(0, 0, 0),
-			Color::new(0, 0, 0), Color::new(0, 0, 0)
-		];
-		GifBuilder::new(Version::Gif87a, 4, 4).global_color_table(gct).write_logical_screen_descriptor(&mut out);
-
-		assert_eq!(out.vec(), vec![0x04, 0x00, 0x04, 0x00, 0b1000_0010, 0x00, 0x00]);
-	}
-
-	#[test]
-	pub fn write_global_color_table() {
-		let mut out = OutVec::new();
-		let gct = vec![
-			Color::new(1, 2, 3), Color::new(253, 254, 255)
-		];
-		GifBuilder::new(Version::Gif87a, 4, 4).global_color_table(gct).write_global_color_table(&mut out);
-
-		assert_eq!(out.vec(), vec![1, 2, 3, 253, 254, 255]);
-	}
-
-	#[test]
-	fn write_images() {
-		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![
-			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
-		];
-
-		let mut out = OutVec::new();
-		GifBuilder::new(Version::Gif87a, 4, 4)
-			.global_color_table(gct)
-			.image(ImageBuilder::new(4, 4)
-				.color_table(colortable)
-				.indicies(indicies.clone())
-			).image(ImageBuilder::new(4, 4)
-				.indicies(indicies)
-			).write_images(&mut out);
-
-		assert_eq!(out.vec(), expected_out);
-	}
-
-	#[test]
-	fn write_to_vec() {
-		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 mut out = OutVec::new();
-		let actual_out = GifBuilder::new(Version::Gif87a, 4, 4)
-			.global_color_table(gct)
-			.image(ImageBuilder::new(4, 4)
-				.color_table(colortable)
-				.indicies(indicies.clone())
-			).image(ImageBuilder::new(4, 4)
-				.indicies(indicies)
-			).write_to_vec();
-
-		assert_eq!(actual_out, expected_out);
 	}
 }
\ No newline at end of file
diff --git a/src/writer/imagebuilder.rs b/src/writer/imagebuilder.rs
index cd96908..0636bb3 100644
--- a/src/writer/imagebuilder.rs
+++ b/src/writer/imagebuilder.rs
@@ -1,13 +1,11 @@
-use crate::common::Color;
-use super::OutVec;
-use super::LZW;
+use crate::components::{ColorTable, Image, ImageDescriptor};
 
 pub struct ImageBuilder {
 	left_offset: u16,
 	top_offset: u16,
 	width: u16,
 	height: u16,
-	color_table: Option<Vec<Color>>,
+	color_table: Option<ColorTable>,
 	indicies: Vec<u8>
 }
 
@@ -39,13 +37,8 @@ impl ImageBuilder {
 		self
 	}
 
-	pub fn color_table(mut self, vec: Vec<Color>) -> Self {
-		if vec.len() == 0 || vec.len() > 256 {
-			//TODO: Throw error instead of panic
-			panic!("Color table has to be less than or 256 colors in size, and at least one");
-		}
-
-		self.color_table = Some(vec);
+	pub fn color_table(mut self, table: ColorTable) -> Self {
+		self.color_table = Some(table);
 
 		self
 	}
@@ -55,142 +48,24 @@ impl ImageBuilder {
 		self
 	}
 
-	//TODO: Make lzw_minimum_code_size optional. ONly needed with global color tables
-	pub fn write_to_vec(&self, lzw_minimum_code_size: u8) -> Vec<u8> {
-		let mut out = OutVec::new();
-
-		self.write_image_descriptor(&mut out)
-			.write_color_table(&mut out)
-			.write_image_data(&mut out, lzw_minimum_code_size);
-
-		out.vec()
-	}
-
-	fn write_image_descriptor(&self, out: &mut OutVec) -> &Self {
-		// Image seperator. At the start of every image descriptor
-		out.push_u8(0x2C)
-		.push_u16(self.left_offset)
-		.push_u16(self.top_offset)
-		.push_u16(self.width)
-		.push_u16(self.height);
-
-		// Taken from gifbuilder.rs
-		//TODO: deduplciate code
-		let mut packed: u8 = 0;
-		if let Some(ct) = &self.color_table {
-			packed |= 0b1000_0000; // Set the color table flag
-
-			let size = (ct.len() as f32).log2().ceil() - 1f32;
-			
-			packed |= size as u8;
+	pub fn build(self) -> Image {
+		let mut imgdesc = ImageDescriptor {
+			left: self.left_offset,
+			top: self.top_offset,
+			width: self.width,
+			height: self.height,
+			packed: 0 // Set later
+		};
+
+		if let Some(lct) = &self.color_table {
+			imgdesc.color_table_present(true);
+			imgdesc.color_table_size(lct.packed_len());
 		}
-		//TODO: Interlace and Sort flags in packed
-		out.push_u8(packed);
-
-		self
-	}
 
-	fn write_color_table(&self, out: &mut OutVec) -> &Self {
-		if let Some(ct) = &self.color_table {
-			out.push_colors(&ct);
+		Image {
+			image_descriptor: imgdesc,
+			local_color_table: self.color_table,
+			indicies: self.indicies
 		}
-
-		self
-	}
-
-	fn write_image_data(&self, out: &mut OutVec, minimum_code_size: u8) -> &Self {
-		let mut mcs = minimum_code_size;
-
-		//TODO: Deduplicate color table size code
-		if let Some(ct) = &self.color_table {
-			mcs = ((ct.len() as f32).log2().ceil() - 1f32) as u8;
-		}
-
-		if mcs < 2 {
-			// Must always be true: 2 <= mcs <= 8
-			mcs = 2;
-		}
-
-		// First write out the MCS
-		out.push_u8(mcs);
-
-		let compressed = LZW::encode(mcs, &self.indicies);
-		
-		for chunk in compressed.chunks(255) {
-			out.push_u8(chunk.len() as u8);
-			out.push_slice(chunk);
-		}
-		// Data block length 0 to indicate an end
-		out.push_u8(0x00);
-
-		self
 	}
 }
-
-#[cfg(test)]
-mod imagebuilder_test {
-	use super::*;
-
-	#[test]
-	fn write_to_vec() {
-		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![
-			0x2C, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0b1000_0000, // Image Descriptor
-			0, 0, 0, 128, 0, 255, // Color Table
-			0x02, 0x05, 0x84, 0x1D, 0x81, 0x7A, 0x50, 0x00 // Image Data
-		];
-		let actual_out = ImageBuilder::new(4, 4)
-			.color_table(colortable)
-			.indicies(indicies)
-			.write_to_vec(0);
-
-		assert_eq!(actual_out, expected_out);
-	}
-
-	#[test]
-	fn write_image_descriptor() {
-		let mut out = OutVec::new();
-		ImageBuilder::new(16, 16).offsets(1, 6).write_image_descriptor(&mut out);
-
-		assert_eq!(out.vec(), vec![0x2C, 0x01, 0x00, 0x06, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00]);
-
-		let mut out = OutVec::new();
-		ImageBuilder::new(16, 16)
-			.offsets(1, 6)
-			.color_table(vec![Color::new(0, 0, 0)])
-			.write_image_descriptor(&mut out);
-
-		assert_eq!(out.vec(), vec![0x2C, 0x01, 0x00, 0x06, 0x00, 0x10, 0x00, 0x10, 0x00, 0b1000_0000]);
-	}
-
-	#[test]
-	fn write_color_table() {
-		let mut out = OutVec::new();
-		ImageBuilder::new(16, 16)
-			.color_table(vec![Color::new(1, 2, 3), Color::new(253, 254, 255)])
-			.write_color_table(&mut out);
-
-		assert_eq!(out.vec(), vec![0x01, 0x02, 0x03, 0xFD, 0xFE, 0xFF]);
-	}
-
-	#[test]
-	fn write_image_data() {
-		#[test]
-		fn encode() {
-			let indicies = vec![0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0];
-			let output = vec![0x02, 0x05, 0x84, 0x1D, 0x81, 0x7A, 0x50, 0x00];
-
-			let mut out = OutVec::new();
-			ImageBuilder::new(16, 16)
-				.indicies(indicies)
-				.write_image_data(&mut out, 2);
-	
-			assert_eq!(out.vec(), output);
-		}
-	}
-}
\ No newline at end of file
diff --git a/src/writer/lzw.rs b/src/writer/lzw.rs
deleted file mode 100644
index 1970617..0000000
--- a/src/writer/lzw.rs
+++ /dev/null
@@ -1,175 +0,0 @@
-use std::collections::HashMap;
-
-pub struct LZW {}
-impl LZW {
-	//TODO: Cleanup and make not awful
-	pub fn encode(minimum_size: u8, indicies: &[u8]) -> Vec<u8> {
-		let mut dictionary: HashMap<Vec<u8>, u16> = HashMap::new();
-
-		let cc: u16 = 2u16.pow(minimum_size as u32);
-		let eoi: u16 = cc+1;
-
-		let mut index: u16 = eoi+1; // Next code number
-		let mut codesize: u8 = minimum_size+1; // Current code size
-
-		//println!("cc: {} - eoi: {}", cc, eoi);
-
-		// Populate starting codes
-		for code in 0..cc {
-			dictionary.insert(vec![code as u8], code);
-		}
-
-		let mut iter = indicies.iter();
-		let mut out = BitStream::new();
-		let mut buffer = vec![*iter.next().unwrap()]; //TODO: Remove unwrap
-
-		//"Encoders should output a Clear code as the first code of each image data stream."
-		out.push_bits(codesize, cc);
-
-		//println!("Before Loop\n\tBuffer: {:?}\n\tCodesize:{}\n\tIndex:{}", buffer, codesize, index);
-
-		for next_code in iter {
-			buffer.push(*next_code);
-
-			//println!("Buffer: {:?} - Codesize:{} - Index:{}\n\tDict: {:?}", buffer, codesize, index, dictionary);
-
-			match dictionary.get(&buffer) {
-				Some(_code) => {
-					//println!("\tPresent!");
-					continue;
-				},
-				None => {
-					buffer.pop();
-					match dictionary.get(&buffer) {
-						Some(code) => {
-							out.push_bits(codesize, *code);
-							//println!("\tOutputting {} with {} bits. Buffer is {:?}", *code, codesize, buffer);
-
-							// Add new entry for buffer and increase the index
-							buffer.push(*next_code);
-							dictionary.insert(buffer, index);
-							index += 1;
-
-							// Increase code size if we should
-							if index-1 == 2u16.pow(codesize as u32) {
-								codesize += 1;
-							}
-
-							// Reset the buffer to only contain next_code
-							buffer = vec![*next_code];
-						},
-						None => panic!("No dictionary entry when there should be! Something is wrong!")
-					}
-				}
-			}
-		}
-
-		if buffer.len() > 0 {
-			match dictionary.get(&buffer) {
-				None => panic!("No codes left but not in the dictionary!"),
-				Some(code) => out.push_bits(codesize, *code)
-			}
-		}
-		out.push_bits(codesize, eoi);
-
-		out.vec()
-	}
-}
-
-#[cfg(test)]
-mod lzw_test {
-	use super::*;
-
-	#[test]
-	fn encode() {
-		let indicies = vec![0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0];
-		let output = vec![0x84, 0x1D, 0x81, 0x7A, 0x50];
-
-		let lzout = LZW::encode(2, &indicies);
-
-		assert_eq!(lzout, output);
-	}
-}
-
-struct BitStream {
-	formed: Vec<u8>,
-	current: u8,
-	index: u8
-}
-
-impl BitStream {
-	fn new() -> Self {
-		Self {
-			formed: vec![],
-			current: 0,
-			index: 0
-		}
-	}
-
-	fn push_bits(&mut self, count: u8, data: u16) {
-		let mut new_index = self.index + count;
-		let mut current32 = (self.current as u32) | ((data as u32) << self.index);
-
-		loop {
-			if new_index >= 8 {
-				self.formed.push(current32 as u8);
-				current32 = current32 >> 8;
-				new_index -= 8;
-			} else {
-				self.current = current32 as u8;
-				self.index = new_index;
-
-				break;
-			}
-		}
-	}
-
-	fn vec(self) -> Vec<u8> {
-		let mut out = self.formed;
-
-		if self.index != 0 {
-			out.push(self.current);
-		}
-
-		out
-	}
-}
-
-#[cfg(test)]
-mod bitstream_test {
-	use super::*;
-
-	#[test]
-	fn short_push() {
-		let mut bs = BitStream::new();
-		bs.push_bits(2, 3);
-		bs.push_bits(2, 3);
-		bs.push_bits(3, 1);
-		bs.push_bits(2, 3);
-
-		let bsvec = bs.vec();
-
-		for byte in &bsvec {
-			print!("{:b} ", byte);
-		}
-		println!("");
-
-		assert_eq!(bsvec, vec![0b1001_1111, 0b0000_0001]);
-	}
-
-	#[test]
-	fn long_push() {
-		let mut bs = BitStream::new();
-		bs.push_bits(1, 1);
-		bs.push_bits(12, 2049);
-
-		let bsvec = bs.vec();
-
-		for byte in &bsvec {
-			print!("{:b} ", byte);
-		}
-		println!("");
-
-		assert_eq!(bsvec, vec![0b0000_0011, 0b0001_0000]);
-	}
-}
\ No newline at end of file
diff --git a/src/writer/mod.rs b/src/writer/mod.rs
index 965dcfa..b801a3a 100644
--- a/src/writer/mod.rs
+++ b/src/writer/mod.rs
@@ -1,9 +1,5 @@
-mod outvec;
 mod gifbuilder;
-mod lzw;
 mod imagebuilder;
 
-use outvec::OutVec;
 pub use gifbuilder::GifBuilder;
-use lzw::LZW;
 pub use imagebuilder::ImageBuilder;
\ No newline at end of file
diff --git a/src/writer/outvec.rs b/src/writer/outvec.rs
deleted file mode 100644
index eb04934..0000000
--- a/src/writer/outvec.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-use crate::common::Color;
-
-#[derive(Debug, PartialEq)]
-pub struct OutVec {
-	data: Vec<u8>
-}
-
-impl OutVec {
-	pub fn new() -> Self {
-		Self {
-			data: vec![]
-		}
-	}
-
-	pub fn push_u8(&mut self, n: u8) -> &mut Self {
-		self.data.push(n);
-		self
-	}
-
-	pub fn push_u16(&mut self, n: u16) -> &mut Self {
-		self.data.extend_from_slice(&n.to_le_bytes());
-		self
-	}
-
-	pub fn push_u32(&mut self, n: u32) -> &mut Self {
-		self.data.extend_from_slice(&n.to_le_bytes());
-		self
-	}
-
-	pub fn push_u64(&mut self, n: u64) -> &mut Self {
-		self.data.extend_from_slice(&n.to_le_bytes());
-		self
-	}
-
-	pub fn push_slice(&mut self, slice: &[u8]) -> &mut Self {
-		self.data.extend_from_slice(slice);
-		self
-	}
-
-	pub fn push_color(&mut self, color: &Color) -> &mut Self {
-		self.data.extend_from_slice(&[color.r, color.g, color.b]);
-		self
-	}
-
-	pub fn push_colors(&mut self, colors: &[Color]) -> &mut Self {
-		for color in colors {
-			self.push_color(color);
-		}
-		self
-	}
-
-	pub fn vec(self) -> Vec<u8> {
-		self.data
-	}
-}
-
-impl From<Vec<u8>> for OutVec {
-	fn from(v: Vec<u8>) -> Self {
-		let mut outvec = Self::new();
-		outvec.push_slice(&v);
-
-		outvec
-	}
-}
\ No newline at end of file