image_optimizer/optimization/
jpeg_optimizer.rs

1use anyhow::{Context, Result};
2use image::DynamicImage;
3use std::fs;
4use std::path::Path;
5
6use crate::cli::Cli;
7
8/// Optimizes a JPEG image using mozjpeg compression
9///
10/// # Errors
11/// Returns an error if JPEG compression fails or file I/O operations fail
12pub fn optimize_jpeg(
13    input_path: &Path,
14    output_path: &Path,
15    args: &Cli,
16    resized_img: Option<DynamicImage>,
17) -> Result<()> {
18    let quality = if args.lossless { 100 } else { args.quality };
19
20    let (width, height, rgb_data) = if let Some(img) = resized_img {
21        let rgb_img = img.to_rgb8();
22        (rgb_img.width(), rgb_img.height(), rgb_img.into_raw())
23    } else {
24        let input_data = fs::read(input_path)?;
25        let decompress = mozjpeg::Decompress::new_mem(&input_data)?;
26        let width = u32::try_from(decompress.width()).context("Width too large")?;
27        let height = u32::try_from(decompress.height()).context("Height too large")?;
28        let mut decompress_started = decompress.rgb()?;
29        let rgb_data: Vec<u8> = decompress_started.read_scanlines()?;
30        (width, height, rgb_data)
31    };
32
33    let mut compress = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_RGB);
34    compress.set_quality(f32::from(quality));
35    compress.set_size(width as usize, height as usize);
36
37    let mut output_data = Vec::new();
38    let mut compress_started = compress.start_compress(&mut output_data)?;
39
40    let row_stride = (width * 3) as usize;
41    for row in rgb_data.chunks(row_stride) {
42        compress_started.write_scanlines(row)?;
43    }
44
45    compress_started.finish()?;
46    fs::write(output_path, output_data)?;
47
48    Ok(())
49}