diff options
Diffstat (limited to 'gifed/src/block')
-rw-r--r-- | gifed/src/block/extension/application.rs | 1 | ||||
-rw-r--r-- | gifed/src/block/indexedimage.rs | 22 | ||||
-rw-r--r-- | gifed/src/block/mod.rs | 2 | ||||
-rw-r--r-- | gifed/src/block/palette.rs | 160 | ||||
-rw-r--r-- | gifed/src/block/screendescriptor.rs | 1 |
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, |