about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Cargo.toml9
-rw-r--r--src/common/mod.rs20
-rw-r--r--src/lib.rs5
-rw-r--r--src/writer/gifbuilder.rs238
-rw-r--r--src/writer/imagebuilder.rs196
-rw-r--r--src/writer/lzw.rs175
-rw-r--r--src/writer/mod.rs9
-rw-r--r--src/writer/outvec.rs64
9 files changed, 718 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..96ef6c0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..dc07e9a
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "gifed"
+version = "0.1.0"
+authors = ["Brad Alfirevic <brad@genbyte.dev>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/common/mod.rs b/src/common/mod.rs
new file mode 100644
index 0000000..1080e29
--- /dev/null
+++ b/src/common/mod.rs
@@ -0,0 +1,20 @@
+pub enum Version {
+	Gif87a,
+	Gif89a
+}
+
+pub struct Color {
+	pub r: u8,
+	pub g: u8,
+	pub b: u8
+}
+
+impl Color {
+	pub fn new(r: u8, g: u8, b: u8) -> Self {
+		Self {
+			r,
+			g,
+			b
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..187decb
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,5 @@
+mod common;
+mod writer;
+
+pub use common::Version;
+pub use writer::GifBuilder;
\ No newline at end of file
diff --git a/src/writer/gifbuilder.rs b/src/writer/gifbuilder.rs
new file mode 100644
index 0000000..ff98ba7
--- /dev/null
+++ b/src/writer/gifbuilder.rs
@@ -0,0 +1,238 @@
+use crate::common::{Color, Version};
+use super::OutVec;
+use super::ImageBuilder;
+
+pub struct GifBuilder {
+	version: Version,
+	width: u16,
+	height: u16,
+	global_color_table: Option<Vec<Color>>,
+	background_color_index: u8,
+	imagebuilders: Vec<ImageBuilder>
+}
+
+impl GifBuilder {
+	pub fn new(version: Version, width: u16, height: u16) -> Self {
+		Self {
+			version,
+			width,
+			height,
+			global_color_table: None,
+			background_color_index: 0,
+			imagebuilders: vec![]
+		}
+	}
+
+	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);
+
+		self
+	}
+
+	pub fn background_color_index(mut self, ind: u8) -> Self {
+		if self.global_color_table.is_none() {
+			//TODO: Throw error or let it go by, who knows
+			panic!("Setting background color index with noGCT!");
+		}
+
+		self.background_color_index = ind;
+		self
+	}
+
+	pub fn image(mut self, ib: ImageBuilder) -> Self {
+		self.imagebuilders.push(ib);
+		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")
+		};
+
+		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;
+		}
+		//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);
+		}
+
+		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);
+		}
+
+		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
new file mode 100644
index 0000000..cd96908
--- /dev/null
+++ b/src/writer/imagebuilder.rs
@@ -0,0 +1,196 @@
+use crate::common::Color;
+use super::OutVec;
+use super::LZW;
+
+pub struct ImageBuilder {
+	left_offset: u16,
+	top_offset: u16,
+	width: u16,
+	height: u16,
+	color_table: Option<Vec<Color>>,
+	indicies: Vec<u8>
+}
+
+impl ImageBuilder {
+	pub fn new(width: u16, height: u16) -> Self {
+		Self {
+			left_offset: 0,
+			top_offset: 0,
+			width,
+			height,
+			color_table: 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;
+		self
+	}
+
+	pub fn left_offset(mut self, offset: u16) -> Self {
+		self.left_offset = offset;
+		self
+	}
+
+	pub fn top_offset(mut self, offset: u16) -> Self {
+		self.top_offset = offset;
+		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);
+
+		self
+	}
+
+	pub fn indicies(mut self, vec: Vec<u8>) -> Self {
+		self.indicies = vec;
+		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;
+		}
+		//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);
+		}
+
+		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
new file mode 100644
index 0000000..1970617
--- /dev/null
+++ b/src/writer/lzw.rs
@@ -0,0 +1,175 @@
+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
new file mode 100644
index 0000000..965dcfa
--- /dev/null
+++ b/src/writer/mod.rs
@@ -0,0 +1,9 @@
+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
new file mode 100644
index 0000000..eb04934
--- /dev/null
+++ b/src/writer/outvec.rs
@@ -0,0 +1,64 @@
+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