about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Cargo.toml1
-rw-r--r--README.md4
-rw-r--r--src/block/colortable.rs47
-rw-r--r--src/block/extension/graphiccontrol.rs38
-rw-r--r--src/block/extension/mod.rs7
-rw-r--r--src/block/indexedimage.rs18
-rw-r--r--src/block/mod.rs10
-rw-r--r--src/block/screendescriptor.rs10
-rw-r--r--src/block/version.rs1
-rw-r--r--src/color.rs18
-rw-r--r--src/colorimage.rs16
-rw-r--r--src/gif.rs140
-rw-r--r--src/lib.rs27
-rw-r--r--src/main.rs195
-rw-r--r--src/reader/mod.rs21
-rw-r--r--src/writer/gifbuilder.rs44
-rw-r--r--src/writer/imagebuilder.rs92
-rw-r--r--src/writer/mod.rs2
19 files changed, 351 insertions, 343 deletions
diff --git a/.gitignore b/.gitignore
index 96ef6c0..02f12fc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
 /target
 Cargo.lock
+*.gif
+*.txt
+*.png
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
index ae6962b..e416b18 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,6 +12,7 @@ criterion = "0.3"
 rand = "0.7.3"
 
 [dependencies]
+png = "0.17.1"
 weezl = "0.1.5"
 owo-colors = "2.0.0"
 
diff --git a/README.md b/README.md
index d48b26e..8a76871 100644
--- a/README.md
+++ b/README.md
@@ -8,8 +8,8 @@ always will be, one of the main goals of this crate.
 
 ### TODO
 - [x] Writing GIF87a
-- [ ] Writing GIF89a
-- [ ] Automatically select the lowest version possible when writing
+- [x] Writing GIF89a
+- [x] Automatically select the lowest version possible when writing
 - [ ] Read GIF87a
 - [ ] Read GIF89a
 - [ ] Feature to allow using the [weezl][weezl-crates] crate for LZW compression instead of the built-in
diff --git a/src/block/colortable.rs b/src/block/colortable.rs
index da9458c..d8241f7 100644
--- a/src/block/colortable.rs
+++ b/src/block/colortable.rs
@@ -1,9 +1,11 @@
 pub use crate::Color;
+use crate::EncodingError;
 use std::{
     convert::{TryFrom, TryInto},
     ops::Deref,
 };
 
+#[derive(Clone, Debug)]
 pub struct ColorTable {
     table: Vec<Color>,
 }
@@ -29,6 +31,19 @@ impl ColorTable {
     pub fn push(&mut self, color: Color) {
         self.table.push(color);
     }
+
+    pub fn get(&self, index: u8) -> Option<Color> {
+        self.table.get(index as usize).map(|v| v.clone())
+    }
+
+    pub fn from_color(&self, color: Color) -> Option<u8> {
+        for (i, &c) in self.table.iter().enumerate() {
+            if c == color {
+                return Some(i as u8);
+            }
+        }
+        None
+    }
 }
 
 impl Deref for ColorTable {
@@ -39,12 +54,6 @@ impl Deref for ColorTable {
     }
 }
 
-impl From<Vec<Color>> for ColorTable {
-    fn from(table: Vec<Color>) -> Self {
-        ColorTable { table }
-    }
-}
-
 impl From<&ColorTable> for Box<[u8]> {
     fn from(table: &ColorTable) -> Self {
         let mut vec = vec![];
@@ -80,3 +89,29 @@ impl TryFrom<&[u8]> for ColorTable {
         }
     }
 }
+
+impl TryFrom<Vec<Color>> for ColorTable {
+    type Error = EncodingError;
+
+    fn try_from(value: Vec<Color>) -> Result<Self, Self::Error> {
+        if value.len() > 256 {
+            Err(EncodingError::TooManyColors)
+        } else {
+            Ok(Self { table: value })
+        }
+    }
+}
+
+impl TryFrom<Vec<(u8, u8, u8)>> for ColorTable {
+    type Error = EncodingError;
+
+    fn try_from(value: Vec<(u8, u8, u8)>) -> Result<Self, Self::Error> {
+        if value.len() > 256 {
+            Err(EncodingError::TooManyColors)
+        } else {
+            Ok(Self {
+                table: value.into_iter().map(|c| c.into()).collect(),
+            })
+        }
+    }
+}
diff --git a/src/block/extension/graphiccontrol.rs b/src/block/extension/graphiccontrol.rs
index 0d4edd1..830dff4 100644
--- a/src/block/extension/graphiccontrol.rs
+++ b/src/block/extension/graphiccontrol.rs
@@ -1,4 +1,4 @@
-use std::convert::TryInto;
+use std::{convert::TryInto, fmt};
 
 #[derive(Clone, Debug)]
 pub struct GraphicControl {
@@ -21,22 +21,36 @@ impl GraphicControl {
             transparency_index,
         };
 
-        ret.disposal_method(disposal_method);
+        ret.set_disposal_method(disposal_method);
         ret.user_input(user_input_flag);
         ret.transparency(transparency_flag);
 
         ret
     }
 
-    pub fn disposal_method(&mut self, method: DisposalMethod) {
+    pub fn disposal_method(&self) -> Option<DisposalMethod> {
+        match self.packed & 0b000_111_00 {
+            0b000_000_00 => Some(DisposalMethod::NoAction),
+            0b000_100_00 => Some(DisposalMethod::DoNotDispose),
+            0b000_010_00 => Some(DisposalMethod::RestoreBackground),
+            0b000_110_00 => Some(DisposalMethod::RestorePrevious),
+            _ => None,
+        }
+    }
+
+    pub fn set_disposal_method(&mut self, method: DisposalMethod) {
         match method {
-            DisposalMethod::Clear => self.packed &= 0b111_000_1_1,
+            DisposalMethod::NoAction => self.packed &= 0b111_000_1_1,
             DisposalMethod::DoNotDispose => self.packed |= 0b000_100_0_0,
             DisposalMethod::RestoreBackground => self.packed |= 0b000_010_0_0,
             DisposalMethod::RestorePrevious => self.packed |= 0b000_110_0_0,
         };
     }
 
+    pub fn transparency_index(&self) -> u8 {
+        self.transparency_index
+    }
+
     pub fn user_input(&mut self, flag: bool) {
         if flag {
             self.packed |= 0b000_000_1_0;
@@ -78,9 +92,23 @@ impl From<[u8; 4]> for GraphicControl {
     }
 }
 
+#[derive(Clone, Copy, Debug, PartialEq)]
 pub enum DisposalMethod {
-    Clear,
+    NoAction,
     DoNotDispose,
     RestoreBackground,
     RestorePrevious,
 }
+
+impl fmt::Display for DisposalMethod {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let st = match self {
+            DisposalMethod::NoAction => "Dispose as Normal",
+            DisposalMethod::DoNotDispose => "No Dispose",
+            DisposalMethod::RestoreBackground => "Restore to background",
+            DisposalMethod::RestorePrevious => "Restore previous image",
+        };
+
+        write!(f, "{}", st)
+    }
+}
diff --git a/src/block/extension/mod.rs b/src/block/extension/mod.rs
index d0e57c6..f328357 100644
--- a/src/block/extension/mod.rs
+++ b/src/block/extension/mod.rs
@@ -2,6 +2,7 @@ mod application;
 mod graphiccontrol;
 
 pub use graphiccontrol::{DisposalMethod, GraphicControl};
+use owo_colors::colors::xterm::GrandisCaramel;
 
 pub use self::application::Application;
 
@@ -41,3 +42,9 @@ impl From<&Extension> for Box<[u8]> {
         vec.into_boxed_slice()
     }
 }
+
+impl From<GraphicControl> for Extension {
+    fn from(gce: GraphicControl) -> Self {
+        Extension::GraphicControl(gce)
+    }
+}
diff --git a/src/block/indexedimage.rs b/src/block/indexedimage.rs
index 52be3d5..0be066f 100644
--- a/src/block/indexedimage.rs
+++ b/src/block/indexedimage.rs
@@ -10,6 +10,22 @@ pub struct IndexedImage {
 }
 
 impl IndexedImage {
+    pub fn left(&self) -> u16 {
+        self.image_descriptor.left
+    }
+
+    pub fn top(&self) -> u16 {
+        self.image_descriptor.left
+    }
+
+    pub fn width(&self) -> u16 {
+        self.image_descriptor.width
+    }
+
+    pub fn height(&self) -> u16 {
+        self.image_descriptor.height
+    }
+
     pub fn as_boxed_slice(&self, minimum_code_size: u8) -> Box<[u8]> {
         let mut out = vec![];
 
@@ -46,7 +62,7 @@ impl IndexedImage {
     }
 }
 
-pub struct BlockedImage {
+pub struct CompressedImage {
     pub image_descriptor: ImageDescriptor,
     pub local_color_table: Option<ColorTable>,
     pub lzw_minimum_code_size: u8,
diff --git a/src/block/mod.rs b/src/block/mod.rs
index e3136bf..645c11d 100644
--- a/src/block/mod.rs
+++ b/src/block/mod.rs
@@ -7,13 +7,19 @@ mod version;
 
 pub use colortable::ColorTable;
 pub use imagedescriptor::ImageDescriptor;
-pub use indexedimage::BlockedImage;
+pub use indexedimage::CompressedImage;
 pub use indexedimage::IndexedImage;
 pub use screendescriptor::ScreenDescriptor;
 pub use version::Version;
 
+use crate::writer::ImageBuilder;
+
 pub enum Block {
     IndexedImage(IndexedImage),
-    BlockedImage(BlockedImage),
     Extension(extension::Extension),
 }
+
+enum WriteBlock {
+    ImageBuilder(ImageBuilder),
+    Block(Block),
+}
diff --git a/src/block/screendescriptor.rs b/src/block/screendescriptor.rs
index ff70896..444b44f 100644
--- a/src/block/screendescriptor.rs
+++ b/src/block/screendescriptor.rs
@@ -9,6 +9,16 @@ pub struct ScreenDescriptor {
 }
 
 impl ScreenDescriptor {
+    pub fn new(width: u16, height: u16) -> Self {
+        Self {
+            width,
+            height,
+            packed: 0,
+            background_color_index: 0,
+            pixel_aspect_ratio: 0,
+        }
+    }
+
     pub fn set_color_table_present(&mut self, is_present: bool) {
         if is_present {
             self.packed |= 0b1000_0000;
diff --git a/src/block/version.rs b/src/block/version.rs
index b785f27..c52439c 100644
--- a/src/block/version.rs
+++ b/src/block/version.rs
@@ -1,5 +1,6 @@
 use std::fmt;
 
+#[derive(Clone, Copy, Debug, PartialEq)]
 pub enum Version {
     Gif87a,
     Gif89a,
diff --git a/src/color.rs b/src/color.rs
index 764acaf..dd96280 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -1,4 +1,4 @@
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub struct Color {
     pub r: u8,
     pub g: u8,
@@ -20,3 +20,19 @@ impl From<[u8; 3]> for Color {
         }
     }
 }
+
+impl From<(u8, u8, u8)> for Color {
+    fn from(t: (u8, u8, u8)) -> Self {
+        Self {
+            r: t.0,
+            g: t.1,
+            b: t.2,
+        }
+    }
+}
+
+impl Into<[u8; 3]> for Color {
+    fn into(self) -> [u8; 3] {
+        [self.r, self.g, self.b]
+    }
+}
diff --git a/src/colorimage.rs b/src/colorimage.rs
new file mode 100644
index 0000000..71f2bee
--- /dev/null
+++ b/src/colorimage.rs
@@ -0,0 +1,16 @@
+pub struct ColorImage {
+	width: u16,
+	height: u16,
+	data: Vec<Pixel>
+}
+
+impl ColorImage {
+	pub fn new() {
+		
+	}
+}
+
+pub enum Pixel {
+	Color(Color),
+	Transparent
+}
\ No newline at end of file
diff --git a/src/gif.rs b/src/gif.rs
index 90564fc..837de3a 100644
--- a/src/gif.rs
+++ b/src/gif.rs
@@ -1,4 +1,4 @@
-use crate::block::{extension::Extension, Block, ColorTable, ScreenDescriptor, Version};
+use crate::{block::{extension::Extension, Block, ColorTable, ScreenDescriptor, Version}, writer::GifBuilder};
 pub struct Gif {
     pub header: Version,
     pub screen_descriptor: ScreenDescriptor,
@@ -7,6 +7,10 @@ pub struct Gif {
 }
 
 impl Gif {
+    pub fn builder(width: u16, height: u16) -> GifBuilder {
+        GifBuilder::new(width, height)
+    }
+
     pub fn to_vec(&self) -> Vec<u8> {
         let mut out = vec![];
 
@@ -32,7 +36,7 @@ impl Gif {
                     boxed = image.as_boxed_slice(mcs);
                     out.extend_from_slice(&*boxed);
                 }
-                Block::BlockedImage(_) => unreachable!(),
+                //Block::BlockedImage(_) => unreachable!(),
                 Block::Extension(ext) => {
                     boxed = ext.into();
                     out.extend_from_slice(&*boxed);
@@ -47,10 +51,24 @@ impl Gif {
     }
 }
 
+/*struct FrameIterator {
+
+}
+
+impl Iterator for FrameIterator {
+    type Item = ();
+
+    fn next(&mut self) -> Option<Self::Item> {
+        todo!()
+    }
+}*/
+
 #[cfg(test)]
 pub mod gif {
-    use super::*;
-    use crate::block::extension::{DisposalMethod, GraphicControl};
+    use std::convert::TryInto;
+    use std::io::Write;
+
+    use crate::block::extension::DisposalMethod;
     use crate::writer::{GifBuilder, ImageBuilder};
     use crate::Color;
 
@@ -125,18 +143,19 @@ pub mod gif {
             0x3B, // Trailer
         ];
 
-        let actual_out = GifBuilder::new(Version::Gif87a, 4, 4)
-            .global_color_table(gct.into())
+        let actual = GifBuilder::new(4, 4)
+            .palette(gct.try_into().unwrap())
             .image(
                 ImageBuilder::new(4, 4)
-                    .color_table(colortable.into())
+                    .palette(colortable.try_into().unwrap())
                     .indicies(indicies.clone()),
             )
+            .unwrap()
             .image(ImageBuilder::new(4, 4).indicies(indicies))
-            .build()
-            .to_vec();
+            .unwrap();
 
-        assert_eq!(actual_out, expected_out);
+        let bytes = actual.build().to_vec();
+        assert_eq!(bytes, expected_out);
     }
 
     #[test]
@@ -146,96 +165,35 @@ pub mod gif {
         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
-            0x21,
-            0xF9,
-            0x04,
-            0b000_010_0_0,
-            0x40,
-            0x00,
-            0x00,
-            0x00, // Graphic Control Extension
-            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
+            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(Version::Gif87a, 4, 4)
-            .global_color_table(gct.into())
+        let actual_out = GifBuilder::new(4, 4)
+            .palette(gct.try_into().unwrap())
             .image(
                 ImageBuilder::new(4, 4)
-                    .color_table(colortable.into())
-                    .indicies(indicies.clone()),
+                    .palette(colortable.try_into().unwrap())
+                    .indicies(indicies.clone())
+                    .disposal_method(DisposalMethod::RestoreBackground)
+                    .delay(64),
             )
-            .extension(Extension::GraphicControl(GraphicControl::new(
-                DisposalMethod::RestoreBackground,
-                false,
-                false,
-                64,
-                0,
-            )))
+            .unwrap()
             .image(ImageBuilder::new(4, 4).indicies(indicies))
+            .unwrap()
             .build()
             .to_vec();
 
+        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/src/lib.rs b/src/lib.rs
index 6c0203c..97bab10 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,6 +6,9 @@ pub mod block;
 pub mod reader;
 pub mod writer;
 
+use core::fmt;
+use std::error::Error;
+
 pub use color::Color;
 pub use gif::Gif;
 pub use lzw::LZW;
@@ -20,3 +23,27 @@ pub(crate) fn packed_to_color_table_length(packed: u8) -> usize {
 //in 87a aren't set if version is 87a, or that we return a warning, etc. Just
 //remember about this.
 //bottom of page 24 in 89a
+
+#[derive(Clone, Copy, Debug)]
+pub enum EncodingError {
+    TooManyColors,
+    NoColorTable,
+    IndicieSizeMismatch { expected: usize, got: usize },
+}
+
+impl fmt::Display for EncodingError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::TooManyColors => write!(f, "A palette is limited to 256 colors"),
+            Self::NoColorTable => write!(
+                f,
+                "Refusing to set the background color index when no color table is set!"
+            ),
+            Self::IndicieSizeMismatch { expected, got } => {
+                write!(f, "Expected to have {} indicies but got {}", expected, got)
+            }
+        }
+    }
+}
+
+impl Error for EncodingError {}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index 269470a..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,195 +0,0 @@
-use std::{fs::File, io::Write};
-
-use gifed::{
-    block::{
-        extension::{Extension, GraphicControl},
-        BlockedImage, ColorTable, Version,
-    },
-    reader::GifReader,
-    writer::{GifBuilder, ImageBuilder},
-    Color,
-};
-use owo_colors::OwoColorize;
-
-fn main() {
-    write_test();
-    //return;
-    let gif = GifReader::file("test.gif");
-    //let gif = GifReader::file("/home/gen/Downloads/tas_tillie.gif");
-
-    println!("Version {}", gif.header.yellow());
-    println!(
-        "Logical Screen Descriptor\n\tDimensions {}x{}",
-        gif.screen_descriptor.width.yellow(),
-        gif.screen_descriptor.height.yellow()
-    );
-
-    if gif.screen_descriptor.color_table_present() {
-        println!(
-            "\tGlobal Color Table Present {}\n\tGlobal Color Table Size {}",
-            "Yes".green(),
-            gif.screen_descriptor.color_table_len().green()
-        );
-    } else {
-        println!(
-            "\tGlobal Color Table Present {}\n\tGlobal Color Table Size {}",
-            "No".red(),
-            gif.screen_descriptor.color_table_len().red()
-        );
-    }
-
-    for block in gif.blocks {
-        match block {
-            gifed::block::Block::IndexedImage(_) => todo!(),
-            gifed::block::Block::BlockedImage(bli) => describe_blocked_image(bli),
-            gifed::block::Block::Extension(ext) => match ext {
-                gifed::block::extension::Extension::GraphicControl(gce) => {
-                    println!(
-                        "Graphic Control Extension\n\tDelay Time {}",
-                        format!("{}s", gce.delay_time() as f32 / 100.0).yellow()
-                    )
-                }
-                gifed::block::extension::Extension::Looping(_) => todo!(),
-                gifed::block::extension::Extension::Comment(cmt) => {
-                    println!("Comment Extension\n\tLength {}", cmt.len())
-                }
-                gifed::block::extension::Extension::Application(app) => {
-                    let auth = app.authentication_code();
-                    println!("Application Extension\n\tIdentifier {}\n\tAuthentication {:02X} {:02X} {:02X}",app.identifier().yellow(), auth[0].yellow(), auth[1].yellow(), auth[2].yellow());
-                }
-            },
-        }
-    }
-}
-
-fn describe_blocked_image(bli: BlockedImage) {
-    println!(
-        "Image\n\tOffset {}x{}\n\tDimensions {}x{}",
-        bli.image_descriptor.left.yellow(),
-        bli.image_descriptor.top.yellow(),
-        bli.image_descriptor.width.yellow(),
-        bli.image_descriptor.height.yellow(),
-    );
-
-    if bli.image_descriptor.color_table_present() {
-        println!(
-            "\tLocal Color Table Present {}\n\tLocal Color Table Size {}",
-            "Yes".green(),
-            bli.image_descriptor.color_table_size().green()
-        );
-    } else {
-        println!(
-            "\tLocal Color Table Present {}\n\tLocal Color Table Size {}",
-            "No".red(),
-            bli.image_descriptor.color_table_size().red()
-        );
-    }
-
-    println!(
-        "\t{} image data sub-blocks totaling {} bytes",
-        bli.blocks.len().yellow(),
-        bli.blocks
-            .iter()
-            .map(|vec| vec.len())
-            .sum::<usize>()
-            .yellow()
-    )
-}
-
-fn write_test() {
-    const size: u16 = 256;
-    let gcon = GraphicControl::new(
-        gifed::block::extension::DisposalMethod::Clear,
-        false,
-        false,
-        25,
-        0,
-    );
-
-    let mut gct = ColorTable::new();
-    gct.push(Color {
-        r: 0x44,
-        g: 0x44,
-        b: 0x44,
-    });
-    gct.push(Color {
-        r: 0x88,
-        g: 0x44,
-        b: 0xDD,
-    });
-
-    println!("{} {}", gct.packed_len(), gct.len());
-
-    let mut builder = GifBuilder::new(Version::Gif89a, size, size).global_color_table(gct);
-
-    for x in 4..16 {
-        let mut raw = vec![0; size as usize * size as usize];
-
-        for i in x * 7..x * 7 + 32 {
-            for j in x * 7..x * 7 + 32 {
-                let index = i * size as usize + j;
-                raw[index as usize] = 1;
-            }
-        }
-
-        builder = builder
-            .extension(Extension::GraphicControl(gcon.clone()))
-            .image(ImageBuilder::new(size, size).indicies(raw));
-    }
-
-    builder = builder.extension(Extension::Looping(0));
-
-    let vec = builder.build().to_vec();
-    File::create("test.gif").unwrap().write_all(&vec).unwrap();
-
-    let mut text_gif = File::create("test_gif.txt").unwrap();
-    for byte in vec {
-        text_gif
-            .write_all(&format!("{:02X} ", byte).as_bytes())
-            .unwrap();
-    }
-}
-
-fn write_static_test() {
-    const size: u16 = 256;
-
-    let mut gct = ColorTable::new();
-    gct.push(Color {
-        r: 0x44,
-        g: 0x44,
-        b: 0x44,
-    });
-    gct.push(Color {
-        r: 0x55,
-        g: 0x44,
-        b: 0x88,
-    });
-
-    println!("{} {}", gct.packed_len(), gct.len());
-
-    let mut builder = GifBuilder::new(Version::Gif87a, size, size).global_color_table(gct);
-
-    let x = 4;
-    let mut raw = vec![0; size as usize * size as usize];
-
-    for i in x * 7..x * 7 + 8 {
-        for j in x * 7..x * 7 + 8 {
-            let index = i * size as usize + j;
-            raw[index as usize] = 1;
-        }
-    }
-
-    builder = builder.image(ImageBuilder::new(size, size).indicies(raw));
-
-    //builder = builder.extension(Extension::Looping(0));
-
-    let vec = builder.build().to_vec();
-    File::create("test.gif").unwrap().write_all(&vec).unwrap();
-
-    let mut text_gif = File::create("test_gif.txt").unwrap();
-    for byte in vec {
-        text_gif
-            .write_all(&format!("{:02X} ", byte).as_bytes())
-            .unwrap();
-    }
-}
diff --git a/src/reader/mod.rs b/src/reader/mod.rs
index 606de11..7653216 100644
--- a/src/reader/mod.rs
+++ b/src/reader/mod.rs
@@ -9,9 +9,10 @@ use std::{
 use crate::{
     block::{
         extension::{Application, Extension, GraphicControl},
-        Block, BlockedImage, ColorTable, ImageDescriptor, ScreenDescriptor, Version,
+        Block, ColorTable, CompressedImage, ImageDescriptor, IndexedImage, ScreenDescriptor,
+        Version,
     },
-    Gif,
+    color, Gif,
 };
 
 pub struct GifReader {}
@@ -100,8 +101,8 @@ impl GifReader {
         }
     }
 
-    fn read_extension(mut reader: &mut SmartReader) -> Block {
-        let mut extension_id = reader.u8().expect("File ended early");
+    fn read_extension(reader: &mut SmartReader) -> Block {
+        let extension_id = reader.u8().expect("File ended early");
 
         match extension_id {
             0xF9 => {
@@ -149,13 +150,17 @@ impl GifReader {
 
         let lzw_csize = reader.u8().expect("Failed to read LZW Minimum Code Size");
 
-        let blocks = reader.take_data_subblocks();
+        let compressed_data = reader.take_and_collapse_subblocks();
+        println!("c{}", compressed_data.len());
 
-        Block::BlockedImage(BlockedImage {
+        let mut decompress = weezl::decode::Decoder::new(weezl::BitOrder::Lsb, lzw_csize);
+        //TODO: remove unwrap
+        let mut decompressed_data = decompress.decode(&compressed_data).unwrap();
+
+        Block::IndexedImage(IndexedImage {
             image_descriptor: descriptor,
             local_color_table: color_table,
-            lzw_minimum_code_size: lzw_csize,
-            blocks,
+            indicies: decompressed_data,
         })
     }
 }
diff --git a/src/writer/gifbuilder.rs b/src/writer/gifbuilder.rs
index d16be56..ec3c304 100644
--- a/src/writer/gifbuilder.rs
+++ b/src/writer/gifbuilder.rs
@@ -1,6 +1,8 @@
+use std::convert::TryInto;
+
 use crate::block::{extension::Extension, Block, ColorTable, ScreenDescriptor, Version};
 use crate::writer::ImageBuilder;
-use crate::Gif;
+use crate::{EncodingError, Gif};
 
 pub struct GifBuilder {
     version: Version,
@@ -12,9 +14,9 @@ pub struct GifBuilder {
 }
 
 impl GifBuilder {
-    pub fn new(version: Version, width: u16, height: u16) -> Self {
+    pub fn new(width: u16, height: u16) -> Self {
         Self {
-            version,
+            version: Version::Gif87a,
             width,
             height,
             background_color_index: 0,
@@ -23,30 +25,40 @@ impl GifBuilder {
         }
     }
 
-    pub fn global_color_table(mut self, table: ColorTable) -> Self {
-        self.global_color_table = Some(table);
-
+    pub fn palette(mut self, palette: ColorTable) -> Self {
+        self.global_color_table = Some(palette);
         self
     }
 
-    pub fn background_color_index(mut self, ind: u8) -> Self {
+    pub fn background_index(mut self, ind: u8) -> Result<Self, EncodingError> {
         if self.global_color_table.is_none() {
-            //TODO: Throw error or let it go by, who knows
-            panic!("Setting background color index with noGCT!");
+            Err(EncodingError::NoColorTable)
+        } else {
+            self.background_color_index = ind;
+            Ok(self)
         }
-
-        self.background_color_index = ind;
-        self
     }
 
-    pub fn image(mut self, ib: ImageBuilder) -> Self {
-        self.blocks.push(Block::IndexedImage(ib.build()));
-        self
+    pub fn image(mut self, ib: ImageBuilder) -> Result<Self, EncodingError> {
+        if ib.required_version() == Version::Gif89a {
+            self.version = Version::Gif89a;
+        }
+
+        if let Some(gce) = ib.get_graphic_control() {
+            self.blocks.push(Block::Extension(gce.into()));
+        }
+
+        self.blocks.push(Block::IndexedImage(ib.build()?));
+        Ok(self)
     }
 
-    pub fn extension(mut self, ext: Extension) -> Self {
+    /*pub fn extension(mut self, ext: Extension) -> Self {
         self.blocks.push(Block::Extension(ext));
         self
+    }*/
+
+    pub fn repeat(&mut self, count: u16) {
+        self.blocks.push(Block::Extension(Extension::Looping(count)))
     }
 
     pub fn build(self) -> Gif {
diff --git a/src/writer/imagebuilder.rs b/src/writer/imagebuilder.rs
index d38687e..1486f16 100644
--- a/src/writer/imagebuilder.rs
+++ b/src/writer/imagebuilder.rs
@@ -1,4 +1,10 @@
-use crate::block::{ColorTable, ImageDescriptor, IndexedImage};
+use crate::{
+    block::{
+        extension::{DisposalMethod, GraphicControl},
+        ColorTable, ImageDescriptor, IndexedImage, Version,
+    },
+    EncodingError,
+};
 
 pub struct ImageBuilder {
     left_offset: u16,
@@ -6,6 +12,11 @@ pub struct ImageBuilder {
     width: u16,
     height: u16,
     color_table: Option<ColorTable>,
+
+    delay: u16,
+    disposal_method: DisposalMethod,
+    transparent_index: Option<u8>,
+
     indicies: Vec<u8>,
 }
 
@@ -17,38 +28,89 @@ impl ImageBuilder {
             width,
             height,
             color_table: None,
+            delay: 0,
+            disposal_method: DisposalMethod::NoAction,
+            transparent_index: None,
             indicies: vec![],
         }
     }
 
-    pub fn offsets(mut self, left_offset: u16, top_offset: u16) -> Self {
-        self.left_offset = left_offset;
-        self.top_offset = top_offset;
+    pub fn offset(mut self, left: u16, top: u16) -> Self {
+        self.left_offset = left;
+        self.top_offset = top;
         self
     }
 
-    pub fn left_offset(mut self, offset: u16) -> Self {
-        self.left_offset = offset;
+    pub fn palette(mut self, table: ColorTable) -> Self {
+        self.color_table = Some(table);
         self
     }
 
-    pub fn top_offset(mut self, offset: u16) -> Self {
-        self.top_offset = offset;
+    /// Time to wait, in hundreths of a second, before this image is drawn
+    pub fn delay(mut self, hundreths: u16) -> Self {
+        self.delay = hundreths;
         self
     }
 
-    pub fn color_table(mut self, table: ColorTable) -> Self {
-        self.color_table = Some(table);
+    pub fn disposal_method(mut self, method: DisposalMethod) -> Self {
+        self.disposal_method = method;
+        self
+    }
 
+    pub fn transparent_index(mut self, index: Option<u8>) -> Self {
+        self.transparent_index = index;
         self
     }
 
-    pub fn indicies(mut self, vec: Vec<u8>) -> Self {
-        self.indicies = vec;
+    pub fn required_version(&self) -> Version {
+        if self.delay > 0
+            || self.disposal_method != DisposalMethod::NoAction
+            || self.transparent_index.is_some()
+        {
+            Version::Gif89a
+        } else {
+            Version::Gif87a
+        }
+    }
+
+    pub fn get_graphic_control(&self) -> Option<GraphicControl> {
+        if self.required_version() == Version::Gif89a {
+            if let Some(transindex) = self.transparent_index {
+                Some(GraphicControl::new(
+                    self.disposal_method,
+                    false,
+                    true,
+                    self.delay,
+                    transindex,
+                ))
+            } else {
+                Some(GraphicControl::new(
+                    self.disposal_method,
+                    false,
+                    false,
+                    self.delay,
+                    0,
+                ))
+            }
+        } else {
+            None
+        }
+    }
+
+    pub fn indicies(mut self, indicies: Vec<u8>) -> Self {
+        self.indicies = indicies;
         self
     }
 
-    pub fn build(self) -> IndexedImage {
+    pub fn build(self) -> Result<IndexedImage, EncodingError> {
+        let expected_len = self.width as usize * self.height as usize;
+        if self.indicies.len() != expected_len {
+            return Err(EncodingError::IndicieSizeMismatch {
+                expected: expected_len,
+                got: self.indicies.len(),
+            });
+        }
+
         let mut imgdesc = ImageDescriptor {
             left: self.left_offset,
             top: self.top_offset,
@@ -62,10 +124,10 @@ impl ImageBuilder {
             imgdesc.set_color_table_size(lct.packed_len());
         }
 
-        IndexedImage {
+        Ok(IndexedImage {
             image_descriptor: imgdesc,
             local_color_table: self.color_table,
             indicies: self.indicies,
-        }
+        })
     }
 }
diff --git a/src/writer/mod.rs b/src/writer/mod.rs
index b801a3a..88311fc 100644
--- a/src/writer/mod.rs
+++ b/src/writer/mod.rs
@@ -2,4 +2,4 @@ mod gifbuilder;
 mod imagebuilder;
 
 pub use gifbuilder::GifBuilder;
-pub use imagebuilder::ImageBuilder;
\ No newline at end of file
+pub use imagebuilder::ImageBuilder;