diff options
author | Genny <gen@nyble.dev> | 2021-11-23 00:57:05 -0600 |
---|---|---|
committer | Genny <gen@nyble.dev> | 2021-11-23 00:57:05 -0600 |
commit | e2730a9990e13803f6fd6f3c7a6942e64b833f5f (patch) | |
tree | aabcc26dde59256b62fe5edd8b29ff39bf4c56e8 | |
parent | c75b385f52b86bb31e13615086f5040074e3b77b (diff) | |
download | gifed-e2730a9990e13803f6fd6f3c7a6942e64b833f5f.tar.gz gifed-e2730a9990e13803f6fd6f3c7a6942e64b833f5f.zip |
Collapse extension enum into block
-rw-r--r-- | gifed/examples/read.rs | 10 | ||||
-rw-r--r-- | gifed/src/block/extension/graphiccontrol.rs | 8 | ||||
-rw-r--r-- | gifed/src/block/extension/mod.rs | 46 | ||||
-rw-r--r-- | gifed/src/block/mod.rs | 58 | ||||
-rw-r--r-- | gifed/src/gif.rs | 123 | ||||
-rw-r--r-- | gifed/src/reader/mod.rs | 14 | ||||
-rw-r--r-- | gifed/src/writer/gifbuilder.rs | 9 | ||||
-rw-r--r-- | gifprobe/src/main.rs | 177 |
8 files changed, 240 insertions, 205 deletions
diff --git a/gifed/examples/read.rs b/gifed/examples/read.rs index 3c40bfe..3a96509 100644 --- a/gifed/examples/read.rs +++ b/gifed/examples/read.rs @@ -8,12 +8,12 @@ fn main() { let reader = GifReader::file("examples/simulation.gif").unwrap(); let first = reader.images().next().unwrap(); - Gif::builder(first.width(), first.height()) - .palette(first.palette().clone()) + Gif::builder(first.width, first.height) + .palette(first.palette.clone()) .image( - ImageBuilder::new(first.width(), first.height()) - .transparent_index(first.transparent_index()) - .indicies(first.indicies()), + ImageBuilder::new(first.width, first.height) + .transparent_index(first.transparent_index) + .indicies(first.indicies), ) .build() .unwrap() diff --git a/gifed/src/block/extension/graphiccontrol.rs b/gifed/src/block/extension/graphiccontrol.rs index b595554..8b62160 100644 --- a/gifed/src/block/extension/graphiccontrol.rs +++ b/gifed/src/block/extension/graphiccontrol.rs @@ -22,7 +22,7 @@ impl GraphicControl { }; ret.set_disposal_method(disposal_method); - ret.user_input(user_input_flag); + ret.set_user_input(user_input_flag); ret.transparency(transparency_flag); ret @@ -51,7 +51,11 @@ impl GraphicControl { self.transparency_index } - pub fn user_input(&mut self, flag: bool) { + pub fn user_input(&self) -> bool { + self.packed & 0b000_000_1_0 > 0 + } + + pub fn set_user_input(&mut self, flag: bool) { if flag { self.packed |= 0b000_000_1_0; } else { diff --git a/gifed/src/block/extension/mod.rs b/gifed/src/block/extension/mod.rs index fb5eb20..99ac88b 100644 --- a/gifed/src/block/extension/mod.rs +++ b/gifed/src/block/extension/mod.rs @@ -1,49 +1,5 @@ mod application; mod graphiccontrol; -pub use graphiccontrol::{DisposalMethod, GraphicControl}; - pub use self::application::Application; - -pub enum Extension { - GraphicControl(GraphicControl), - Looping(u16), - Comment(Vec<u8>), // Plain Text - Application(Application), -} - -impl From<&Extension> for Box<[u8]> { - fn from(ext: &Extension) -> Self { - let mut vec = vec![]; - vec.push(0x21); // Push the extension introducer - - match ext { - Extension::GraphicControl(gc) => { - vec.push(0xF9); // Graphic control label - vec.push(0x04); // Block size for this extension is always 4 - vec.push(gc.packed); - vec.extend_from_slice(&gc.delay_time.to_le_bytes()); - vec.push(gc.transparency_index); - } - Extension::Looping(count) => { - vec.push(0xFF); // Application extension label - vec.push(0x0B); // 11 bytes in this block - vec.extend_from_slice(b"NETSCAPE2.0"); // App. ident. and "auth code" - vec.push(0x03); // Sub-block length - vec.push(0x01); // Identifies netscape looping extension - vec.extend_from_slice(&count.to_le_bytes()); - } - Extension::Comment(_) => todo!(), - Extension::Application(_) => todo!(), - } - - vec.push(0x00); // Zero-length data block indicates end of extension - vec.into_boxed_slice() - } -} - -impl From<GraphicControl> for Extension { - fn from(gce: GraphicControl) -> Self { - Extension::GraphicControl(gce) - } -} +pub use graphiccontrol::{DisposalMethod, GraphicControl}; diff --git a/gifed/src/block/mod.rs b/gifed/src/block/mod.rs index e35224b..a101600 100644 --- a/gifed/src/block/mod.rs +++ b/gifed/src/block/mod.rs @@ -14,12 +14,68 @@ pub use version::Version; use crate::writer::ImageBuilder; +use self::extension::Application; +use self::extension::GraphicControl; + pub enum Block { IndexedImage(IndexedImage), - Extension(extension::Extension), + //TODO: Extension(Extension), + GraphicControlExtension(GraphicControl), + CommentExtension(Vec<u8>), + //TODO: PlainTextExtension(PlainTextExtension), + ApplicationExtension(Application), + LoopingExtension(LoopCount), } enum WriteBlock { ImageBuilder(ImageBuilder), Block(Block), } + +pub enum LoopCount { + Forever, + Number(u16), +} + +pub(crate) fn encode_block(mcs: u8, block: &Block) -> Box<[u8]> { + match block { + Block::IndexedImage(img) => img.as_boxed_slice(mcs), + Block::GraphicControlExtension(_) => encode_extension(block), + Block::CommentExtension(_) => encode_extension(block), + Block::ApplicationExtension(_) => encode_extension(block), + Block::LoopingExtension(_) => encode_extension(block), + } +} + +fn encode_extension(block: &Block) -> Box<[u8]> { + let mut vec = vec![]; + vec.push(0x21); // Extension Introducer + + match block { + Block::IndexedImage(_) => unreachable!(), + Block::GraphicControlExtension(gce) => { + vec.push(0xF9); // Graphic control label + vec.push(0x04); // Block size for this extension is always 4 + vec.push(gce.packed); + vec.extend_from_slice(&gce.delay_time.to_le_bytes()); + vec.push(gce.transparency_index); + } + Block::CommentExtension(comment) => todo!(), + Block::ApplicationExtension(app) => todo!(), + Block::LoopingExtension(lc) => { + vec.push(0xFF); // Application extension label + vec.push(0x0B); // 11 bytes in this block + vec.extend_from_slice(b"NETSCAPE2.0"); // App. ident. and "auth code" + vec.push(0x03); // Sub-block length + vec.push(0x01); // Identifies netscape looping extension + + match lc { + LoopCount::Forever => vec.extend_from_slice(&[0x00, 0x00]), + LoopCount::Number(count) => vec.extend_from_slice(&count.to_le_bytes()), + } + } + } + + vec.push(0x00); // Zero length sub-block indicates end of extension + vec.into_boxed_slice() +} diff --git a/gifed/src/gif.rs b/gifed/src/gif.rs index 89aaa64..5f1bd7a 100644 --- a/gifed/src/gif.rs +++ b/gifed/src/gif.rs @@ -1,10 +1,12 @@ -use std::{fs::File, io::Write, path::Path}; +use std::{fs::File, io::Write, iter::Peekable, path::Path}; use crate::{ - block::{extension::Extension, Block, ColorTable, ScreenDescriptor, Version}, + block::{ + encode_block, extension::GraphicControl, Block, ColorTable, ScreenDescriptor, Version, + }, colorimage, writer::GifBuilder, - ColorImage, + Color, ColorImage, }; pub struct Gif { pub header: Version, @@ -38,17 +40,8 @@ impl Gif { }; for block in self.blocks.iter() { - match block { - Block::IndexedImage(image) => { - boxed = image.as_boxed_slice(mcs); - out.extend_from_slice(&*boxed); - } - //Block::BlockedImage(_) => unreachable!(), - Block::Extension(ext) => { - boxed = ext.into(); - out.extend_from_slice(&*boxed); - } - } + boxed = encode_block(mcs, block); + out.extend_from_slice(&*boxed); } // Write Trailer @@ -84,7 +77,7 @@ impl<'a> Iterator for ImageIterator<'a> { match self.veciter.next() { Some(block) => match block { Block::IndexedImage(img) => break img, - Block::Extension(Extension::GraphicControl(gce)) => { + Block::GraphicControlExtension(gce) => { if gce.is_transparent() { transparent = Some(gce.transparency_index()); } else { @@ -121,56 +114,84 @@ impl<'a> Iterator for ImageIterator<'a> { } } -pub struct FrameIterator<'a> { - gif: &'a Gif, - veciter: std::slice::Iter<'a, Block>, - buffer: Vec<u8>, -} - pub struct Image<'a> { - pub(crate) width: u16, - pub(crate) height: u16, - left_offset: u16, - top_offset: u16, - pub(crate) palette: &'a ColorTable, - pub(crate) transparent_index: Option<u8>, - pub(crate) indicies: &'a [u8], + pub width: u16, + pub height: u16, + pub left_offset: u16, + pub top_offset: u16, + pub palette: &'a ColorTable, + pub transparent_index: Option<u8>, + pub indicies: &'a [u8], } impl<'a> Image<'a> { - pub fn width(&self) -> u16 { - self.width - } + 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; + } + } + } + } - pub fn height(&self) -> u16 { - self.height + Some(rgba) } - pub fn position(&self) -> (u16, u16) { - (self.left_offset, self.top_offset) - } + pub fn rgb(&self, transparent_replace: Color) -> Option<Vec<u8>> { + let mut rgb = vec![0; self.indicies.len() * 3]; - pub fn palette(&self) -> &ColorTable { - self.palette - } + 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; + } + } + } + } - pub fn transparent_index(&self) -> Option<u8> { - self.transparent_index + Some(rgb) } +} - pub fn indicies(&self) -> &[u8] { - self.indicies - } +pub struct FrameIterator<'a> { + gif: &'a Gif, + veciter: std::slice::Iter<'a, Block>, + buffer: Vec<u8>, } pub struct Frame { - width: u16, - height: u16, - palette: ColorTable, - transparent_index: Option<u8>, - indicies: Vec<u8>, - delay_after_draw: u16, - user_input_flag: bool, + pub width: u16, + pub height: u16, + pub palette: ColorTable, + pub transparent_index: Option<u8>, + pub indicies: Vec<u8>, + pub delay_after_draw: u16, + pub user_input_flag: bool, } #[cfg(test)] diff --git a/gifed/src/reader/mod.rs b/gifed/src/reader/mod.rs index 41494df..966ebde 100644 --- a/gifed/src/reader/mod.rs +++ b/gifed/src/reader/mod.rs @@ -10,7 +10,7 @@ use std::{ use crate::{ block::{ - extension::{Application, Extension, GraphicControl}, + extension::{Application, GraphicControl}, Block, ColorTable, CompressedImage, ImageDescriptor, IndexedImage, ScreenDescriptor, Version, }, @@ -104,13 +104,11 @@ impl GifReader { .ok_or(DecodingError::UnexpectedEof)?; reader.skip(1); // Skip block terminator - Ok(Block::Extension(Extension::GraphicControl( - GraphicControl::from(data), - ))) + Ok(Block::GraphicControlExtension(GraphicControl::from(data))) } - 0xFE => Ok(Block::Extension(Extension::Comment( + 0xFE => Ok(Block::CommentExtension( reader.take_and_collapse_subblocks(), - ))), + )), 0x01 => todo!(), //TODO: do; plain text extension 0xFF => { //TODO: error instead of unwraps @@ -120,11 +118,11 @@ impl GifReader { TryInto::try_into(reader.take(3).unwrap()).unwrap(); let data = reader.take_and_collapse_subblocks(); - Ok(Block::Extension(Extension::Application(Application { + Ok(Block::ApplicationExtension(Application { identifier, authentication_code, data, - }))) + })) } _ => panic!("Unknown Extension Identifier!"), } diff --git a/gifed/src/writer/gifbuilder.rs b/gifed/src/writer/gifbuilder.rs index 57a62e3..337e404 100644 --- a/gifed/src/writer/gifbuilder.rs +++ b/gifed/src/writer/gifbuilder.rs @@ -1,6 +1,6 @@ use std::convert::TryInto; -use crate::block::{extension::Extension, Block, ColorTable, ScreenDescriptor, Version}; +use crate::block::{Block, ColorTable, LoopCount, ScreenDescriptor, Version}; use crate::writer::ImageBuilder; use crate::{EncodingError, Gif}; @@ -55,7 +55,7 @@ impl GifBuilder { } if let Some(gce) = ib.get_graphic_control() { - self.blocks.push(Block::Extension(gce.into())); + self.blocks.push(Block::GraphicControlExtension(gce)); } match ib.build() { @@ -71,9 +71,8 @@ impl GifBuilder { self }*/ - pub fn repeat(mut self, count: u16) -> Self { - self.blocks - .push(Block::Extension(Extension::Looping(count))); + pub fn repeat(mut self, count: LoopCount) -> Self { + self.blocks.push(Block::LoopingExtension(count)); self } diff --git a/gifprobe/src/main.rs b/gifprobe/src/main.rs index 3af9a50..35a137f 100644 --- a/gifprobe/src/main.rs +++ b/gifprobe/src/main.rs @@ -1,107 +1,108 @@ use std::{convert::TryInto, fs::File, io::Write, path::Path}; use gifed::{ - block::{ - extension::{Extension, GraphicControl}, - ColorTable, IndexedImage, Version, - }, - reader::GifReader, - writer::{GifBuilder, ImageBuilder}, - Color, + block::{extension::GraphicControl, Block, ColorTable, IndexedImage, Version}, + reader::GifReader, + writer::{GifBuilder, ImageBuilder}, + Color, }; use owo_colors::OwoColorize; fn main() { - let file = if let Some(file) = std::env::args().skip(1).next() { - file - } else { - println!("usage: gifprobe file.gif"); - return; - }; + let file = if let Some(file) = std::env::args().skip(1).next() { + file + } else { + println!("usage: gifprobe file.gif"); + return; + }; - let gif = GifReader::file(&file).unwrap(); + let gif = GifReader::file(&file).unwrap(); - println!("Version {}", gif.header.yellow()); - println!( - "Logical Screen Descriptor\n\tDimensions {}x{}", - gif.screen_descriptor.width.yellow(), - gif.screen_descriptor.height.yellow() - ); + 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() - ); - } + 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() + ); + } - let mut img_count = 0; - let mut hundreths: usize = 0; + let mut img_count = 0; + let mut hundreths: usize = 0; - for block in gif.blocks { - match block { - gifed::block::Block::IndexedImage(img) => { - describe_image(&img); - img_count += 1; - } - gifed::block::Block::Extension(ext) => match ext { - gifed::block::extension::Extension::GraphicControl(gce) => { - hundreths += gce.delay_time() as usize; + for block in gif.blocks { + match block { + Block::IndexedImage(img) => { + describe_image(&img); + img_count += 1; + } + Block::GraphicControlExtension(gce) => { + hundreths += gce.delay_time() as usize; - println!( - "Graphic Control Extension\n\tDelay Time {}\n\tDispose {}", - format!("{}s", gce.delay_time() as f32 / 100.0).yellow(), - gce.disposal_method().unwrap().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()); - } - }, - } - } + println!( + "Graphic Control Extension\n\tDelay Time {}\n\tDispose {}", + format!("{}s", gce.delay_time() as f32 / 100.0).yellow(), + gce.disposal_method().unwrap().yellow() + ) + } + Block::LoopingExtension(_) => todo!(), + Block::CommentExtension(cmt) => { + println!("Comment Extension\n\tLength {}", cmt.len()) + } + Block::ApplicationExtension(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() + ); + } + } + } - println!( - "{} is {}.{}s long and has {} frames", - file, - hundreths / 100, - hundreths % 100, - img_count - ); + println!( + "{} is {}.{}s long and has {} frames", + file, + hundreths / 100, + hundreths % 100, + img_count + ); } fn describe_image(bli: &IndexedImage) { - 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(), - ); + 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() - ); - } + 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() + ); + } } |