diff --git a/docs/changelog.md b/docs/changelog.md index daa1dd84..57bc81a5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,12 @@ Requires libvips v8.6.1. +#### v0.20.3 - TBD + +* Fix tint operation by ensuring LAB interpretation and allowing negative values. + [#1235](https://github.com/lovell/sharp/issues/1235) + [@wezside](https://github.com/wezside) + #### v0.20.2 - 28th April 2018 * Add tint operation to set image chroma. diff --git a/lib/constructor.js b/lib/constructor.js index 2ec20f8c..7efa8605 100644 --- a/lib/constructor.js +++ b/lib/constructor.js @@ -152,8 +152,8 @@ const Sharp = function (input, options) { fastShrinkOnLoad: true, // operations background: [0, 0, 0, 255], - tintA: 0, - tintB: 0, + tintA: 128, + tintB: 128, flatten: false, negate: false, medianSize: 0, diff --git a/src/operations.cc b/src/operations.cc index da5f56d7..819bbbf0 100644 --- a/src/operations.cc +++ b/src/operations.cc @@ -161,13 +161,14 @@ namespace sharp { if (typeBeforeTint == VIPS_INTERPRETATION_RGB) { typeBeforeTint = VIPS_INTERPRETATION_sRGB; } - // Create 2 band image with every pixel set to the tint chroma - std::vector chromaPixel {a, b}; - VImage chroma = image.new_from_image(chromaPixel); // Extract luminance VImage luminance = image.colourspace(VIPS_INTERPRETATION_LAB)[0]; // Create the tinted version by combining the L from the original and the chroma from the tint - VImage tinted = luminance.bandjoin(chroma).colourspace(typeBeforeTint); + std::vector chroma {a, b}; + VImage tinted = luminance + .bandjoin(chroma) + .copy(VImage::option()->set("interpretation", VIPS_INTERPRETATION_LAB)) + .colourspace(typeBeforeTint); // Attach original alpha channel, if any if (HasAlpha(image)) { // Extract original alpha channel diff --git a/src/pipeline.cc b/src/pipeline.cc index abd5ab1d..b99de41f 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -683,7 +683,7 @@ class PipelineWorker : public Nan::AsyncWorker { } // Tint the image - if (baton->tintA > 0 || baton->tintB > 0) { + if (baton->tintA < 128.0 || baton->tintB < 128.0) { image = sharp::Tint(image, baton->tintA, baton->tintB); } diff --git a/src/pipeline.h b/src/pipeline.h index 0ae147dc..74e12706 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -157,8 +157,8 @@ struct PipelineBaton { cropOffsetLeft(0), cropOffsetTop(0), premultiplied(false), - tintA(0.0), - tintB(0.0), + tintA(128.0), + tintB(128.0), flatten(false), negate(false), blurSigma(0.0), diff --git a/test/fixtures/expected/tint-blue.jpg b/test/fixtures/expected/tint-blue.jpg new file mode 100644 index 00000000..f6b3a33e Binary files /dev/null and b/test/fixtures/expected/tint-blue.jpg differ diff --git a/test/fixtures/expected/tint-cmyk.jpg b/test/fixtures/expected/tint-cmyk.jpg new file mode 100644 index 00000000..087099c2 Binary files /dev/null and b/test/fixtures/expected/tint-cmyk.jpg differ diff --git a/test/fixtures/expected/tint-green.jpg b/test/fixtures/expected/tint-green.jpg new file mode 100644 index 00000000..bff7df28 Binary files /dev/null and b/test/fixtures/expected/tint-green.jpg differ diff --git a/test/unit/tint.js b/test/unit/tint.js index 7e18b920..582b501c 100644 --- a/test/unit/tint.js +++ b/test/unit/tint.js @@ -19,6 +19,32 @@ describe('Tint', function () { }); }); + it('tints rgb image green', function (done) { + const output = fixtures.path('output.tint-green.jpg'); + sharp(fixtures.inputJpg) + .resize(320, 240) + .tint('#00FF00') + .toFile(output, function (err, info) { + if (err) throw err; + assert.strictEqual(true, info.size > 0); + fixtures.assertMaxColourDistance(output, fixtures.expected('tint-green.jpg'), 10); + done(); + }); + }); + + it('tints rgb image blue', function (done) { + const output = fixtures.path('output.tint-blue.jpg'); + sharp(fixtures.inputJpg) + .resize(320, 240) + .tint('#0000FF') + .toFile(output, function (err, info) { + if (err) throw err; + assert.strictEqual(true, info.size > 0); + fixtures.assertMaxColourDistance(output, fixtures.expected('tint-blue.jpg'), 10); + done(); + }); + }); + it('tints rgb image with sepia tone', function (done) { const output = fixtures.path('output.tint-sepia.jpg'); sharp(fixtures.inputJpg) @@ -60,4 +86,17 @@ describe('Tint', function () { done(); }); }); + + it('tints cmyk image red', function (done) { + const output = fixtures.path('output.tint-cmyk.jpg'); + sharp(fixtures.inputJpgWithCmykProfile) + .resize(320, 240) + .tint('#FF0000') + .toFile(output, function (err, info) { + if (err) throw err; + assert.strictEqual(true, info.size > 0); + fixtures.assertMaxColourDistance(output, fixtures.expected('tint-cmyk.jpg'), 10); + done(); + }); + }); });