You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
keepGainMap() retains P3 channel gains after converting the base to sRGB #4612
When processing an image without keepIccProfile(), sharp normally converts it to sRGB. I understand that this is intentional.
However, when a P3 JPEG has an RGB gain map that uses the base image's color space, keepGainMap() appears to convert the base to sRGB while keeping the original channel gains. The output is a valid, decodable file, but its reconstructed HDR colors are not a correct conversion of the source to sRGB.
The gains depend on the color space in which they are applied. Keeping the same red, green, and blue gains after changing the base from P3 to sRGB can therefore change the HDR colors, even when the reconstructed colors fit inside sRGB. This can affect simple recompression without any resize or other adjustment.
Is this a possible bug in a feature of sharp, unrelated to installation?
Running npm install sharp completes without error.
Running node -e "import 'sharp'" completes without error.
Are you using the latest version of sharp?
Tested with released sharp 0.35.5, libvips 8.18.7, and libultrahdr 2.0.2 on macOS ARM64.
What is the output of running npx envinfo --binaries --system --npmPackages=sharp --npmGlobalPackages=sharp?
The problem occurs with default cache settings and was also observed with sharp.cache(false).
Does this problem relate to images appearing to have been rotated by 90 degrees?
No. No rotation is needed.
What are the steps to reproduce?
Run the code below with the attached P3 source (image #1).
The result is be attached image #2, which does not match source. The source color can be represented in sRGB, so a correct conversion should preserve it. The SDR base remains the same gray after processing. However, applying the original P3 channel gains to that base in sRGB produces a different HDR color.
What is the expected behaviour?
If sharp converts the base to sRGB while retaining HDR, the gain map should remain compatible with the converted base so that the HDR rendition is also converted correctly.
One approach would be to reconstruct the HDR image, convert the SDR and HDR renditions to the output color space, and generate a new gain map relating them. Preserving the source color interpretation when retaining the existing map is another approach; keepIccProfile() avoids the problem in this test.
Would the best place to address this be the interaction between ICC conversion and keepGainMap() in src/pipeline.cc? This report concerns the retained map's color correctness, rather than changing the general sRGB default for SDR images.
Full HDR color conversion would likely be a significant effort. #4608 may offer a practical solution for ordinary processing: preserving the source profile would keep the existing gain map in its intended color space, avoiding the need to regenerate it. Explicit conversion to another color space while retaining the gain map would still need a defined policy, but supporting that could be considered separately.
Please provide a minimal, standalone code sample, without other dependencies, that demonstrates this problem
constsharp=require('sharp');(async()=>{awaitsharp('input-p3.jpg').keepGainMap().jpeg({quality: 100}).toFile('gainmap-only.jpg');// HDR colors change incorrectly in this testawaitsharp('input-p3.jpg').keepGainMap().keepIccProfile().jpeg({quality: 100}).toFile('gainmap-and-profile.jpg');// Preserves the source P3 color interpretation})().catch(console.error);
Please provide sample image(s) that help explain this problem
These three JPEGs contain gain maps and use the same gray SDR base, [192,192,192]. Their HDR renditions demonstrate the difference most clearly at 4× headroom, equivalent to two stops above SDR white.
P3 source: the original green HDR color.
Current sharp output: the green becomes less saturated and shifts toward yellow.
Expected sRGB reference: closely matches the source at full 4× headroom, with gains recalculated for sRGB. Intermediate-headroom rendering may differ. It was generated by decoding the source with libultrahdr 2.0.2, converting the reconstructed linear-light P3 colors to sRGB using a matrix, and recalculating the gains before re-encoding with libultrahdr. Sharp was used to prepare the JPEG components, but not to perform the HDR color conversion.
The reference was checked numerically with libultrahdr and Photoshop. MacOS ImageIO also decodes all three as HDR and shows the reference much closer to the source, though the decoded values are not identical.
Activity