about summary refs log tree commit diff
path: root/giftool/src/main.rs
diff options
context:
space:
mode:
authorGenevieve Alfirevic <gen@nyble.dev>2022-04-05 00:55:37 -0500
committerGenevieve Alfirevic <gen@nyble.dev>2022-04-05 00:55:37 -0500
commit5e4c832053635b6900044b2c48771788856b5244 (patch)
tree40bbc3df4c2532b1489c9f2862264ad6fc543c91 /giftool/src/main.rs
parent90119b61130a7475ad743e512f41110d1b50ed99 (diff)
downloadgifed-5e4c832053635b6900044b2c48771788856b5244.tar.gz
gifed-5e4c832053635b6900044b2c48771788856b5244.zip
giftool: some analyze subcommand work
Diffstat (limited to 'giftool/src/main.rs')
-rw-r--r--giftool/src/main.rs101
1 files changed, 92 insertions, 9 deletions
diff --git a/giftool/src/main.rs b/giftool/src/main.rs
index a835112..dcfe113 100644
--- a/giftool/src/main.rs
+++ b/giftool/src/main.rs
@@ -1,6 +1,6 @@
-use std::{fs::File, path::PathBuf};
+use std::{fs::File, ops::Deref, path::PathBuf};
 
-use gifed::reader::GifReader;
+use gifed::{reader::GifReader, Gif};
 
 fn main() {
 	let subcommand = std::env::args().nth(1);
@@ -16,15 +16,14 @@ fn print_usage_and_exit() -> ! {
 	println!("usage: giftool <subcommand> <options>\n");
 	println!("extract_frames <input_gif> <output_directory>");
 	println!("\tExtract each frame of the gif to a png in the output directory.");
+	println!("analyze <input_gif>");
+	println!("\tAnalyze the gif, looking for places to make the image smaller.");
 
 	std::process::exit(0)
 }
 
-fn extract_frames() {
-	let input_gif = std::env::args().nth(2).map(PathBuf::from);
-	let out_dir = std::env::args().nth(3).map(PathBuf::from);
-
-	let input_gif = if let Some(igif) = input_gif {
+fn check_input_gif(igif: Option<PathBuf>) -> PathBuf {
+	if let Some(igif) = igif {
 		if !igif.exists() {
 			println!("The path provided to the gif does not exist");
 			std::process::exit(1);
@@ -37,7 +36,14 @@ fn extract_frames() {
 	} else {
 		println!("No gif file provided");
 		std::process::exit(1);
-	};
+	}
+}
+
+fn extract_frames() {
+	let input_gif = std::env::args().nth(2).map(PathBuf::from);
+	let out_dir = std::env::args().nth(3).map(PathBuf::from);
+
+	let input_gif = check_input_gif(input_gif);
 
 	let out_dir = if let Some(odir) = out_dir {
 		if !odir.exists() {
@@ -85,4 +91,81 @@ fn extract_frames() {
 	}
 }
 
-fn analyze() {}
+fn analyze() {
+	let input_gif = check_input_gif(std::env::args().nth(2).map(PathBuf::from));
+
+	//TODO:
+	// Look at color tables:
+	// - Can any of them be combined into a global colour table? Can they all be?
+	// - Can the colour tables be shrank? Is it bigger than it needs to be. (Are all the inidicies used)
+
+	let gread = match GifReader::file(&input_gif) {
+		Ok(gread) => gread,
+		Err(e) => {
+			println!(
+				"Failed to read {} as a gif:\n{}",
+				input_gif.to_string_lossy(),
+				e
+			);
+			std::process::exit(1);
+		}
+	};
+
+	if gread.global_color_table.is_some() {
+		if analyze_can_be_a_gct(&gread) {
+			println!("All local colors are in the global, this gif doesn't need locals");
+		}
+	}
+}
+
+/// A gif can be reindexed to not need any local color tables if:
+/// - The global color table contains all colors used in the gif
+/// OR
+/// - The global color table does not contain all colors used in the gif,
+///   but it can fit the colors it does not have.
+/// OR
+/// - There is no global color table, but there are only 256 colors used in
+///   the gif, so it can be created.
+///
+/// # Returns
+/// This function returns `true` if the gif can be reindexed to not need any
+/// local tables anymore.
+fn analyze_can_be_a_gct(gread: &Gif) -> bool {
+	let mut gct = if let Some(gct) = gread.global_color_table.as_ref() {
+		let mut gct = gct.clone().to_vec();
+		gct.dedup();
+		gct
+	} else {
+		vec![]
+	};
+
+	let mut gct_contains_all_colors = true;
+	let mut largest_gct_seen = 0;
+	let mut gif_has_local_tables = false;
+
+	for image in gread.images() {
+		if image.packed.color_table() {
+			gif_has_local_tables = true;
+			let lct = image.palette;
+
+			for color in lct.deref() {
+				if !gct.contains(color) {
+					gct_contains_all_colors = false;
+					gct.push(color.clone());
+				}
+			}
+		} else {
+			for index in image.indicies {
+				largest_gct_seen = largest_gct_seen.max(*index);
+			}
+		}
+	}
+
+	if gct_contains_all_colors || !gif_has_local_tables {
+		true
+	} else if !gct_contains_all_colors && gct.len() <= 256 {
+		true
+	} else {
+		false
+	}
+}