image_optimizer/optimization/
jpeg_optimizer.rs1use anyhow::{Context, Result};
2use image::DynamicImage;
3use std::fs;
4use std::path::Path;
5
6use crate::cli::Cli;
7
8pub fn optimize_jpeg(
33 input_path: &Path,
34 output_path: &Path,
35 args: &Cli,
36 resized_img: Option<DynamicImage>,
37) -> Result<()> {
38 let quality = args.jpeg_quality;
39
40 let (width, height, rgb_data) = if let Some(img) = resized_img {
41 let rgb_img = img.to_rgb8();
42 (rgb_img.width(), rgb_img.height(), rgb_img.into_raw())
43 } else {
44 let input_data = fs::read(input_path)?;
45 let decompress = mozjpeg::Decompress::new_mem(&input_data)?;
46 let width = u32::try_from(decompress.width()).context("Width too large")?;
47 let height = u32::try_from(decompress.height()).context("Height too large")?;
48 let mut decompress_started = decompress.rgb()?;
49 let rgb_data: Vec<u8> = decompress_started.read_scanlines()?;
50 (width, height, rgb_data)
51 };
52
53 let mut compress = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_RGB);
54 compress.set_quality(f32::from(quality));
55 compress.set_size(width as usize, height as usize);
56
57 let mut output_data = Vec::new();
58 let mut compress_started = compress.start_compress(&mut output_data)?;
59
60 let row_stride = (width * 3) as usize;
61 for row in rgb_data.chunks(row_stride) {
62 compress_started.write_scanlines(row)?;
63 }
64
65 compress_started.finish()?;
66 fs::write(output_path, output_data)?;
67
68 Ok(())
69}