Skip to content

Conversation

@leiserfg
Copy link

Which issue does this PR resolve?

Currently yazi can't correctly preview images that are not srgb, this PRs transforms the colorspace while making the cache entry so they work fine.

Rationale of this PR

It reads the icc of the image, checks if it's not srgb and if that's the case, makes it into srgb before writing to the cache folder.

This is an example of an image that works fine after the change and didn't before:

P1060211

@leiserfg
Copy link
Author

leiserfg commented Nov 23, 2025

By the way, it was a headache to deal with jemalloc in nix-shell, no idea why it does not compile. I had to disable it while making the PR and then enable it again before pushing.
Edit: added another commit to solve for that.

@sxyazi
Copy link
Owner

sxyazi commented Nov 24, 2025

This change looks reasonable to me, thank you for the patch!

I don't know Nix well, so would like someone else to review the Nix-related change some, would you like to pull it out to a separate PR so it won't block this one?

Copy link
Owner

@sxyazi sxyazi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit

impl Image {
pub async fn precache(src: PathBuf, cache: &Path) -> Result<()> {
let (mut img, orientation, icc) = Self::decode_from(src).await?;
let (mut img, orientation, icc) = Self::decode_from(src.clone()).await?;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let (mut img, orientation, icc) = Self::decode_from(src.clone()).await?;
let (mut img, orientation, icc) = Self::decode_from(src).await?;

icc.map(|b| encoder.set_icc_profile(b));
encoder.encode_image(&img.into_rgb8())?;

let mut data = img.to_rgb8().into_raw();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let mut data = img.to_rgb8().into_raw();
let mut data = img.into_rgb8().into_raw();

Comment on lines 38 to 49
// If ICC profile is present and it's not srgb, perform color transformation
if let Some(ref icc_profile) = icc
&& let Some(input) = Profile::new_from_slice(icc_profile, false)
&& !input.is_sRGB()
{
let mut output = Profile::new_sRGB();
output.precache_output_transform();

let xfm = Transform::new(&input, &output, qcms::DataType::RGB8, qcms::Intent::default())
.context("Failed creating the ICC transformer")?;
xfm.apply(&mut data);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please pull these out into a new function called apply_profile

@leiserfg
Copy link
Author

would you like to pull it out to a separate PR so it won't block this one?

The thing is that without this change I can't build in the nix shell, cargo fails to build jemalloc. The change is only to tell jemalloc-sys where to find the pre built one.

@sxyazi
Copy link
Owner

sxyazi commented Nov 24, 2025

That sounds like a problem specific to your environment since our CI passed the build and other people seem not to have reported similar issues.

CC @XYenon who is the maintainer of Yazi's Nix package - do you think that change makes sense?

@leiserfg
Copy link
Author

That sounds like a problem specific to your environment since our CI

In CI it uses nix build which does work fine cause it's replacing rust-jemalloc-sys with the one ni nixpkgs, same thing I did here for nix-shell. Still if you don't like it I can remove it after you approve the other changes, or move it to another pr.

@sxyazi sxyazi force-pushed the main branch 2 times, most recently from 41034d0 to 449450d Compare November 24, 2025 07:23
@XYenon
Copy link
Contributor

XYenon commented Nov 24, 2025

By the way, it was a headache to deal with jemalloc in nix-shell, no idea why it does not compile. I had to disable it while making the PR and then enable it again before pushing.

What error message occurred on which platform?

@leiserfg
Copy link
Author

leiserfg commented Nov 24, 2025

By the way, it was a headache to deal with jemalloc in nix-shell, no idea why it does not compile. I had to disable it while making the PR and then enable it again before pushing.

What error message occurred on which platform?

In nix develop / nix-shell / direnv, cargo build triggers a compilation of jemalloc and that breaks (the configure step cant figure out the type of stderr_r). I don't think solving that in this project is worthy given that nixpkgs ships a jemalloc pre-built.

I'm in nixos btw.

@XYenon
Copy link
Contributor

XYenon commented Nov 24, 2025

I found the issue because shell.nix references yazi.nix, and yazi.nix is a wrapper created by directly referencing the path of the yazi-unwrapped package. Nix cannot infer the dependencies that should be introduced by the yazi-unwrapped package through the wrapper. I will submit a fix shortly.

@XYenon XYenon mentioned this pull request Nov 24, 2025
@leiserfg
Copy link
Author

Ok, then I will rebase the PR and remove the commit for the jemalloc thingy as soon as the one from @XYenon gets merged.

@sxyazi sxyazi closed this in #3363 Nov 24, 2025
@sxyazi sxyazi reopened this Nov 24, 2025
let rgba = img.into_rgba8();
let mut encoder = PngEncoder::new(&mut buf);
icc.map(|b| encoder.set_icc_profile(b));
encoder.write_image(&rgba, rgba.width(), rgba.height(), ExtendedColorType::Rgba8)?;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the same process of sRGB to RGB(A) conversion be applied to the PNG encoder as well for transparent images?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't do it there as I don't know of a case where that affects images with alpha, therefore I don't have how to test it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think I found some, I'll give it a try.

let width = img.width();
let height = img.height();
let mut data = img.into_rgb8().into_raw();
Self::apply_profile(icc, &mut data)?;
Copy link
Owner

@sxyazi sxyazi Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that apply_profile only applied to the precache function, which means it will only work if the user doesn't disable the image preloader and the image has generated a cache at the time, which is problematic as image preview should behave the same way regardless of whether it has cached, i.e. it should also be implemented for downscale which is directlly called by image_show (instead of image_precache), maybe this process should placed under decode_from which precache and downscale both call

@leiserfg
Copy link
Author

I don't like the _decode name either, so let me know if you have any alternative.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants