commit 7d3a6016867f56dfaa752191639834f12581e106 Author: Joshua Holmer Date: Wed Dec 16 08:01:05 2015 -0500 So as to not lose my work diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a9d37c56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..5a71d187 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "optipng" +version = "2.0.0" +authors = ["Joshua Holmer "] + +[dependencies] +flate2 = "0.2" +png = "0.4" diff --git a/src/compress.rs b/src/compress.rs new file mode 100644 index 00000000..eb2321b4 --- /dev/null +++ b/src/compress.rs @@ -0,0 +1,5 @@ +extern crate png; + +pub fn compress(idat: Vec, f: u8, i: u8, zc: u8, zm: u8, zs: u8, zw: u32) -> Vec { + +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..8733eb63 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,91 @@ +extern crate png; + +use std::path::Path; +use std::fs::File; +use std::io; +use std::collections::HashSet; + +mod compress; + +pub struct Options<'a> { + pub backup: bool, + pub out_file: &'a Path, + pub fix_errors: bool, + pub force: bool, + pub clobber: bool, + pub create: bool, + pub preserve_attrs: bool, + pub verbosity: Option, + pub f: HashSet, + pub i: u8, + pub zc: HashSet, + pub zm: HashSet, + pub zs: HashSet, + pub zw: u32, + pub bit_depth_reduction: bool, + pub color_type_reduction: bool, + pub palette_reduction: bool, + pub idat_recoding: bool, + pub idat_paranoia: bool, +} + +// Processing: /Users/holmerj/Downloads/renpy-6.99.7-sdk/doc/_images/frame_example.png +// 579x354 pixels, PNG format +// 3x8 bits/pixel, RGB +// IDAT size = 45711 bytes +// file size = 45881 bytes +// Trying: 8 combinations +// Output: +// IDAT size = 45711 bytes (no change) +// file size = 45821 bytes (60 bytes = 0.13% decrease) +pub fn optimize(filepath: &Path, opts: Options) -> Result<(), io::Error> { + // Decode PNG from file + println!("Processing: {}", filepath.to_str().unwrap()); + let in_file = try!(File::open(filepath)); + let decoder = png::Decoder::new(in_file); + let (info, mut reader) = try!(decoder.read_info()); + let mut img_buf = vec![0; info.buffer_size()]; + try!(reader.next_frame(&mut img_buf)); + + // Read and print + let info = reader.info(); + let (width, height) = info.size(); + let depth = (info.bytes_per_pixel(), info.bits_per_pixel() / info.bytes_per_pixel()); + // FIXME + let idat_current_size = img_buf.len(); + let file_current_size = filepath.metadata().unwrap().len(); + if opts.verbosity.is_some() { + println!("{}x{} pixels, PNG format", width, height); + if let Some(palette) = info.palette.clone() { + println!("{} bits/pixel, {} colors in palette", depth.1, palette.len() / 3); + } else { + println!("{}x{} bits/pixel, {:?}", depth.0, depth.1, info.color_type); + } + println!("IDAT size = {} bytes", idat_current_size); + println!("File size = {} bytes", file_current_size); + } + + // TODO: Bit depth/palette reduction + + // Go through selected permutations and determine the best + if opts.idat_recoding { + let combinations = opts.f.len() * opts.zc.len() * opts.zm.len() * opts.zs.len(); + println!("Trying: {} combinations", combinations); + // TODO: Multithreading + for f in &opts.f { + for zc in &opts.zc { + for zm in &opts.zm { + for zs in &opts.zs { + // TODO: Test compressions + } + } + } + } + } + + // TODO: Backup before writing? + + // TODO: Write output file + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 00000000..57db6a2c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,45 @@ +extern crate optipng; + +use std::env; +use std::path::Path; +use std::collections::HashSet; + +fn main() { + // TODO: Handle wildcards + let filename = env::args().skip(1).next().unwrap(); + let infile = Path::new(&filename); + let mut f = HashSet::new(); + f.insert(0); + f.insert(5); + let mut zc = HashSet::new(); + zc.insert(9); + let mut zm = HashSet::new(); + zm.insert(9); + let mut zs = HashSet::new(); + for i in 0..4 { + zs.insert(i); + } + + let default_opts = optipng::Options { + backup: false, + out_file: infile, + fix_errors: false, + force: false, + clobber: true, + create: true, + preserve_attrs: false, + verbosity: Some(0), + f: f, + i: 0, + zc: zc, + zm: zm, + zs: zs, + zw: 4096, + bit_depth_reduction: true, + color_type_reduction: true, + palette_reduction: true, + idat_recoding: true, + idat_paranoia: false, + }; + optipng::optimize(infile, default_opts); +}