Implements negation.

This commit is contained in:
David Carley 2015-11-17 10:18:59 -06:00
parent 5dfeaa9fd1
commit 33a175eafb
10 changed files with 130 additions and 0 deletions

View File

@ -272,6 +272,10 @@ The default background is `{r: 0, g: 0, b: 0, a: 1}`, black without transparency
Merge alpha transparency channel, if any, with `background`.
#### negate()
Produces the "negative" of the image. White => Black, Black => White, Blue => Yellow, etc.
#### rotate([angle])
Rotate the output image by either an explicit angle or auto-orient based on the EXIF `Orientation` tag.

View File

@ -58,6 +58,7 @@ var Sharp = function(input) {
// operations
background: [0, 0, 0, 255],
flatten: false,
negate: false,
blurSigma: 0,
sharpenRadius: 0,
sharpenFlat: 1,
@ -215,6 +216,11 @@ Sharp.prototype.flatten = function(flatten) {
return this;
};
Sharp.prototype.negate = function(negate) {
this.options.negate = (typeof negate === 'boolean') ? negate : true;
return this;
};
Sharp.prototype.overlayWith = function(overlayPath) {
if (typeof overlayPath !== 'string') {
throw new Error('The overlay path must be a string');

View File

@ -100,6 +100,7 @@ struct PipelineBaton {
std::string interpolator;
double background[4];
bool flatten;
bool negate;
double blurSigma;
int sharpenRadius;
double sharpenFlat;
@ -138,6 +139,7 @@ struct PipelineBaton {
canvas(Canvas::CROP),
gravity(0),
flatten(false),
negate(false),
blurSigma(0.0),
sharpenRadius(0),
sharpenFlat(1.0),
@ -451,6 +453,16 @@ class PipelineWorker : public AsyncWorker {
image = flattened;
}
// Negate the colors in the image.
if (baton->negate) {
VipsImage *negated;
if (vips_invert(image, &negated, nullptr)) {
return Error();
}
vips_object_local(hook, negated);
image = negated;
}
// Gamma encoding (darken)
if (baton->gamma >= 1 && baton->gamma <= 3 && !HasAlpha(image)) {
VipsImage *gammaEncoded;
@ -1212,6 +1224,7 @@ NAN_METHOD(pipeline) {
baton->interpolator = *Utf8String(Get(options, New("interpolator").ToLocalChecked()).ToLocalChecked());
// Operators
baton->flatten = To<bool>(Get(options, New("flatten").ToLocalChecked()).ToLocalChecked()).FromJust();
baton->negate = To<bool>(Get(options, New("negate").ToLocalChecked()).ToLocalChecked()).FromJust();
baton->blurSigma = To<double>(Get(options, New("blurSigma").ToLocalChecked()).ToLocalChecked()).FromJust();
baton->sharpenRadius = To<int32_t>(Get(options, New("sharpenRadius").ToLocalChecked()).ToLocalChecked()).FromJust();
baton->sharpenFlat = To<double>(Get(options, New("sharpenFlat").ToLocalChecked()).ToLocalChecked()).FromJust();

BIN
test/fixtures/expected/negate-alpha.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
test/fixtures/expected/negate-trans.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
test/fixtures/expected/negate-trans.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
test/fixtures/expected/negate.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
test/fixtures/expected/negate.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
test/fixtures/expected/negate.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

107
test/unit/negate.js Normal file
View File

@ -0,0 +1,107 @@
'use strict';
var assert = require('assert');
var sharp = require('../../index');
var fixtures = require('../fixtures');
sharp.cache(0);
describe('Negate', function() {
it('negate (jpeg)', function(done) {
sharp(fixtures.inputJpg)
.resize(320, 240)
.negate()
.toBuffer(function(err, data, info) {
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
fixtures.assertSimilar(fixtures.expected('negate.jpg'), data, done);
});
});
it('negate (png)', function(done) {
sharp(fixtures.inputPng)
.resize(320, 240)
.negate()
.toBuffer(function(err, data, info) {
assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
fixtures.assertSimilar(fixtures.expected('negate.png'), data, done);
});
});
it('negate (png, trans)', function(done) {
sharp(fixtures.inputPngWithTransparency)
.resize(320, 240)
.negate()
.toBuffer(function(err, data, info) {
assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
fixtures.assertSimilar(fixtures.expected('negate-trans.png'), data, done);
});
});
it('negate (png, alpha)', function(done) {
sharp(fixtures.inputPngWithGreyAlpha)
.resize(320, 240)
.negate()
.toBuffer(function(err, data, info) {
assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
fixtures.assertSimilar(fixtures.expected('negate-alpha.png'), data, done);
});
});
if (sharp.format.webp.output.file) {
it('negate (webp)', function(done) {
sharp(fixtures.inputWebP)
.resize(320, 240)
.negate()
.toBuffer(function(err, data, info) {
assert.strictEqual('webp', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
fixtures.assertSimilar(fixtures.expected('negate.webp'), data, done);
});
});
it('negate (webp, trans)', function(done) {
sharp(fixtures.inputWebPWithTransparency)
.resize(320, 240)
.negate()
.toBuffer(function(err, data, info) {
assert.strictEqual('webp', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
fixtures.assertSimilar(fixtures.expected('negate-trans.webp'), data, done);
});
});
}
it('negate (true)', function(done) {
sharp(fixtures.inputJpg)
.resize(320, 240)
.negate(true)
.toBuffer(function(err, data, info) {
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
fixtures.assertSimilar(fixtures.expected('negate.jpg'), data, done);
});
});
it('negate (false)', function(done) {
var output = fixtures.path('output.unmodified-by-negate.png');
sharp(fixtures.inputJpgWithLowContrast)
.negate(false)
.toFile(output, function(err, info) {
if (err) done(err);
fixtures.assertMaxColourDistance(output, fixtures.inputJpgWithLowContrast, 0);
done();
});
});
});