mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
Ensure images with P3 profiles retain full gamut #2862
This commit is contained in:
parent
60adc110f5
commit
104464c2e0
@ -12,6 +12,9 @@ Requires libvips v8.11.3
|
|||||||
* Ensure background is always premultiplied when compositing.
|
* Ensure background is always premultiplied when compositing.
|
||||||
[#2858](https://github.com/lovell/sharp/issues/2858)
|
[#2858](https://github.com/lovell/sharp/issues/2858)
|
||||||
|
|
||||||
|
* Ensure images with P3 profiles retain full gamut.
|
||||||
|
[#2862](https://github.com/lovell/sharp/issues/2862)
|
||||||
|
|
||||||
### v0.29.0 - 17th August 2021
|
### v0.29.0 - 17th August 2021
|
||||||
|
|
||||||
* Drop support for Node.js 10, now requires Node.js >= 12.13.0.
|
* Drop support for Node.js 10, now requires Node.js >= 12.13.0.
|
||||||
|
@ -291,14 +291,15 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we're using a device-independent colour space
|
// Ensure we're using a device-independent colour space
|
||||||
|
char const *processingProfile = image.interpretation() == VIPS_INTERPRETATION_RGB16 ? "p3" : "srgb";
|
||||||
if (
|
if (
|
||||||
sharp::HasProfile(image) &&
|
sharp::HasProfile(image) &&
|
||||||
image.interpretation() != VIPS_INTERPRETATION_LABS &&
|
image.interpretation() != VIPS_INTERPRETATION_LABS &&
|
||||||
image.interpretation() != VIPS_INTERPRETATION_GREY16
|
image.interpretation() != VIPS_INTERPRETATION_GREY16
|
||||||
) {
|
) {
|
||||||
// Convert to sRGB using embedded profile
|
// Convert to sRGB/P3 using embedded profile
|
||||||
try {
|
try {
|
||||||
image = image.icc_transform("srgb", VImage::option()
|
image = image.icc_transform(processingProfile, VImage::option()
|
||||||
->set("embedded", TRUE)
|
->set("embedded", TRUE)
|
||||||
->set("depth", image.interpretation() == VIPS_INTERPRETATION_RGB16 ? 16 : 8)
|
->set("depth", image.interpretation() == VIPS_INTERPRETATION_RGB16 ? 16 : 8)
|
||||||
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
||||||
@ -306,7 +307,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Ignore failure of embedded profile
|
// Ignore failure of embedded profile
|
||||||
}
|
}
|
||||||
} else if (image.interpretation() == VIPS_INTERPRETATION_CMYK) {
|
} else if (image.interpretation() == VIPS_INTERPRETATION_CMYK) {
|
||||||
image = image.icc_transform("srgb", VImage::option()
|
image = image.icc_transform(processingProfile, VImage::option()
|
||||||
->set("input_profile", "cmyk")
|
->set("input_profile", "cmyk")
|
||||||
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
||||||
}
|
}
|
||||||
@ -715,9 +716,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
// Convert colourspace, pass the current known interpretation so libvips doesn't have to guess
|
// Convert colourspace, pass the current known interpretation so libvips doesn't have to guess
|
||||||
image = image.colourspace(baton->colourspace, VImage::option()->set("source_space", image.interpretation()));
|
image = image.colourspace(baton->colourspace, VImage::option()->set("source_space", image.interpretation()));
|
||||||
// Transform colours from embedded profile to output profile
|
// Transform colours from embedded profile to output profile
|
||||||
if (baton->withMetadata && sharp::HasProfile(image)) {
|
if (baton->withMetadata && sharp::HasProfile(image) && baton->withMetadataIcc.empty()) {
|
||||||
image = image.icc_transform(vips_enum_nick(VIPS_TYPE_INTERPRETATION, baton->colourspace),
|
image = image.icc_transform("srgb", VImage::option()
|
||||||
VImage::option()->set("embedded", TRUE));
|
->set("embedded", TRUE)
|
||||||
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -726,7 +728,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
image = image.icc_transform(
|
image = image.icc_transform(
|
||||||
const_cast<char*>(baton->withMetadataIcc.data()),
|
const_cast<char*>(baton->withMetadataIcc.data()),
|
||||||
VImage::option()
|
VImage::option()
|
||||||
->set("input_profile", "srgb")
|
->set("input_profile", processingProfile)
|
||||||
|
->set("embedded", TRUE)
|
||||||
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
||||||
}
|
}
|
||||||
// Override EXIF Orientation tag
|
// Override EXIF Orientation tag
|
||||||
|
1
test/fixtures/index.js
vendored
1
test/fixtures/index.js
vendored
@ -92,6 +92,7 @@ module.exports = {
|
|||||||
inputPngRGBWithAlpha: getPath('2569067123_aca715a2ee_o.png'), // http://www.flickr.com/photos/grizdave/2569067123/ (same as inputJpg)
|
inputPngRGBWithAlpha: getPath('2569067123_aca715a2ee_o.png'), // http://www.flickr.com/photos/grizdave/2569067123/ (same as inputJpg)
|
||||||
inputPngImageInAlpha: getPath('image-in-alpha.png'), // https://github.com/lovell/sharp/issues/1597
|
inputPngImageInAlpha: getPath('image-in-alpha.png'), // https://github.com/lovell/sharp/issues/1597
|
||||||
inputPngSolidAlpha: getPath('with-alpha.png'), // https://github.com/lovell/sharp/issues/1599
|
inputPngSolidAlpha: getPath('with-alpha.png'), // https://github.com/lovell/sharp/issues/1599
|
||||||
|
inputPngP3: getPath('p3.png'), // https://github.com/lovell/sharp/issues/2862
|
||||||
|
|
||||||
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp
|
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp
|
||||||
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
inputWebPWithTransparency: getPath('5_webp_a.webp'), // http://www.gstatic.com/webp/gallery3/5_webp_a.webp
|
||||||
|
BIN
test/fixtures/p3.png
vendored
Normal file
BIN
test/fixtures/p3.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 610 B |
@ -105,6 +105,25 @@ describe('Colour space conversion', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Convert P3 to sRGB', async () => {
|
||||||
|
const [r, g, b] = await sharp(fixtures.inputPngP3)
|
||||||
|
.raw()
|
||||||
|
.toBuffer();
|
||||||
|
assert.strictEqual(r, 255);
|
||||||
|
assert.strictEqual(g, 0);
|
||||||
|
assert.strictEqual(b, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Passthrough P3', async () => {
|
||||||
|
const [r, g, b] = await sharp(fixtures.inputPngP3)
|
||||||
|
.withMetadata({ icc: 'p3' })
|
||||||
|
.raw()
|
||||||
|
.toBuffer();
|
||||||
|
assert.strictEqual(r, 234);
|
||||||
|
assert.strictEqual(g, 51);
|
||||||
|
assert.strictEqual(b, 34);
|
||||||
|
});
|
||||||
|
|
||||||
it('Invalid pipelineColourspace input', function () {
|
it('Invalid pipelineColourspace input', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user