Skip to main content

fastpack_compress/backends/
jpeg.rs

1#[cfg(not(feature = "jpeg-turbo"))]
2use std::io::Cursor;
3
4#[cfg(not(feature = "jpeg-turbo"))]
5use image::codecs::jpeg::JpegEncoder;
6
7use crate::{
8    compressor::{CompressInput, CompressOutput, Compressor},
9    error::CompressError,
10};
11
12/// JPEG encoder. Quality is taken from `CompressInput::quality` (0–100).
13///
14/// When built with the `jpeg-turbo` cargo feature, mozjpeg is used for better
15/// compression at the same quality setting. Without it, the `image` crate's
16/// built-in encoder is used.
17pub struct JpegCompressor;
18
19impl Compressor for JpegCompressor {
20    fn compress(&self, input: &CompressInput<'_>) -> Result<CompressOutput, CompressError> {
21        compress_jpeg(input)
22    }
23
24    fn format_id(&self) -> &'static str {
25        "jpeg"
26    }
27
28    fn file_extension(&self) -> &'static str {
29        "jpg"
30    }
31}
32
33fn compress_jpeg(input: &CompressInput<'_>) -> Result<CompressOutput, CompressError> {
34    let quality = input.quality;
35
36    #[cfg(feature = "jpeg-turbo")]
37    return compress_mozjpeg(input.image, quality);
38
39    #[cfg(not(feature = "jpeg-turbo"))]
40    {
41        let rgb = input.image.to_rgb8();
42        let mut buf = Cursor::new(Vec::new());
43        let mut encoder = JpegEncoder::new_with_quality(&mut buf, quality);
44        encoder.encode(
45            rgb.as_raw(),
46            rgb.width(),
47            rgb.height(),
48            image::ExtendedColorType::Rgb8,
49        )?;
50        Ok(CompressOutput {
51            data: buf.into_inner(),
52        })
53    }
54}
55
56#[cfg(feature = "jpeg-turbo")]
57fn compress_mozjpeg(
58    image: &image::DynamicImage,
59    quality: u8,
60) -> Result<CompressOutput, CompressError> {
61    let rgb = image.to_rgb8();
62    let (width, height) = rgb.dimensions();
63    let raw = rgb.as_raw();
64
65    let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_RGB);
66    comp.set_size(width as usize, height as usize);
67    comp.set_quality(quality as f32);
68
69    let mut buf = Vec::new();
70    let mut comp = comp
71        .start_compress(&mut buf)
72        .map_err(|e| CompressError::Other(e.to_string()))?;
73    comp.write_scanlines(raw)
74        .map_err(|e| CompressError::Other(e.to_string()))?;
75    comp.finish()
76        .map_err(|e| CompressError::Other(e.to_string()))?;
77    Ok(CompressOutput { data: buf })
78}