diff --git a/Cargo.lock b/Cargo.lock index 0d4df9a61..76775eae6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2505,17 +2505,29 @@ dependencies = [ [[package]] name = "image" -version = "0.24.9" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +checksum = "fd54d660e773627692c524beaad361aca785a4f9f5730ce91f42aabe5bce3d11" dependencies = [ "bytemuck", "byteorder", "color_quant", "gif", - "jpeg-decoder", + "image-webp", "num-traits", "png", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a84a25dcae3ac487bc24ef280f9e20c79c9b1a3e5e32cbed3041d1c514aa87c" +dependencies = [ + "byteorder", + "thiserror", ] [[package]] @@ -2640,12 +2652,6 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" -[[package]] -name = "jpeg-decoder" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" - [[package]] name = "js-sys" version = "0.3.69" @@ -5900,3 +5906,18 @@ dependencies = [ "quote", "syn 2.0.57", ] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec866b44a2a1fd6133d363f073ca1b179f438f99e7e5bfb1e33f7181facfe448" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index 784bd04a8..d881d1c3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ futures-lite = "2.3.0" hex = "0.4.0" hickory-resolver = "0.24" humansize = "2" -image = { version = "0.24.9", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] } +image = { version = "0.25.1", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] } iroh = { version = "0.4.2", default-features = false } kamadak-exif = "0.5" lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" } diff --git a/src/blob.rs b/src/blob.rs index 2554c6e31..3f67c2b52 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -11,9 +11,8 @@ use std::path::{Path, PathBuf}; use anyhow::{format_err, Context as _, Result}; use base64::Engine as _; use futures::StreamExt; -use image::{ - DynamicImage, GenericImage, GenericImageView, ImageFormat, ImageOutputFormat, Pixel, Rgba, -}; +use image::codecs::jpeg::JpegEncoder; +use image::{DynamicImage, GenericImage, GenericImageView, ImageFormat, Pixel, Rgba}; use num_traits::FromPrimitive; use tokio::io::AsyncWriteExt; use tokio::{fs, io}; @@ -37,6 +36,12 @@ pub struct BlobObject<'a> { name: String, } +#[derive(Debug, Clone)] +enum ImageOutputFormat { + Png, + Jpeg { quality: u8 }, +} + impl<'a> BlobObject<'a> { /// Creates a new blob object with a unique name. /// @@ -457,9 +462,13 @@ impl<'a> BlobObject<'a> { Ok(ImageFormat::Png) if !exceeds_max_bytes => ImageOutputFormat::Png, Ok(ImageFormat::Jpeg) => { add_white_bg = false; - ImageOutputFormat::Jpeg(jpeg_quality) + ImageOutputFormat::Jpeg { + quality: jpeg_quality, + } } - _ => ImageOutputFormat::Jpeg(jpeg_quality), + _ => ImageOutputFormat::Jpeg { + quality: jpeg_quality, + }, }; // We need to rewrite images with Exif to remove metadata such as location, // camera model, etc. @@ -530,7 +539,7 @@ impl<'a> BlobObject<'a> { if do_scale || exif.is_some() { // The file format is JPEG/PNG now, we may have to change the file extension if !matches!(fmt, Ok(ImageFormat::Jpeg)) - && matches!(ofmt, ImageOutputFormat::Jpeg(_)) + && matches!(ofmt, ImageOutputFormat::Jpeg { .. }) { blob_abs = blob_abs.with_extension("jpg"); let file_name = blob_abs.file_name().context("No image file name (???)")?; @@ -685,7 +694,13 @@ fn encode_img( ) -> anyhow::Result<()> { encoded.clear(); let mut buf = Cursor::new(encoded); - img.write_to(&mut buf, fmt)?; + match fmt { + ImageOutputFormat::Png => img.write_to(&mut buf, ImageFormat::Png)?, + ImageOutputFormat::Jpeg { quality } => { + let encoder = JpegEncoder::new_with_quality(&mut buf, quality); + img.write_with_encoder(encoder)?; + } + } Ok(()) }