From 25b78bb991fa96864558cccd651519c296af1e91 Mon Sep 17 00:00:00 2001 From: gennyble Date: Mon, 9 Oct 2023 17:45:19 -0500 Subject: changes --- gifcheck/Cargo.toml | 1 + gifcheck/src/fix.rs | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++ gifcheck/src/main.rs | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 gifcheck/src/fix.rs (limited to 'gifcheck') diff --git a/gifcheck/Cargo.toml b/gifcheck/Cargo.toml index 8ae0b40..5c63df7 100644 --- a/gifcheck/Cargo.toml +++ b/gifcheck/Cargo.toml @@ -7,3 +7,4 @@ license = "ISC" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +gifed = { path = "../gifed" } diff --git a/gifcheck/src/fix.rs b/gifcheck/src/fix.rs new file mode 100644 index 0000000..95f7ef2 --- /dev/null +++ b/gifcheck/src/fix.rs @@ -0,0 +1,103 @@ +use gifed::{ + block::{Block, Palette}, + Color, Gif, +}; + +use crate::PaletteReport; + +pub fn palette_errors(gif: &Gif, report: PaletteReport) -> Option { + if report.local_matching_indicies { + let mut new = gif.clone(); + + for block in new.blocks.iter_mut() { + if let Block::CompressedImage(comp) = block { + comp.image_descriptor.packed.set_color_table(false); + comp.image_descriptor.packed.set_color_table_size(0); + + if let Some(plt) = comp.local_color_table.take() { + new.global_color_table.get_or_insert(plt); + } + } + } + + Some(new) + } else { + None + } +} + +pub fn images_match_exactly(gifa: &Gif, gifb: &Gif) -> bool { + let mut a_buf = vec![0; gifa.width() * gifa.height() * 4]; + let mut b_buf = vec![0; gifb.width() * gifb.height() * 4]; + + for (a, b) in gifa.images().zip(gifb.images()) { + if a.width() != b.width() || a.height() != b.height() { + return false; + } + + if a.left() != b.left() || a.top() != b.top() { + return false; + } + + let a_decomp = a.decompess(); + let b_decomp = b.decompess(); + + let a_size = deindex( + &a_decomp.indicies, + a.palette(), + a.transparent_index(), + &mut a_buf, + ); + + let b_size = deindex( + &b_decomp.indicies, + b.palette(), + b.transparent_index(), + &mut b_buf, + ); + + match (a_size, b_size) { + (None, _) | (_, None) => return false, + (Some(asize), Some(bsize)) => { + if asize != bsize { + return false; + } + + if a_buf[..asize] != b_buf[..bsize] { + return false; + } + } + } + } + + true +} + +fn deindex(indicies: &[u8], plt: &Palette, trns: Option, buffer: &mut [u8]) -> Option { + let mut rgba = |idx: usize, clr: Option| match clr { + None => { + buffer[idx] = 0; + buffer[idx + 1] = 0; + buffer[idx + 2] = 0; + buffer[idx + 3] = 0; + } + Some(clr) => { + buffer[idx] = clr.r; + buffer[idx + 1] = clr.g; + buffer[idx + 2] = clr.b; + buffer[idx + 3] = 255; + } + }; + + for (idx, color_idx) in indicies.iter().enumerate() { + match (trns, plt.get(*color_idx)) { + (Some(trns_idx), _) if trns_idx == *color_idx => rgba(idx * 4, None), + (_, Some(color)) => rgba(idx * 4, Some(color)), + (Some(_) | None, None) => { + return None; + } + } + } + + Some(indicies.len() * 4) +} diff --git a/gifcheck/src/main.rs b/gifcheck/src/main.rs index a30eb95..8eec4c1 100644 --- a/gifcheck/src/main.rs +++ b/gifcheck/src/main.rs @@ -1,3 +1,104 @@ +use std::{ops::Deref, path::PathBuf}; + +use gifed::{reader::Decoder, Gif}; + +mod fix; + fn main() { - println!("Hello, world!"); + let file = std::env::args().nth(1).unwrap(); + let arg = std::env::args().nth(2).map(|cmd| cmd.to_lowercase()); + + let gif = Decoder::file(&file).unwrap().read_all().unwrap(); + + let plt_report = same_palette(&gif); + match plt_report { + PaletteReport { + has_local: true, + local_redundant: true, + local_matching_indicies, + } => { + if local_matching_indicies { + println!("!!! LOCPLT_NORE. This could've been a global palette"); + } else { + println!("!! LOCPLT. This gif can be reindexed and have a global palette"); + } + } + PaletteReport { + has_local: true, + local_redundant: false, + .. + } => { + println!(" gif has local palettes and they differ"); + } + PaletteReport { + has_local: false, .. + } => { + println!(" gif only has a global palette"); + } + } + + if arg.as_deref() == Some("fix") { + if let Some(fix_gif) = fix::palette_errors(&gif, plt_report) { + if !fix::images_match_exactly(&gif, &fix_gif) { + panic!("fixed images did not exactly match, this is a hard error") + } + + println!("--- fixing, writing!"); + let mut path = PathBuf::from(file); + path.set_file_name(format!( + "{}_fix", + path.file_stem().unwrap().to_string_lossy() + )); + + fix_gif.save(path).unwrap(); + } + } +} + +pub struct PaletteReport { + // Does the gif even contain local color tables? + has_local: bool, + // ... do those color tables always contain the same colors? + local_redundant: bool, + // ... and do those colors all have matching inidices, making it possible + // to simply set the global palette and remove the locals? + local_matching_indicies: bool, +} + +fn same_palette(gif: &Gif) -> PaletteReport { + let mut palette = gif.global_color_table.as_ref(); + let mut report = PaletteReport { + has_local: false, + local_redundant: true, + local_matching_indicies: true, + }; + + for img in gif.images() { + if let Some(local_palette) = img.compressed.palette() { + report.has_local = true; + + match palette { + None => palette = Some(local_palette), + Some(known_palette) => { + if !local_palette.eq(known_palette) { + // Are the palletes equal, even? + report.local_redundant = false; + report.local_matching_indicies = false; + return report; + } else if report.local_matching_indicies { + // it's matching, but are the indicies the same? + for known_color in known_palette.deref() { + for local_color in local_palette.deref() { + if known_color != local_color { + report.local_matching_indicies = false; + } + } + } + } + } + } + } + } + + report } -- cgit 1.4.1-3-g733a5