mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Use libvips built-in ICC profiles when required #1619
This commit is contained in:
parent
2ed4b5ae83
commit
417cca6e0d
@ -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)
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
BIN
lib/icc/cmyk.icm
BIN
lib/icc/cmyk.icm
Binary file not shown.
BIN
lib/icc/sRGB.icc
BIN
lib/icc/sRGB.icc
Binary file not shown.
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user