about summary refs log tree commit diff
path: root/gifed/src/block
diff options
context:
space:
mode:
Diffstat (limited to 'gifed/src/block')
-rw-r--r--gifed/src/block/extension/application.rs1
-rw-r--r--gifed/src/block/indexedimage.rs22
-rw-r--r--gifed/src/block/mod.rs2
-rw-r--r--gifed/src/block/palette.rs160
-rw-r--r--gifed/src/block/screendescriptor.rs1
5 files changed, 178 insertions, 8 deletions
diff --git a/gifed/src/block/extension/application.rs b/gifed/src/block/extension/application.rs
index 2244c35..f13047a 100644
--- a/gifed/src/block/extension/application.rs
+++ b/gifed/src/block/extension/application.rs
@@ -1,3 +1,4 @@
+#[derive(Clone, Debug)]
 pub struct Application {
 	pub(crate) identifier: [u8; 8],
 	pub(crate) authentication_code: [u8; 3],
diff --git a/gifed/src/block/indexedimage.rs b/gifed/src/block/indexedimage.rs
index 03c2119..c98e753 100644
--- a/gifed/src/block/indexedimage.rs
+++ b/gifed/src/block/indexedimage.rs
@@ -34,7 +34,7 @@ impl IndexedImage {
 
 	/// The `lzw_code_size` should be None if there is a local color table present. If
 	/// this image is using the Global Color Table, you must provide an
-	/// LZW Minimum Code Size here. It is equal to the value of [Palette::packed_len], but
+	/// LZW Minimum Code Size here. It is equal to the value of [Palette::packed_len] + 1, but
 	/// must also be at least 2.
 	pub fn compress(self, lzw_code_size: Option<u8>) -> Result<CompressedImage, EncodeError> {
 		// gen- The old code had a +1 here. Why?
@@ -46,14 +46,12 @@ impl IndexedImage {
 			Some(palette) => palette.lzw_code_size(),
 			None => match lzw_code_size {
 				None => return Err(EncodeError::InvalidCodeSize { lzw_code_size: 0 }),
-				Some(mcs) => mcs,
+				Some(mcs) => mcs.max(2),
 			},
 		};
 
-		let mcs = if mcs < 2 { 2 } else { mcs };
-
 		//FIXME: gen- This seems  broken
-		//let compressed = LZW::encode(mcs, &self.indicies);
+		//let compressed = crate::LZW::encode(mcs, &self.indicies);
 		let compressed = Encoder::new(weezl::BitOrder::Lsb, mcs)
 			.encode(&self.indicies)
 			.unwrap();
@@ -105,6 +103,11 @@ impl CompressedImage {
 		let mut ret = vec![];
 
 		ret.extend_from_slice(&self.image_descriptor.as_bytes());
+
+		if let Some(palette) = &self.local_color_table {
+			ret.extend_from_slice(&palette.as_bytes());
+		}
+
 		ret.push(self.lzw_code_size);
 
 		for block in &self.blocks {
@@ -128,8 +131,15 @@ impl CompressedImage {
 
 		let data: Vec<u8> = blocks.into_iter().flat_map(<_>::into_iter).collect();
 
+		println!("lzw: {lzw_code_size}");
+
+		if local_color_table.is_some() {
+			let lct = local_color_table.as_ref().unwrap();
+			println!("lct-lzw: {}", lct.lzw_code_size());
+		}
+
 		//TODO: remove unwrap
-		let mut decompressor = weezl::decode::Decoder::new(weezl::BitOrder::Msb, lzw_code_size);
+		let mut decompressor = weezl::decode::Decoder::new(weezl::BitOrder::Lsb, lzw_code_size);
 		let indicies = match decompressor.decode(&data) {
 			Err(LzwError::InvalidCode) => Err(DecodeError::LzwInvalidCode),
 			Ok(o) => Ok(o),
diff --git a/gifed/src/block/mod.rs b/gifed/src/block/mod.rs
index 36cc6fe..3a18a07 100644
--- a/gifed/src/block/mod.rs
+++ b/gifed/src/block/mod.rs
@@ -16,6 +16,7 @@ pub use version::Version;
 use self::extension::Application;
 use self::extension::GraphicControl;
 
+#[derive(Clone, Debug)]
 pub enum Block {
 	CompressedImage(CompressedImage),
 	//TODO: Extension(Extension),
@@ -26,6 +27,7 @@ pub enum Block {
 	LoopingExtension(LoopCount),
 }
 
+#[derive(Clone, Debug)]
 pub enum LoopCount {
 	Forever,
 	Number(u16),
diff --git a/gifed/src/block/palette.rs b/gifed/src/block/palette.rs
index 9c4a5a9..8a06883 100644
--- a/gifed/src/block/palette.rs
+++ b/gifed/src/block/palette.rs
@@ -27,7 +27,8 @@ impl Palette {
 	}
 
 	pub fn lzw_code_size(&self) -> u8 {
-		self.packed_len() + 1
+		let table_log = (self.table.len() as f32).log2() as u8;
+		table_log.max(2)
 	}
 
 	/// Returns the number of colours in the pallette
@@ -66,17 +67,20 @@ impl Palette {
 	/// How many padding bytes we need to write.
 	/// We need to pad the colour table because the size must be a power of two.
 	//TODO: gen- better docs
-	pub fn padding(&self) -> usize {
+	fn padding(&self) -> usize {
 		let comp = self.computed_len();
 		(comp - self.len()) * 3
 	}
 
+	/// The palette with padding if required
 	pub fn as_bytes(&self) -> Vec<u8> {
 		let mut bytes = Vec::with_capacity(self.table.len() * 3);
 		for color in &self.table {
 			bytes.extend_from_slice(&[color.r, color.g, color.b]);
 		}
 
+		bytes.extend(std::iter::repeat(0u8).take(self.padding()));
+
 		bytes
 	}
 }
@@ -101,6 +105,22 @@ impl AsRef<Palette> for Palette {
 	}
 }
 
+impl PartialEq for Palette {
+	fn eq(&self, other: &Self) -> bool {
+		if self.len() != other.len() {
+			return false;
+		}
+
+		for color in &other.table {
+			if !self.table.contains(color) {
+				return false;
+			}
+		}
+
+		true
+	}
+}
+
 //TODO: TryFrom Vec<u8> (must be multiple of 3 len) and From Vec<Color>
 impl TryFrom<&[u8]> for Palette {
 	type Error = ();
@@ -144,3 +164,139 @@ impl TryFrom<Vec<(u8, u8, u8)>> for Palette {
 		}
 	}
 }
+
+#[cfg(test)]
+mod test {
+	use super::*;
+
+	fn vec_tuple_test(vec: Vec<(u8, u8, u8)>, expected: &[u8]) {
+		let plt: Palette = vec.try_into().unwrap();
+		let bytes = plt.as_bytes();
+
+		assert_eq!(expected, bytes.as_slice())
+	}
+
+	#[test]
+	fn writes_one_with_padding() {
+		vec_tuple_test(vec![(1, 2, 3)], &[1, 2, 3, 0, 0, 0])
+	}
+
+	#[test]
+	fn writes_two_without_padding() {
+		vec_tuple_test(vec![(1, 2, 3), (4, 5, 6)], &[1, 2, 3, 4, 5, 6])
+	}
+
+	fn test_n_with_padding(real_count: usize, exected_padding_bytes: usize) {
+		let mut palette = Palette::new();
+		let mut expected = vec![];
+
+		for x in 0..real_count {
+			let x = x as u8;
+			palette.push(Color { r: x, g: x, b: x });
+			expected.extend_from_slice(&[x, x, x])
+		}
+
+		// yes, this is really how I'm doing it. I have... trust issues with
+		// myself and iter::repeat. i hope you understand
+		for _ in 0..exected_padding_bytes {
+			expected.push(0x00);
+		}
+
+		let bytes = palette.as_bytes();
+		assert_eq!(expected, bytes.as_slice())
+	}
+
+	fn test_n_with_padding_range(real_count_low: u8, real_count_high: u8, next_padstop: usize) {
+		for x in real_count_low..=real_count_high {
+			test_n_with_padding(x as usize, (next_padstop as usize - x as usize) * 3)
+		}
+	}
+
+	#[test]
+	fn writes_three_with_padding() {
+		test_n_with_padding(3, 3);
+	}
+
+	#[test]
+	fn writes_four_without_padding() {
+		test_n_with_padding(4, 0);
+	}
+
+	#[test]
+	fn writes_five_to_seven_with_padding() {
+		test_n_with_padding_range(5, 7, 8);
+	}
+
+	#[test]
+	fn writes_eight_without_padding() {
+		test_n_with_padding(8, 0);
+	}
+
+	#[test]
+	fn writes_nine_to_fifteen_with_padding() {
+		test_n_with_padding_range(9, 15, 16);
+	}
+
+	#[test]
+	fn writes_sixteen_without_padding() {
+		test_n_with_padding(16, 0);
+	}
+
+	#[test]
+	fn writes_seventeen_to_thirtyone_with_padding() {
+		test_n_with_padding_range(17, 31, 32);
+	}
+
+	#[test]
+	fn writes_thirtytwo_without_padding() {
+		test_n_with_padding(32, 0);
+	}
+
+	#[test]
+	fn writes_thirtythree_to_sixtythree_with_padding() {
+		test_n_with_padding_range(33, 63, 64);
+	}
+
+	#[test]
+	fn writes_sixtyfour_without_padding() {
+		test_n_with_padding(64, 0);
+	}
+
+	#[test]
+	fn writes_sixtyfive_to_onehundredtwentyseven_with_padding() {
+		test_n_with_padding_range(65, 127, 128);
+	}
+
+	#[test]
+	fn writes_onetwentyeight_without_padding() {
+		test_n_with_padding(128, 0);
+	}
+
+	#[test]
+	fn writes_onetwentynine_to_twofiftyfive_with_padding() {
+		test_n_with_padding_range(129, 255, 256);
+	}
+
+	#[test]
+	fn writes_256_without_padding() {
+		test_n_with_padding(256, 0);
+	}
+
+	#[test]
+	fn packed_len_are_correct() {
+		let black = Color::new(0, 0, 0);
+		let mut palette = Palette::new();
+
+		// Nothing is nothing
+		assert_eq!(0, palette.packed_len());
+
+		// One color is still 0 because the formula is
+		// 2 ^ (len + 1)
+		// which means we should increase at 3
+		palette.push(black);
+		assert_eq!(0, palette.packed_len());
+
+		palette.push(black);
+		assert_eq!(0, palette.packed_len());
+	}
+}
diff --git a/gifed/src/block/screendescriptor.rs b/gifed/src/block/screendescriptor.rs
index d44ca2f..a23bfdd 100644
--- a/gifed/src/block/screendescriptor.rs
+++ b/gifed/src/block/screendescriptor.rs
@@ -2,6 +2,7 @@ use std::convert::TryInto;
 
 use super::{packed::ScreenPacked, Palette};
 
+#[derive(Clone, Debug)]
 pub struct ScreenDescriptor {
 	pub width: u16,
 	pub height: u16,