Skip to content

Commit

Permalink
Gamma correction and premultiply do not mix
Browse files Browse the repository at this point in the history
Skip premultiply for fast blur/sharpen

Automate gamma correction tests
  • Loading branch information
lovell committed Aug 11, 2015
1 parent 9c83d98 commit 36ac882
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 39 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sharp",
"version": "0.11.0",
"version": "0.11.1",
"author": "Lovell Fuller <npm@lovell.info>",
"contributors": [
"Pierre Inglebert <pierre.inglebert@gmail.com>",
Expand Down Expand Up @@ -51,7 +51,7 @@
"semver": "^5.0.1"
},
"devDependencies": {
"async": "^1.3.0",
"async": "^1.4.2",
"coveralls": "^2.11.2",
"exif-reader": "1.0.0",
"icc": "^0.0.2",
Expand Down
50 changes: 24 additions & 26 deletions src/pipeline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ class PipelineWorker : public NanAsyncWorker {
}

// Gamma encoding (darken)
if (baton->gamma >= 1 && baton->gamma <= 3) {
if (baton->gamma >= 1 && baton->gamma <= 3 && !HasAlpha(image)) {
VipsImage *gammaEncoded;
if (vips_gamma(image, &gammaEncoded, "exponent", 1.0 / baton->gamma, NULL)) {
return Error();
Expand Down Expand Up @@ -493,11 +493,9 @@ class PipelineWorker : public NanAsyncWorker {
}

bool shouldAffineTransform = xresidual != 0.0 || yresidual != 0.0;
bool shouldBlur = baton->blurSigma != 0.0;
bool shouldSharpen = baton->sharpenRadius != 0;
bool shouldTransform = shouldAffineTransform || shouldBlur || shouldSharpen;
bool willConvolve = baton->blurSigma > 0.0 || baton->sharpenRadius > 0;
bool hasOverlay = !baton->overlayPath.empty();
bool shouldPremultiplyAlpha = HasAlpha(image) && image->Bands == 4 && (shouldTransform || hasOverlay);
bool shouldPremultiplyAlpha = HasAlpha(image) && (shouldAffineTransform || willConvolve || hasOverlay);

// Premultiply image alpha channel before all transformations to avoid
// dark fringing around bright pixels
Expand Down Expand Up @@ -681,7 +679,7 @@ class PipelineWorker : public NanAsyncWorker {
}

// Blur
if (shouldBlur) {
if (baton->blurSigma != 0.0) {
VipsImage *blurred;
if (Blur(hook, image, &blurred, baton->blurSigma)) {
return Error();
Expand All @@ -690,33 +688,14 @@ class PipelineWorker : public NanAsyncWorker {
}

// Sharpen
if (shouldSharpen) {
if (baton->sharpenRadius != 0) {
VipsImage *sharpened;
if (Sharpen(hook, image, &sharpened, baton->sharpenRadius, baton->sharpenFlat, baton->sharpenJagged)) {
return Error();
}
image = sharpened;
}

// Gamma decoding (brighten)
if (baton->gamma >= 1 && baton->gamma <= 3) {
VipsImage *gammaDecoded;
if (vips_gamma(image, &gammaDecoded, "exponent", baton->gamma, NULL)) {
return Error();
}
vips_object_local(hook, gammaDecoded);
image = gammaDecoded;
}

// Apply normalization - stretch luminance to cover full dynamic range
if (baton->normalize) {
VipsImage *normalized;
if (Normalize(hook, image, &normalized)) {
return Error();
}
image = normalized;
}

// Composite with overlay, if present
if (hasOverlay) {
VipsImage *overlayImage = NULL;
Expand Down Expand Up @@ -789,6 +768,25 @@ class PipelineWorker : public NanAsyncWorker {
image = imageUnpremultiplied;
}

// Gamma decoding (brighten)
if (baton->gamma >= 1 && baton->gamma <= 3 && !HasAlpha(image)) {
VipsImage *gammaDecoded;
if (vips_gamma(image, &gammaDecoded, "exponent", baton->gamma, NULL)) {
return Error();
}
vips_object_local(hook, gammaDecoded);
image = gammaDecoded;
}

// Apply normalization - stretch luminance to cover full dynamic range
if (baton->normalize) {
VipsImage *normalized;
if (Normalize(hook, image, &normalized)) {
return Error();
}
image = normalized;
}

// Convert image to sRGB, if not already
if (image->Type != VIPS_INTERPRETATION_sRGB) {
// Switch interpretation to sRGB
Expand Down
Binary file added test/fixtures/expected/gamma-0.0.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/expected/gamma-2.2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/expected/gamma-3.0.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/expected/gamma-alpha.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 32 additions & 11 deletions test/unit/gamma.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,53 @@ describe('Gamma correction', function() {
it('value of 0.0 (disabled)', function(done) {
sharp(fixtures.inputJpgWithGammaHoliness)
.resize(129, 111)
.toFile(fixtures.path('output.gamma-0.0.jpg'), done);
.toBuffer(function(err, data, info) {
assert.strictEqual('jpeg', info.format);
assert.strictEqual(129, info.width);
assert.strictEqual(111, info.height);
fixtures.assertSimilar(fixtures.expected('gamma-0.0.jpg'), data, done);
});
});

it('value of 2.2 (default)', function(done) {
sharp(fixtures.inputJpgWithGammaHoliness)
.resize(129, 111)
.gamma()
.toFile(fixtures.path('output.gamma-2.2.jpg'), done);
.toBuffer(function(err, data, info) {
assert.strictEqual('jpeg', info.format);
assert.strictEqual(129, info.width);
assert.strictEqual(111, info.height);
fixtures.assertSimilar(fixtures.expected('gamma-2.2.jpg'), data, done);
});
});

it('value of 3.0', function(done) {
sharp(fixtures.inputJpgWithGammaHoliness)
.resize(129, 111)
.gamma(3)
.toFile(fixtures.path('output.gamma-3.0.jpg'), done);
.toBuffer(function(err, data, info) {
assert.strictEqual('jpeg', info.format);
assert.strictEqual(129, info.width);
assert.strictEqual(111, info.height);
fixtures.assertSimilar(fixtures.expected('gamma-3.0.jpg'), data, done);
});
});

it('invalid value', function(done) {
var isValid = true;
try {
it('alpha transparency', function(done) {
sharp(fixtures.inputPngOverlayLayer1)
.resize(320)
.gamma()
.toBuffer(function(err, data, info) {
assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width);
fixtures.assertSimilar(fixtures.expected('gamma-alpha.jpg'), data, done);
});
});

it('invalid value', function() {
assert.throws(function() {
sharp(fixtures.inputJpgWithGammaHoliness).gamma(4);
} catch (err) {
isValid = false;
}
assert.strictEqual(false, isValid);
done();
});
});

});

1 comment on commit 36ac882

@lovell
Copy link
Owner Author

@lovell lovell commented on 36ac882 Aug 11, 2015

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added note to #152 to track the work to make this behave.

Please sign in to comment.