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();
+ });
+ });
});