aboutsummaryrefslogtreecommitdiff
path: root/vendor/png/examples/corpus-bench.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/png/examples/corpus-bench.rs')
-rw-r--r--vendor/png/examples/corpus-bench.rs198
1 files changed, 198 insertions, 0 deletions
diff --git a/vendor/png/examples/corpus-bench.rs b/vendor/png/examples/corpus-bench.rs
new file mode 100644
index 0000000..b030d6d
--- /dev/null
+++ b/vendor/png/examples/corpus-bench.rs
@@ -0,0 +1,198 @@
+use std::{fs, path::PathBuf};
+
+use clap::Parser;
+use png::Decoder;
+
+#[derive(clap::ValueEnum, Clone)]
+enum Speed {
+ Fast,
+ Default,
+ Best,
+}
+
+#[derive(clap::ValueEnum, Clone)]
+enum Filter {
+ None,
+ Sub,
+ Up,
+ Average,
+ Paeth,
+ Adaptive,
+}
+
+#[derive(clap::Parser)]
+struct Args {
+ directory: Option<PathBuf>,
+ #[clap(short, long, value_enum, default_value_t = Speed::Fast)]
+ speed: Speed,
+ #[clap(short, long, value_enum, default_value_t = Filter::Adaptive)]
+ filter: Filter,
+}
+
+#[inline(never)]
+fn run_encode(
+ args: &Args,
+ dimensions: (u32, u32),
+ color_type: png::ColorType,
+ bit_depth: png::BitDepth,
+ image: &[u8],
+) -> Vec<u8> {
+ let mut reencoded = Vec::new();
+ let mut encoder = png::Encoder::new(&mut reencoded, dimensions.0, dimensions.1);
+ encoder.set_color(color_type);
+ encoder.set_depth(bit_depth);
+ encoder.set_compression(match args.speed {
+ Speed::Fast => png::Compression::Fast,
+ Speed::Default => png::Compression::Default,
+ Speed::Best => png::Compression::Best,
+ });
+ encoder.set_filter(match args.filter {
+ Filter::None => png::FilterType::NoFilter,
+ Filter::Sub => png::FilterType::Sub,
+ Filter::Up => png::FilterType::Up,
+ Filter::Average => png::FilterType::Avg,
+ Filter::Paeth => png::FilterType::Paeth,
+ Filter::Adaptive => png::FilterType::Paeth,
+ });
+ encoder.set_adaptive_filter(match args.filter {
+ Filter::Adaptive => png::AdaptiveFilterType::Adaptive,
+ _ => png::AdaptiveFilterType::NonAdaptive,
+ });
+ let mut encoder = encoder.write_header().unwrap();
+ encoder.write_image_data(&image).unwrap();
+ encoder.finish().unwrap();
+ reencoded
+}
+
+#[inline(never)]
+fn run_decode(image: &[u8], output: &mut [u8]) {
+ let mut reader = Decoder::new(image).read_info().unwrap();
+ reader.next_frame(output).unwrap();
+}
+
+fn main() {
+ let mut total_uncompressed = 0;
+ let mut total_compressed = 0;
+ let mut total_pixels = 0;
+ let mut total_encode_time = 0;
+ let mut total_decode_time = 0;
+
+ let args = Args::parse();
+
+ println!(
+ "{:45} Ratio Encode Decode",
+ "Directory"
+ );
+ println!(
+ "{:45}------- -------------------- --------------------",
+ "---------"
+ );
+
+ let mut image2 = Vec::new();
+
+ let mut pending = vec![args.directory.clone().unwrap_or(PathBuf::from("."))];
+ while let Some(directory) = pending.pop() {
+ let mut dir_uncompressed = 0;
+ let mut dir_compressed = 0;
+ let mut dir_pixels = 0;
+ let mut dir_encode_time = 0;
+ let mut dir_decode_time = 0;
+
+ for entry in fs::read_dir(&directory).unwrap().flatten() {
+ if entry.file_type().unwrap().is_dir() {
+ pending.push(entry.path());
+ continue;
+ }
+
+ match entry.path().extension() {
+ Some(st) if st == "png" => {}
+ _ => continue,
+ }
+
+ // Parse
+ let data = fs::read(entry.path()).unwrap();
+ let mut decoder = Decoder::new(&*data);
+ if decoder.read_header_info().ok().map(|h| h.color_type)
+ == Some(png::ColorType::Indexed)
+ {
+ decoder.set_transformations(
+ png::Transformations::EXPAND | png::Transformations::STRIP_16,
+ );
+ }
+ let mut reader = match decoder.read_info() {
+ Ok(reader) => reader,
+ Err(_) => continue,
+ };
+ let mut image = vec![0; reader.output_buffer_size()];
+ let info = match reader.next_frame(&mut image) {
+ Ok(info) => info,
+ Err(_) => continue,
+ };
+ let (width, height) = (info.width, info.height);
+ let bit_depth = info.bit_depth;
+ let mut color_type = info.color_type;
+
+ // qoibench expands grayscale to RGB, so we do the same.
+ if bit_depth == png::BitDepth::Eight {
+ if color_type == png::ColorType::Grayscale {
+ image = image.into_iter().flat_map(|v| [v, v, v, 255]).collect();
+ color_type = png::ColorType::Rgba;
+ } else if color_type == png::ColorType::GrayscaleAlpha {
+ image = image
+ .chunks_exact(2)
+ .flat_map(|v| [v[0], v[0], v[0], v[1]])
+ .collect();
+ color_type = png::ColorType::Rgba;
+ }
+ }
+
+ // Re-encode
+ let start = std::time::Instant::now();
+ let reencoded = run_encode(&args, (width, height), color_type, bit_depth, &image);
+ let elapsed = start.elapsed().as_nanos() as u64;
+
+ // And decode again
+ image2.resize(image.len(), 0);
+ let start2 = std::time::Instant::now();
+ run_decode(&reencoded, &mut image2);
+ let elapsed2 = start2.elapsed().as_nanos() as u64;
+
+ assert_eq!(image, image2);
+
+ // Stats
+ dir_uncompressed += image.len();
+ dir_compressed += reencoded.len();
+ dir_pixels += (width * height) as u64;
+ dir_encode_time += elapsed;
+ dir_decode_time += elapsed2;
+ }
+ if dir_uncompressed > 0 {
+ println!(
+ "{:45}{:6.2}%{:8} mps {:6.2} GiB/s {:8} mps {:6.2} GiB/s",
+ directory.display(),
+ 100.0 * dir_compressed as f64 / dir_uncompressed as f64,
+ dir_pixels * 1000 / dir_encode_time,
+ dir_uncompressed as f64 / (dir_encode_time as f64 * 1e-9 * (1 << 30) as f64),
+ dir_pixels * 1000 / dir_decode_time,
+ dir_uncompressed as f64 / (dir_decode_time as f64 * 1e-9 * (1 << 30) as f64)
+ );
+ }
+
+ total_uncompressed += dir_uncompressed;
+ total_compressed += dir_compressed;
+ total_pixels += dir_pixels;
+ total_encode_time += dir_encode_time;
+ total_decode_time += dir_decode_time;
+ }
+
+ println!();
+ println!(
+ "{:44}{:7.3}%{:8} mps {:6.3} GiB/s {:8} mps {:6.3} GiB/s",
+ "Total",
+ 100.0 * total_compressed as f64 / total_uncompressed as f64,
+ total_pixels * 1000 / total_encode_time,
+ total_uncompressed as f64 / (total_encode_time as f64 * 1e-9 * (1 << 30) as f64),
+ total_pixels * 1000 / total_decode_time,
+ total_uncompressed as f64 / (total_decode_time as f64 * 1e-9 * (1 << 30) as f64)
+ );
+}