lovell / lovell/sharp

Preserve source color space by default, with an explicit opt-out

Open
#4,608 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
JavaScript
Stars
32.7k
Forks
1.4k
Avg merge
1d 14h
Merged PRs (30d)
5

Description

## Feature request

### What are you trying to achieve?
Preserve an image's embedded color profile and color gamut by default when resizing or recompressing it.

Sharp currently uses the input profile to convert pixels to sRGB, then omits the profile. For wide-gamut images such as P3, this can permanently remove colors that sRGB cannot preserve, even when the caller has only requested resizing or compression.

Sharp already supports preserving the source profile with `keepIccProfile()`. I would propose that behavior become the default where the operation and output format support it, with an explicit opt-out for applications that want conversion to sRGB.

This request concerns color information needed to render the image. EXIF, GPS, and other unrelated metadata should continue to be stripped by default.

### When you searched for similar feature requests, what did you find that might be related?

- [#218](https://github.com/lovell/sharp/issues/218) discusses preserving non-sRGB color spaces and acknowledges gamut loss from converting P3 to sRGB.
- [#1323](https://github.com/lovell/sharp/issues/1323) covers custom output ICC profiles and color spaces.
- [#3824](https://github.com/lovell/sharp/issues/3824) and [PR #3856](https://github.com/lovell/sharp/pull/3856) introduced explicit controls such as `keepIccProfile()`, while deliberately retaining the existing defaults.

Those discussions provide opt-in preservation. This request proposes making preservation the default, with an explicit opt-out.

Applications that prefer sRGB output should retain an explicit way to request the current behavior, which is included in the proposal below.

### What would you expect the API to look like?
No additional call should be needed to preserve a valid embedded ICC profile. Ordinary processing should behave as though `.keepIccProfile()` were enabled by default, wherever the operation and output format support it.

Images without an embedded profile would retain their existing behavior. Explicit output color-space or profile requests, including `.withIccProfile(...)`, would continue to take precedence.

There should also be an explicit opt-out that retains today's behavior: convert to sRGB and omit the profile. It should not simply remove the profile from unchanged non-sRGB pixels.

I would defer the opt-out method's name to the maintainers' preferred API conventions.

### What alternatives have you considered?
Applications can already call `keepIccProfile()`, but developers must know to request it. The loss of wide-gamut colors may go unnoticed when testing on an sRGB-only display.

This example compares the current default with explicit profile preservation:

```sh
node <<'NODE'
const sharp = require('sharp');

(async () => {
await sharp('input-p3.jpg')
.jpeg({ quality: 100 })
.toFile('out-default-srgb.jpg'); // Current default: converts to sRGB, reducing the gamut of this P3 image.

await sharp('input-p3.jpg')
.keepIccProfile()
.jpeg({ quality: 100 })
.toFile('out-preserve-profile.jpg');
})().catch(error => {
console.error(error.message);
process.exitCode = 1;
});
NODE
```

Both outputs use the same JPEG quality setting. The second preserves the source color profile; it is still a lossy JPEG recompression.

### Please provide sample image(s) that help explain this feature
Use this image as input-p3.jpg with the code above. The default will show the P3 text is not longer visible as those values are outside the sRGB gamut and therefore clamped (the background in the source show red, green, blue sRGB primary colors encoded in P3 - while the text color uses the full P3 primaries).
Image

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the existing keepIccProfile() API and the Node.js example comparing default output with explicit profile preservation. Trace how embedded ICC profiles are handled during resizing and recompression across supported output formats. Done means preservation is the default where supported, explicit output profile requests still take precedence, and an opt-out retains today's sRGB conversion behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, nodejs
Domain
api, computer-vision
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.