`pipelineColourspace` with a device-independent colourspace has incorrect color management
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 32.7k
- Forks
- 1.4k
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 5
Description
## Possible bug
### Is this a possible bug in a feature of sharp, unrelated to installation?
- [x] Running `npm install sharp` completes without error.
- [x] Running `node -e "import 'sharp'"` completes without error.
If you cannot confirm both of these, please open an [installation issue](https://github.com/lovell/sharp/issues/new?labels=installation&template=installation.md) instead.
### Are you using the latest version of sharp?
- [x] I am using the latest version of `sharp` as reported by `npm view sharp dist-tags.latest`.
If you cannot confirm this, please upgrade to the latest version and try again before opening an issue.
If you are using another package which depends on a version of `sharp` that is not the latest, please open an issue against that package instead.
### What is the output of running `npx envinfo --binaries --system --npmPackages=sharp --npmGlobalPackages=sharp`?
```
System:
OS: macOS 26.5
CPU: (14) arm64 Apple M4 Max
Memory: 672.61 MB / 36.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 24.15.0 - /Users/mertalev/.local/share/mise/installs/node/24.15.0/bin/node
npm: 11.12.1 - /Users/mertalev/.local/share/mise/installs/node/24.15.0/bin/npm
pnpm: 11.22.0 - /Users/mertalev/.local/share/mise/installs/pnpm/11.22.0/pnpm
npmPackages:
sharp: ^0.35.4 => 0.35.4
```
### Does this problem relate to file caching?
The default behaviour of libvips is to cache input files, which can lead to `EBUSY` or `EPERM` errors on Windows.
Use [`sharp.cache(false)`](https://sharp.pixelplumbing.com/api-utility#cache) to switch this feature off.
- [x] Adding `sharp.cache(false)` does not fix this problem.
### Does this problem relate to images appearing to have been rotated by 90 degrees?
Images that contain EXIF Orientation metadata are not auto-oriented. By default, EXIF metadata is removed.
- To auto-orient pixel values use the parameter-less [`rotate()`](https://sharp.pixelplumbing.com/api-operation#rotate) operation.
- To retain EXIF Orientation use [`keepExif()`](https://sharp.pixelplumbing.com/api-output#keepexif).
- [x] Using `rotate()` or `keepExif()` does not fix this problem.
### What are the steps to reproduce?
Using an image with an ICC profile as input, set a device-independent pipeline colourspace like `pipelineColourspace('xyz')`. The conversion produces incorrect colours / garbage.
Part of this is because sharp runs `EnsureColourspace` before `icc_transform`, so the input is treated as sRGB. The other part is that when `icc_transform` *does* happen, lcms is handed device-independent float input when it expects device-dependent integer input.
### What is the expected behaviour?
It should transform to the pipeline colourspace using the embedded ICC profile and produce correct colours as output.
### Please provide a minimal, standalone code sample, without other dependencies, that demonstrates this problem
```js
import sharp from 'sharp';
const raw = { width: 8, height: 8, channels: 3 };
const data = Buffer.alloc(8 * 8 * 3);
for (let i = 0; i < data.length; i += 3) data.set([200, 60, 30], i);
// one colour, encoded two ways
const srgb = await sharp(data, { raw }).png().toBuffer();
const p3 = await sharp(data, { raw }).png().withIccProfile('p3').toBuffer();
const render = async (src) => [...await sharp(src).pipelineColourspace('xyz').raw().toBuffer()].slice(0, 3);
console.log('from P3: ', await render(p3));
console.log('from sRGB:', await render(srgb));
```
Output:
```
from P3: [ 23, 14, 2 ]
from sRGB: [ 200, 60, 30 ]
```
This affects scrgb, xyz, yxy, lab, labs and lch; srgb and rgb16 are fine.
### Please provide sample image(s) that help explain this problem
The code above works without a sample, but `test/fixtures/p3.png` reproduces the same issue: `pipelineColourspace('srgb')` gives `255,0,0`, `'xyz'` gives `39,17,0`.
### Additional info
#1317 is relevant as it described using the XYZ connection space to handle embedded input profiles. It seems that part never landed for `pipelineColourspace`.
The motivation of the change is to do resampling in linear-light to address [this](https://github.com/immich-app/immich/issues/30879) issue, which proved to be non-trivial in sharp.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the minimal Node.js reproduction and test/fixtures/p3.png, then trace pipelineColourspace('xyz') through the embedded-profile conversion described in the issue. Compare the EnsureColourspace and icc_transform behavior for device-independent spaces, using #1317 for context. Done means the listed colourspaces produce correct transformed values while srgb and rgb16 remain unaffected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- computer-graphics
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100