Use libvips built-in ICC profiles when required #1619

This commit is contained in:
Lovell Fuller 2019-07-29 14:16:21 +01:00
parent 2ed4b5ae83
commit 417cca6e0d
7 changed files with 17 additions and 24 deletions

View File

@ -17,6 +17,9 @@ Requires libvips v8.8.1.
* Add experimental support for Worker Threads. * Add experimental support for Worker Threads.
[#1558](https://github.com/lovell/sharp/issues/1558) [#1558](https://github.com/lovell/sharp/issues/1558)
* Use libvips' built-in CMYK and sRGB profiles when required.
[#1619](https://github.com/lovell/sharp/issues/1619)
* Drop support for Node.js versions 6 and 11. * Drop support for Node.js versions 6 and 11.
[#1674](https://github.com/lovell/sharp/issues/1674) [#1674](https://github.com/lovell/sharp/issues/1674)

View File

@ -1,6 +1,5 @@
'use strict'; 'use strict';
const path = require('path');
const util = require('util'); const util = require('util');
const stream = require('stream'); const stream = require('stream');
const events = require('events'); const events = require('events');
@ -111,8 +110,6 @@ const Sharp = function (input, options) {
// input options // input options
sequentialRead: false, sequentialRead: false,
limitInputPixels: Math.pow(0x3FFF, 2), limitInputPixels: Math.pow(0x3FFF, 2),
// ICC profiles
iccProfilePath: path.join(__dirname, 'icc') + path.sep,
// resize options // resize options
topOffsetPre: -1, topOffsetPre: -1,
leftOffsetPre: -1, leftOffsetPre: -1,

Binary file not shown.

Binary file not shown.

View File

@ -70,16 +70,6 @@ class PipelineWorker : public Nan::AsyncWorker {
// Increment processing task counter // Increment processing task counter
g_atomic_int_inc(&sharp::counterProcess); g_atomic_int_inc(&sharp::counterProcess);
std::map<VipsInterpretation, std::string> profileMap;
// Default sRGB ICC profile from https://packages.debian.org/sid/all/icc-profiles-free/filelist
profileMap.insert(
std::pair<VipsInterpretation, std::string>(VIPS_INTERPRETATION_sRGB,
baton->iccProfilePath + "sRGB.icc"));
// Convert to sRGB using default CMYK profile from http://www.argyllcms.com/cmyk.icm
profileMap.insert(
std::pair<VipsInterpretation, std::string>(VIPS_INTERPRETATION_CMYK,
baton->iccProfilePath + "cmyk.icm"));
try { try {
// Open input // Open input
vips::VImage image; vips::VImage image;
@ -321,17 +311,15 @@ class PipelineWorker : public Nan::AsyncWorker {
if (sharp::HasProfile(image) && image.interpretation() != VIPS_INTERPRETATION_LABS) { if (sharp::HasProfile(image) && image.interpretation() != VIPS_INTERPRETATION_LABS) {
// Convert to sRGB using embedded profile // Convert to sRGB using embedded profile
try { try {
image = image.icc_transform( image = image.icc_transform("srgb", VImage::option()
const_cast<char*>(profileMap[VIPS_INTERPRETATION_sRGB].data()), VImage::option()
->set("embedded", TRUE) ->set("embedded", TRUE)
->set("intent", VIPS_INTENT_PERCEPTUAL)); ->set("intent", VIPS_INTENT_PERCEPTUAL));
} catch(...) { } catch(...) {
// 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( image = image.icc_transform("srgb", VImage::option()
const_cast<char*>(profileMap[VIPS_INTERPRETATION_sRGB].data()), VImage::option() ->set("input_profile", "cmyk")
->set("input_profile", profileMap[VIPS_INTERPRETATION_CMYK].data())
->set("intent", VIPS_INTENT_PERCEPTUAL)); ->set("intent", VIPS_INTENT_PERCEPTUAL));
} }
@ -701,8 +689,8 @@ class PipelineWorker : public Nan::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) && profileMap[baton->colourspace] != std::string()) { if (baton->withMetadata && sharp::HasProfile(image)) {
image = image.icc_transform(const_cast<char*>(profileMap[baton->colourspace].data()), image = image.icc_transform(vips_enum_nick(VIPS_TYPE_INTERPRETATION, baton->colourspace),
VImage::option()->set("embedded", TRUE)); VImage::option()->set("embedded", TRUE));
} }
} }
@ -1196,9 +1184,6 @@ NAN_METHOD(pipeline) {
// Input // Input
baton->input = CreateInputDescriptor(AttrAs<v8::Object>(options, "input"), buffersToPersist); baton->input = CreateInputDescriptor(AttrAs<v8::Object>(options, "input"), buffersToPersist);
// ICC profile to use when input CMYK image has no embedded profile
baton->iccProfilePath = AttrAsStr(options, "iccProfilePath");
baton->accessMethod = AttrTo<bool>(options, "sequentialRead") ? baton->accessMethod = AttrTo<bool>(options, "sequentialRead") ?
VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM; VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
// Limit input images to a given number of pixels, where pixels = width * height // Limit input images to a given number of pixels, where pixels = width * height

View File

@ -53,7 +53,6 @@ struct Composite {
struct PipelineBaton { struct PipelineBaton {
sharp::InputDescriptor *input; sharp::InputDescriptor *input;
std::string iccProfilePath;
int limitInputPixels; int limitInputPixels;
std::string formatOut; std::string formatOut;
std::string fileOut; std::string fileOut;

View File

@ -394,6 +394,15 @@ describe('Image Stats', function () {
}); });
}); });
it('CMYK input without profile', () =>
sharp(fixtures.inputJpgWithCmykNoProfile)
.stats()
.then(stats => {
assert.strictEqual(4, stats.channels.length);
assert.strictEqual(true, stats.isOpaque);
})
);
it('Stream in, Callback out', function (done) { it('Stream in, Callback out', function (done) {
const readable = fs.createReadStream(fixtures.inputJpg); const readable = fs.createReadStream(fixtures.inputJpg);
const pipeline = sharp().stats(function (err, stats) { const pipeline = sharp().stats(function (err, stats) {