about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGenevieve Alfirevic <gen@nyble.dev>2022-02-14 14:20:29 -0600
committerGenevieve Alfirevic <gen@nyble.dev>2022-02-14 14:20:29 -0600
commit8e57ef9b5042acb6becf4e6e2658505b84fb9974 (patch)
tree5a8005955d6d39c741dee722e82862007eb5646d
parentd9aa9806d32cf717b3d22aae0514478d103f4aa8 (diff)
downloadgifed-8e57ef9b5042acb6becf4e6e2658505b84fb9974.tar.gz
gifed-8e57ef9b5042acb6becf4e6e2658505b84fb9974.zip
Image iterator and rgb/rgba image structs
-rw-r--r--gifed/src/block/extension/graphiccontrol.rs4
-rw-r--r--gifed/src/color.rs9
-rw-r--r--gifed/src/colorimage.rs160
-rw-r--r--gifed/src/gif.rs103
-rw-r--r--gifed/src/lib.rs2
5 files changed, 203 insertions, 75 deletions
diff --git a/gifed/src/block/extension/graphiccontrol.rs b/gifed/src/block/extension/graphiccontrol.rs
index e9662b6..04086a6 100644
--- a/gifed/src/block/extension/graphiccontrol.rs
+++ b/gifed/src/block/extension/graphiccontrol.rs
@@ -95,6 +95,10 @@ impl GraphicControl {
 	pub fn delay_mut(&mut self) -> &mut u16 {
 		&mut self.delay
 	}
+
+	pub fn user_input(&self) -> bool {
+		self.packed.user_input()
+	}
 }
 
 impl From<[u8; 4]> for GraphicControl {
diff --git a/gifed/src/color.rs b/gifed/src/color.rs
index e18ce58..29e7feb 100644
--- a/gifed/src/color.rs
+++ b/gifed/src/color.rs
@@ -36,3 +36,12 @@ impl Into<[u8; 3]> for Color {
 		[self.r, self.g, self.b]
 	}
 }
+
+pub type Rgb = Color;
+
+pub struct Rgba {
+	pub r: u8,
+	pub g: u8,
+	pub b: u8,
+	pub a: u8,
+}
diff --git a/gifed/src/colorimage.rs b/gifed/src/colorimage.rs
index c9cae79..c908081 100644
--- a/gifed/src/colorimage.rs
+++ b/gifed/src/colorimage.rs
@@ -1,52 +1,70 @@
-use std::convert::TryFrom;
+use std::{
+	convert::TryFrom,
+	ops::{Deref, DerefMut, Index},
+};
 
-use crate::{block::ColorTable, gif::Image, reader::DecodingError, Color};
+use crate::{block::ColorTable, color::Rgb, gif::Image, reader::DecodingError, Color};
 
-pub struct ColorImage {
-	width: u16,
-	height: u16,
-	data: Vec<Pixel>,
+/// An RGBA, full color image
+pub struct RgbaImage {
+	pub width: u16,
+	pub height: u16,
+	pub top: u16,
+	pub left: u16,
+	pub data: Vec<u8>,
 }
 
-impl ColorImage {
+impl RgbaImage {
 	pub(crate) fn from_indicies(
 		width: u16,
 		height: u16,
+		top: u16,
+		left: u16,
 		indicies: &[u8],
 		table: &ColorTable,
 		transindex: Option<u8>,
 	) -> Result<Self, DecodingError> {
-		let mut data = vec![Pixel::Transparent; (width * height) as usize];
+		let mut data = vec![0; width as usize * height as usize * 4];
 
 		for (image_index, color_index) in indicies.into_iter().enumerate() {
 			if let Some(trans) = transindex {
 				if trans == *color_index {
-					data[image_index] = Pixel::Transparent;
+					data[image_index * 4] = 0;
+					data[image_index * 4 + 1] = 0;
+					data[image_index * 4 + 2] = 0;
+					data[image_index * 4 + 3] = 0;
 				}
 			} else {
-				data[image_index] = Pixel::Color(
-					table
-						.get(*color_index)
-						.ok_or(DecodingError::ColorIndexOutOfBounds)?,
-				);
+				let color = table
+					.get(*color_index)
+					.ok_or(DecodingError::ColorIndexOutOfBounds)?;
+
+				data[image_index * 4] = color.r;
+				data[image_index * 4 + 1] = color.g;
+				data[image_index * 4 + 2] = color.b;
+				data[image_index * 4 + 3] = 255;
 			}
 		}
 
-		Ok(ColorImage {
+		Ok(Self {
 			width,
 			height,
+			top,
+			left,
 			data,
 		})
 	}
 }
 
-impl<'a> TryFrom<Image<'a>> for ColorImage {
+impl<'a> TryFrom<&Image<'a>> for RgbaImage {
 	type Error = DecodingError;
 
-	fn try_from(img: Image<'a>) -> Result<Self, Self::Error> {
-		ColorImage::from_indicies(
+	fn try_from(img: &Image<'a>) -> Result<Self, Self::Error> {
+		RgbaImage::from_indicies(
 			img.width,
 			img.height,
+			img.top_offset,
+			img.left_offset,
 			img.indicies,
 			img.palette,
 			img.transparent_index(),
@@ -54,8 +72,106 @@ impl<'a> TryFrom<Image<'a>> for ColorImage {
 	}
 }
 
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum Pixel {
-	Color(Color),
-	Transparent,
+impl From<RgbImage> for RgbaImage {
+	fn from(rgb: RgbImage) -> Self {
+		let RgbImage {
+			width,
+			height,
+			top,
+			left,
+			mut data,
+		} = rgb;
+
+		// Extend the data vector to fit the alpha values
+		data.extend(std::iter::repeat(0).take(width as usize * height as usize));
+
+		// Work backwards to fill in the alpha values.
+		for idx in (0..width as usize * height as usize).rev() {
+			data[idx * 4] = data[idx * 3];
+			data[idx * 4 + 1] = data[idx * 3 + 1];
+			data[idx * 4 + 2] = data[idx * 3 + 2];
+			data[idx * 4 + 3] = 255;
+		}
+
+		Self {
+			width,
+			height,
+			top,
+			left,
+			data,
+		}
+	}
+}
+
+impl Deref for RgbaImage {
+	type Target = [u8];
+
+	fn deref(&self) -> &Self::Target {
+		&self.data
+	}
+}
+
+impl DerefMut for RgbaImage {
+	fn deref_mut(&mut self) -> &mut Self::Target {
+		self.data.as_mut_slice()
+	}
+}
+
+/// An RGB, full color image
+pub struct RgbImage {
+	pub width: u16,
+	pub height: u16,
+	pub top: u16,
+	pub left: u16,
+	pub data: Vec<u8>,
+}
+
+impl RgbImage {
+	pub fn from_image<'a>(
+		image: &Image<'a>,
+		transparent_replace: Color,
+	) -> Result<Self, DecodingError> {
+		let mut data = vec![0; image.indicies.len() * 3];
+
+		for (image_idx, &color_idx) in image.indicies.iter().enumerate() {
+			match image.transparent_index() {
+				Some(trans) if trans == color_idx => {
+					data[image_idx as usize * 4] = transparent_replace.r;
+					data[image_idx * 3 + 1] = transparent_replace.g;
+					data[image_idx * 3 + 2] = transparent_replace.b;
+				}
+				_ => {
+					if let Some(color) = image.palette.get(color_idx) {
+						data[image_idx * 3] = color.r;
+						data[image_idx * 3 + 1] = color.g;
+						data[image_idx * 3 + 2] = color.b;
+					} else {
+						return Err(DecodingError::ColorIndexOutOfBounds);
+					}
+				}
+			}
+		}
+
+		Ok(Self {
+			width: image.width,
+			height: image.height,
+			top: image.top_offset,
+			left: image.left_offset,
+			data,
+		})
+	}
+}
+
+impl Deref for RgbImage {
+	type Target = [u8];
+
+	fn deref(&self) -> &Self::Target {
+		&self.data
+	}
+}
+
+impl DerefMut for RgbImage {
+	fn deref_mut(&mut self) -> &mut Self::Target {
+		self.data.as_mut_slice()
+	}
 }
diff --git a/gifed/src/gif.rs b/gifed/src/gif.rs
index 3ccc185..dff3123 100644
--- a/gifed/src/gif.rs
+++ b/gifed/src/gif.rs
@@ -1,12 +1,15 @@
-use std::{fs::File, io::Write, iter::Peekable, path::Path};
+use std::{convert::TryInto, fs::File, io::Write, iter::Peekable, path::Path, time::Duration};
 
 use crate::{
 	block::{
-		encode_block, extension::GraphicControl, Block, ColorTable, ScreenDescriptor, Version,
+		encode_block,
+		extension::{DisposalMethod, GraphicControl},
+		Block, ColorTable, ScreenDescriptor, Version,
 	},
-	colorimage,
+	colorimage::{RgbImage, RgbaImage},
+	reader::DecodingError,
 	writer::GifBuilder,
-	Color, ColorImage,
+	Color,
 };
 pub struct Gif {
 	pub header: Version,
@@ -62,6 +65,16 @@ impl Gif {
 	}
 }
 
+pub struct FrameIterator<'a> {
+	image_iterator: ImageIterator<'a>,
+	canvas: RgbaImage,
+	replace_after_draw: Option<RgbaImage>,
+}
+
+pub struct Frame<'a> {
+	images: Vec<Image<'a>>,
+}
+
 pub struct ImageIterator<'a> {
 	gif: &'a Gif,
 	block_index: usize,
@@ -118,56 +131,12 @@ pub struct Image<'a> {
 }
 
 impl<'a> Image<'a> {
-	pub fn rgba(&self) -> Option<Vec<u8>> {
-		let mut rgba = vec![0; self.indicies.len() * 4];
-
-		for (image_index, &color_index) in self.indicies.iter().enumerate() {
-			match self.transparent_index() {
-				Some(trans) if trans == color_index => {
-					rgba[image_index as usize * 4] = 0;
-					rgba[image_index * 4 + 1] = 0;
-					rgba[image_index * 4 + 2] = 0;
-					rgba[image_index * 4 + 3] = 0;
-				}
-				_ => {
-					if let Some(color) = self.palette.get(color_index) {
-						rgba[image_index * 4] = color.r;
-						rgba[image_index * 4 + 1] = color.g;
-						rgba[image_index * 4 + 2] = color.b;
-						rgba[image_index * 4 + 3] = 255;
-					} else {
-						return None;
-					}
-				}
-			}
-		}
-
-		Some(rgba)
+	pub fn rgba(&self) -> Result<RgbaImage, DecodingError> {
+		self.try_into()
 	}
 
-	pub fn rgb(&self, transparent_replace: Color) -> Option<Vec<u8>> {
-		let mut rgb = vec![0; self.indicies.len() * 3];
-
-		for (image_index, &color_index) in self.indicies.iter().enumerate() {
-			match self.transparent_index() {
-				Some(trans) if trans == color_index => {
-					rgb[image_index as usize * 4] = transparent_replace.r;
-					rgb[image_index * 3 + 1] = transparent_replace.g;
-					rgb[image_index * 3 + 2] = transparent_replace.b;
-				}
-				_ => {
-					if let Some(color) = self.palette.get(color_index) {
-						rgb[image_index * 3] = color.r;
-						rgb[image_index * 3 + 1] = color.g;
-						rgb[image_index * 3 + 2] = color.b;
-					} else {
-						return None;
-					}
-				}
-			}
-		}
-
-		Some(rgb)
+	pub fn rgb(&self, transparent_replace: Color) -> Result<RgbImage, DecodingError> {
+		RgbImage::from_image(self, transparent_replace)
 	}
 
 	pub fn graphic_control(&self) -> Option<&GraphicControl> {
@@ -186,6 +155,30 @@ impl<'a> Image<'a> {
 			.flatten()
 	}
 
+	pub fn frame_control(&self) -> Option<FrameControl> {
+		if let Some(gce) = self.graphic_control() {
+			let delay = gce.delay_duration();
+			let user_input = gce.user_input();
+
+			match (delay.is_zero(), user_input) {
+				(true, true) => Some(FrameControl::Input),
+				(false, true) => Some(FrameControl::InputOrDelay(delay)),
+				(false, false) => Some(FrameControl::Delay(delay)),
+				(true, false) => None,
+			}
+		} else {
+			None
+		}
+	}
+
+	pub fn disposal_method(&self) -> DisposalMethod {
+		if let Some(gce) = self.graphic_control() {
+			gce.disposal_method().unwrap_or(DisposalMethod::NoAction)
+		} else {
+			DisposalMethod::NoAction
+		}
+	}
+
 	pub fn png_trns(&self) -> Option<Vec<u8>> {
 		if let Some(trans_idx) = self.transparent_index() {
 			let mut trns = Vec::with_capacity(self.palette.len());
@@ -205,6 +198,12 @@ impl<'a> Image<'a> {
 	}
 }
 
+pub enum FrameControl {
+	Delay(Duration),
+	Input,
+	InputOrDelay(Duration),
+}
+
 #[cfg(test)]
 pub mod gif {
 	use std::convert::TryInto;
diff --git a/gifed/src/lib.rs b/gifed/src/lib.rs
index 0a11fdc..c47d09c 100644
--- a/gifed/src/lib.rs
+++ b/gifed/src/lib.rs
@@ -11,7 +11,7 @@ use core::fmt;
 use std::error::Error;
 
 pub use color::Color;
-pub use colorimage::ColorImage;
+pub use colorimage::{RgbImage, RgbaImage};
 pub use gif::Gif;
 pub use lzw::LZW;