diff options
author | Genevieve Alfirevic <gen@nyble.dev> | 2022-02-14 14:20:29 -0600 |
---|---|---|
committer | Genevieve Alfirevic <gen@nyble.dev> | 2022-02-14 14:20:29 -0600 |
commit | 8e57ef9b5042acb6becf4e6e2658505b84fb9974 (patch) | |
tree | 5a8005955d6d39c741dee722e82862007eb5646d | |
parent | d9aa9806d32cf717b3d22aae0514478d103f4aa8 (diff) | |
download | gifed-8e57ef9b5042acb6becf4e6e2658505b84fb9974.tar.gz gifed-8e57ef9b5042acb6becf4e6e2658505b84fb9974.zip |
Image iterator and rgb/rgba image structs
-rw-r--r-- | gifed/src/block/extension/graphiccontrol.rs | 4 | ||||
-rw-r--r-- | gifed/src/color.rs | 9 | ||||
-rw-r--r-- | gifed/src/colorimage.rs | 160 | ||||
-rw-r--r-- | gifed/src/gif.rs | 103 | ||||
-rw-r--r-- | gifed/src/lib.rs | 2 |
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; |