Skip to content

Commit 0b88073

Browse files
committed
Fix preview of images with non srgb icc profile
1 parent c725c91 commit 0b88073

File tree

3 files changed

+32
-5
lines changed

3 files changed

+32
-5
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

yazi-adapter/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ ratatui = { workspace = true }
2727
scopeguard = { workspace = true }
2828
tokio = { workspace = true }
2929
tracing = { workspace = true }
30+
qcms = "0.3.0"
3031

3132
[target.'cfg(target_os = "macos")'.dependencies]
3233
crossterm = { workspace = true, features = [ "use-dev-tty", "libc" ] }

yazi-adapter/src/image.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::path::{Path, PathBuf};
22

3-
use anyhow::Result;
3+
use anyhow::{Context, Result};
44
use image::{DynamicImage, ExtendedColorType, ImageDecoder, ImageEncoder, ImageError, ImageReader, ImageResult, Limits, codecs::{jpeg::JpegEncoder, png::PngEncoder}, imageops::FilterType, metadata::Orientation};
5+
use qcms::{Profile, Transform};
56
use ratatui::layout::Rect;
67
use yazi_config::YAZI;
78
use yazi_fs::provider::{Provider, local::Local};
@@ -31,17 +32,36 @@ impl Image {
3132
encoder.write_image(&rgba, rgba.width(), rgba.height(), ExtendedColorType::Rgba8)?;
3233
} else {
3334
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)?
3640
}
3741

38-
Ok::<_, ImageError>(buf)
42+
Ok::<_, anyhow::Error>(buf)
3943
})
4044
.await??;
4145

4246
Ok(Local::regular(&cache).write(buf).await?)
4347
}
4448

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+
4565
pub(super) async fn downscale(path: PathBuf, rect: Rect) -> Result<DynamicImage> {
4666
let (mut img, orientation, _) = Self::decode_from(path).await?;
4767
let (w, h) = Self::flip_size(orientation, Self::max_pixel(rect));
@@ -115,7 +135,6 @@ impl Image {
115135
let mut decoder = reader.with_guessed_format()?.into_decoder()?;
116136
let orientation = decoder.orientation().unwrap_or(Orientation::NoTransforms);
117137
let icc = decoder.icc_profile().unwrap_or_default();
118-
119138
Ok((DynamicImage::from_decoder(decoder)?, orientation, icc))
120139
})
121140
.await

0 commit comments

Comments
 (0)