So as to not lose my work
This commit is contained in:
commit
7d3a601686
5 changed files with 151 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
target
|
||||||
|
Cargo.lock
|
||||||
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "optipng"
|
||||||
|
version = "2.0.0"
|
||||||
|
authors = ["Joshua Holmer <jholmer.in@gmail.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
flate2 = "0.2"
|
||||||
|
png = "0.4"
|
||||||
5
src/compress.rs
Normal file
5
src/compress.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
extern crate png;
|
||||||
|
|
||||||
|
pub fn compress(idat: Vec<u8>, f: u8, i: u8, zc: u8, zm: u8, zs: u8, zw: u32) -> Vec<u8> {
|
||||||
|
|
||||||
|
}
|
||||||
91
src/lib.rs
Normal file
91
src/lib.rs
Normal file
|
|
@ -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<u8>,
|
||||||
|
pub f: HashSet<u8>,
|
||||||
|
pub i: u8,
|
||||||
|
pub zc: HashSet<u8>,
|
||||||
|
pub zm: HashSet<u8>,
|
||||||
|
pub zs: HashSet<u8>,
|
||||||
|
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(())
|
||||||
|
}
|
||||||
45
src/main.rs
Normal file
45
src/main.rs
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue