image_optimizer/optimization/
png_optimizer.rs1use anyhow::{Context, Result};
2use image::{DynamicImage, ImageFormat};
3use std::fs;
4use std::path::Path;
5
6use crate::cli::Cli;
7
8pub fn optimize_png(
34 input_path: &Path,
35 output_path: &Path,
36 args: &Cli,
37 resized_img: Option<DynamicImage>,
38) -> Result<()> {
39 if let Some(img) = resized_img {
40 img.save_with_format(output_path, ImageFormat::Png)?;
41 } else {
42 fs::copy(input_path, output_path)?;
43 }
44
45 let optimization_level = if args.png_optimization_level == "max" {
46 6
47 } else {
48 match args.png_optimization_level.parse::<u8>() {
49 Ok(level) if level <= 6 => level,
50 _ => {
51 return Err(anyhow::anyhow!(
52 "Invalid oxipng optimization level: {}. Valid values are 0-6 or 'max'",
53 args.png_optimization_level
54 ));
55 }
56 }
57 };
58
59 let mut options = oxipng::Options::from_preset(optimization_level);
60 options.optimize_alpha = true;
61 options.fast_evaluation = true;
62 options.strip = oxipng::StripChunks::Safe;
63
64 if args.no_zopfli {
65 options.deflate = oxipng::Deflaters::Libdeflater { compression: 12 };
66 } else {
67 options.deflate = oxipng::Deflaters::Zopfli {
68 iterations: args.zopfli_iterations,
69 };
70 }
71
72 let input_file = oxipng::InFile::Path(output_path.to_path_buf());
73 let output_file = oxipng::OutFile::Path {
74 path: Some(output_path.to_path_buf()),
75 preserve_attrs: true,
76 };
77
78 oxipng::optimize(&input_file, &output_file, &options).context("Failed to optimize PNG")?;
79
80 Ok(())
81}