|
1 | 1 | use std::path::{Path, PathBuf}; |
2 | 2 |
|
3 | | -use anyhow::Result; |
| 3 | +use anyhow::{Context, Result}; |
4 | 4 | use image::{DynamicImage, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageReader, ImageResult, Limits, codecs::{jpeg::JpegEncoder, png::PngEncoder}, imageops::FilterType, metadata::Orientation}; |
| 5 | +use qcms::{Profile, Transform}; |
5 | 6 | use ratatui::layout::Rect; |
6 | 7 | use yazi_config::YAZI; |
7 | 8 | use yazi_fs::provider::{Provider, local::Local}; |
@@ -31,17 +32,36 @@ impl Image { |
31 | 32 | encoder.write_image(&rgba, rgba.width(), rgba.height(), ExtendedColorType::Rgba8)?; |
32 | 33 | } else { |
33 | 34 | let mut encoder = JpegEncoder::new_with_quality(&mut buf, YAZI.preview.image_quality); |
34 | | - icc.map(|b| encoder.set_icc_profile(b)); |
35 | | - encoder.encode_image(&img.into_rgb8())?; |
| 35 | + let width = img.width(); |
| 36 | + let height = img.height(); |
| 37 | + let mut data = img.into_rgb8().into_raw(); |
| 38 | + Self::apply_profile(icc, &mut data)?; |
| 39 | + encoder.encode(&data, width, height, ExtendedColorType::Rgb8)? |
36 | 40 | } |
37 | 41 |
|
38 | | - Ok::<_, ImageError>(buf) |
| 42 | + Ok::<_, anyhow::Error>(buf) |
39 | 43 | }) |
40 | 44 | .await??; |
41 | 45 |
|
42 | 46 | Ok(Local::regular(&cache).write(buf).await?) |
43 | 47 | } |
44 | 48 |
|
| 49 | + fn apply_profile(icc: Option<Vec<u8>>, data: &mut [u8]) -> Result<()> { |
| 50 | + // If ICC profile is present and it's not srgb, perform color transformation |
| 51 | + if let Some(ref icc_profile) = icc |
| 52 | + && let Some(input) = Profile::new_from_slice(icc_profile, false) |
| 53 | + && !input.is_sRGB() |
| 54 | + { |
| 55 | + let mut output = Profile::new_sRGB(); |
| 56 | + output.precache_output_transform(); |
| 57 | + |
| 58 | + let xfm = Transform::new(&input, &output, qcms::DataType::RGB8, qcms::Intent::default()) |
| 59 | + .context("Failed creating the ICC transformer")?; |
| 60 | + xfm.apply(data); |
| 61 | + } |
| 62 | + Ok(()) |
| 63 | + } |
| 64 | + |
45 | 65 | pub(super) async fn downscale(path: PathBuf, rect: Rect) -> Result<DynamicImage> { |
46 | 66 | let (mut img, orientation, _) = Self::decode_from(path).await?; |
47 | 67 | let (w, h) = Self::flip_size(orientation, Self::max_pixel(rect)); |
@@ -115,7 +135,6 @@ impl Image { |
115 | 135 | let mut decoder = reader.with_guessed_format()?.into_decoder()?; |
116 | 136 | let orientation = decoder.orientation().unwrap_or(Orientation::NoTransforms); |
117 | 137 | let icc = decoder.icc_profile().unwrap_or_default(); |
118 | | - |
119 | 138 | Ok((DynamicImage::from_decoder(decoder)?, orientation, icc)) |
120 | 139 | }) |
121 | 140 | .await |
|
0 commit comments