fix: don't strip ICC from exif (#735)

* fix: don't strip ICC from exif

Signed-off-by: ispik <ispik@ispik.dev>

* fix: refactor ICC profile handling in image processing

Signed-off-by: ispik <ispik@ispik.dev>

---------

Signed-off-by: ispik <ispik@ispik.dev>
This commit is contained in:
İspik
2026-05-09 01:49:31 +03:00
committed by GitHub
parent 6f3441cf4a
commit d76a71141f
7 changed files with 185 additions and 47 deletions

View File

@@ -1,13 +1,24 @@
use std::io::{Cursor, Read};
use crate::utils::apply_icc_profile;
use exif::Reader;
use image::{ImageFormat, ImageReader};
use image::{ImageEncoder, ImageReader};
use revolt_config::report_internal_error;
use revolt_database::Metadata;
use revolt_result::{create_error, Result};
use tempfile::NamedTempFile;
use tokio::process::Command;
macro_rules! encode_with_icc {
($encoder:expr, $icc:expr, $image:expr, $width:expr, $height:expr, $color:expr) => {{
let mut encoder = $encoder;
if let Some(icc) = $icc {
let _ = encoder.set_icc_profile(icc.clone());
}
encoder.write_image($image, $width, $height, $color)
}};
}
/// Strip EXIF data from given file and produce new file and metadata
pub async fn strip_metadata(
file: NamedTempFile,
@@ -17,8 +28,8 @@ pub async fn strip_metadata(
) -> Result<(Vec<u8>, Metadata)> {
match &metadata {
Metadata::Image {
width,
height,
width: _,
height: _,
thumbhash,
animated,
} => match mime {
@@ -46,11 +57,12 @@ pub async fn strip_metadata(
let mut cursor = Cursor::new(buf);
// Decode the image
let image = report_internal_error!(report_internal_error!(ImageReader::new(
&mut cursor
)
.with_guessed_format())?
.decode());
let reader =
report_internal_error!(ImageReader::new(&mut cursor).with_guessed_format())?;
let mut decoder = report_internal_error!(reader.into_decoder())?;
let mut icc_profile =
report_internal_error!(image::ImageDecoder::icc_profile(&mut decoder))?;
let mut image = report_internal_error!(image::DynamicImage::from_decoder(decoder))?;
// Reset read position
cursor.set_position(0);
@@ -71,38 +83,68 @@ pub async fn strip_metadata(
// Apply the EXIF rotation
// See https://jdhao.github.io/2019/07/31/image_rotation_exif_info/
report_internal_error!(match &rotation {
2 => image?.fliph(),
3 => image?.rotate180(),
4 => image?.rotate180().fliph(),
5 => image?.rotate90().fliph(),
6 => image?.rotate90(),
7 => image?.rotate270().fliph(),
8 => image?.rotate270(),
_ => image?,
}
.write_to(
&mut writer,
match mime {
"image/jpeg" => ImageFormat::Jpeg,
"image/png" => ImageFormat::Png,
"image/avif" => ImageFormat::Avif,
"image/tiff" => ImageFormat::Tiff,
_ => todo!(),
},
))?;
// Calculate dimensions after rotation.
let (width, height) = match &rotation {
2 | 4 | 5 | 7 => (*height, *width),
_ => (*width, *height),
image = match &rotation {
2 => image.fliph(),
3 => image.rotate180(),
4 => image.rotate180().fliph(),
5 => image.rotate90().fliph(),
6 => image.rotate90(),
7 => image.rotate270().fliph(),
8 => image.rotate270(),
_ => image,
};
if let Some(icc) = &icc_profile {
image = apply_icc_profile(image, icc);
icc_profile = None;
}
let color_type = image.color();
let width = image.width();
let height = image.height();
report_internal_error!(match mime {
"image/jpeg" => encode_with_icc!(
image::codecs::jpeg::JpegEncoder::new(&mut writer),
&icc_profile,
image.as_bytes(),
width,
height,
color_type.into()
),
"image/png" => encode_with_icc!(
image::codecs::png::PngEncoder::new(&mut writer),
&icc_profile,
image.as_bytes(),
width,
height,
color_type.into()
),
"image/avif" => {
// avif encoder doesn't implement set_icc_profile currently
image::codecs::avif::AvifEncoder::new(&mut writer).write_image(
image.as_bytes(),
width,
height,
color_type.into(),
)
}
"image/tiff" => encode_with_icc!(
image::codecs::tiff::TiffEncoder::new(&mut writer),
&icc_profile,
image.as_bytes(),
width,
height,
color_type.into()
),
_ => unreachable!(),
})?;
Ok((
bytes,
Metadata::Image {
width,
height,
width: width as isize,
height: height as isize,
thumbhash: thumbhash.clone(),
animated: *animated,
},