diff --git a/docs/changelog.md b/docs/changelog.md
index 41a67fd7..690e7f56 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -2,9 +2,20 @@
### v0.13 - "*mind*"
+* Switch from libvips' C to C++ bindings, requires upgrade to v8.2.2.
+ [#299](https://github.com/lovell/sharp/issues/299)
+
* Control number of open files in libvips' cache; breaks existing `cache` behaviour.
[#315](https://github.com/lovell/sharp/issues/315)
+* Ensure 16-bit input images can be embedded onto a transparent background.
+ [#340](https://github.com/lovell/sharp/issues/340)
+ [@janaz](https://github.com/janaz)
+
+* Small optimisation when reducing by an integral factor to favour shrink over affine.
+
+* Add support for gamma correction of images with an alpha channel.
+
### v0.12 - "*look*"
#### v0.12.2 - 16th January 2016
diff --git a/package.json b/package.json
old mode 100755
new mode 100644
index 91fef593..6742734b
--- a/package.json
+++ b/package.json
@@ -47,11 +47,11 @@
"vips"
],
"dependencies": {
- "bluebird": "^3.1.1",
+ "bluebird": "^3.1.5",
"color": "^0.11.1",
"nan": "^2.2.0",
"semver": "^5.1.0",
- "request": "^2.67.0",
+ "request": "^2.69.0",
"tar": "^2.2.1"
},
"devDependencies": {
@@ -60,10 +60,10 @@
"exif-reader": "^1.0.0",
"icc": "^0.0.2",
"istanbul": "^0.4.2",
- "mocha": "^2.3.4",
- "mocha-jshint": "^2.2.6",
+ "mocha": "^2.4.5",
+ "mocha-jshint": "^2.3.0",
"node-cpplint": "^0.4.0",
- "rimraf": "^2.5.0",
+ "rimraf": "^2.5.1",
"bufferutil": "^1.2.1"
},
"license": "Apache-2.0",
diff --git a/src/metadata.h b/src/metadata.h
old mode 100755
new mode 100644
diff --git a/src/operations.cc b/src/operations.cc
index 1a6bb4f0..16fdec9e 100644
--- a/src/operations.cc
+++ b/src/operations.cc
@@ -102,11 +102,25 @@ namespace sharp {
return image;
}
+ /*
+ * Gamma encoding/decoding
+ */
+ VImage Gamma(VImage image, double const exponent) {
+ if (HasAlpha(image)) {
+ // Separate alpha channel
+ VImage imageWithoutAlpha = image.extract_band(0,
+ VImage::option()->set("n", image.bands() - 1));
+ VImage alpha = image[image.bands() - 1];
+ return imageWithoutAlpha.gamma(VImage::option()->set("exponent", exponent)).bandjoin(alpha);
+ } else {
+ return image.gamma(VImage::option()->set("exponent", exponent));
+ }
+ }
+
/*
* Gaussian blur (use sigma <0 for fast blur)
*/
VImage Blur(VImage image, double const sigma) {
- VImage blurred;
if (sigma < 0.0) {
// Fast, mild blur - averages neighbouring pixels
VImage blur = VImage::new_matrixv(3, 3,
diff --git a/src/operations.h b/src/operations.h
index 3ce63036..ba69b25d 100644
--- a/src/operations.h
+++ b/src/operations.h
@@ -18,6 +18,11 @@ namespace sharp {
*/
VImage Normalize(VImage image);
+ /*
+ * Gamma encoding/decoding
+ */
+ VImage Gamma(VImage image, double const exponent);
+
/*
* Gaussian blur. Use sigma of -1 for fast blur.
*/
diff --git a/src/pipeline.cc b/src/pipeline.cc
index c1d65c84..b5340d12 100644
--- a/src/pipeline.cc
+++ b/src/pipeline.cc
@@ -43,6 +43,7 @@ using vips::VError;
using sharp::Composite;
using sharp::Normalize;
+using sharp::Gamma;
using sharp::Blur;
using sharp::Sharpen;
@@ -425,7 +426,8 @@ class PipelineWorker : public AsyncWorker {
}
// Calculate maximum alpha value based on input image pixel depth
- double maxAlpha = (image.format() == VIPS_FORMAT_USHORT) ? 65535.0 : 255.0;
+ bool is16Bit = (image.format() == VIPS_FORMAT_USHORT);
+ double maxAlpha = is16Bit ? 65535.0 : 255.0;
// Flatten image to remove alpha channel
if (baton->flatten && HasAlpha(image)) {
@@ -449,8 +451,8 @@ class PipelineWorker : public AsyncWorker {
}
// Gamma encoding (darken)
- if (baton->gamma >= 1 && baton->gamma <= 3 && !HasAlpha(image)) {
- image = image.gamma(VImage::option()->set("exponent", 1.0 / baton->gamma));
+ if (baton->gamma >= 1 && baton->gamma <= 3) {
+ image = Gamma(image, 1.0 / baton->gamma);
}
// Convert to greyscale (linear, therefore after gamma encoding, if any)
@@ -541,10 +543,6 @@ class PipelineWorker : public AsyncWorker {
// Crop/embed
if (image.width() != baton->width || image.height() != baton->height) {
if (baton->canvas == Canvas::EMBED) {
- // Add non-transparent alpha channel, if required
- if (baton->background[3] < 255.0 && !HasAlpha(image)) {
- image = image.bandjoin(VImage::black(image.width(), image.height()).invert());
- }
// Scale up 8-bit values to match 16-bit input image
double multiplier = (image.interpretation() == VIPS_INTERPRETATION_RGB16) ? 256.0 : 1.0;
// Create background colour
@@ -557,6 +555,12 @@ class PipelineWorker : public AsyncWorker {
if (baton->background[3] < 255.0 || HasAlpha(image)) {
background.push_back(baton->background[3] * multiplier);
}
+ // Add non-transparent alpha channel, if required
+ if (baton->background[3] < 255.0 && !HasAlpha(image)) {
+ VImage alpha = VImage::new_matrix(image.width(), image.height())
+ .new_from_image(baton->background[3] * multiplier);
+ image = image.bandjoin(alpha);
+ }
// Embed
int left = static_cast(round((baton->width - image.width()) / 2));
int top = static_cast(round((baton->height - image.height()) / 2));
@@ -639,11 +643,17 @@ class PipelineWorker : public AsyncWorker {
// Reverse premultiplication after all transformations:
if (shouldPremultiplyAlpha) {
image = image.unpremultiply(VImage::option()->set("max_alpha", maxAlpha));
+ // Cast pixel values to integer
+ if (is16Bit) {
+ image = image.cast(VIPS_FORMAT_USHORT);
+ } else {
+ image = image.cast(VIPS_FORMAT_UCHAR);
+ }
}
// Gamma decoding (brighten)
- if (baton->gamma >= 1 && baton->gamma <= 3 && !HasAlpha(image)) {
- image = image.gamma(VImage::option()->set("exponent", baton->gamma));
+ if (baton->gamma >= 1 && baton->gamma <= 3) {
+ image = Gamma(image, baton->gamma);
}
// Apply normalization - stretch luminance to cover full dynamic range
diff --git a/src/pipeline.h b/src/pipeline.h
old mode 100755
new mode 100644
diff --git a/test/fixtures/expected/embed-16bit-rgba.png b/test/fixtures/expected/embed-16bit-rgba.png
new file mode 100644
index 00000000..70e3f365
Binary files /dev/null and b/test/fixtures/expected/embed-16bit-rgba.png differ
diff --git a/test/fixtures/expected/gamma-alpha.jpg b/test/fixtures/expected/gamma-alpha.jpg
index 08b54565..aabde8a9 100644
Binary files a/test/fixtures/expected/gamma-alpha.jpg and b/test/fixtures/expected/gamma-alpha.jpg differ
diff --git a/test/unit/embed.js b/test/unit/embed.js
index aa4e30cd..bc4032ea 100644
--- a/test/unit/embed.js
+++ b/test/unit/embed.js
@@ -80,6 +80,21 @@ describe('Embed', function() {
});
});
+ it('16-bit PNG with alpha channel onto RGBA', function(done) {
+ sharp(fixtures.inputPngWithTransparency16bit)
+ .resize(32, 16)
+ .embed()
+ .background({r: 0, g: 0, b: 0, a: 0})
+ .toBuffer(function(err, data, info) {
+ if (err) throw err;
+ assert.strictEqual(true, data.length > 0);
+ assert.strictEqual('png', info.format);
+ assert.strictEqual(32, info.width);
+ assert.strictEqual(16, info.height);
+ fixtures.assertSimilar(fixtures.expected('embed-16bit-rgba.png'), data, done);
+ });
+ });
+
it('Enlarge and embed', function(done) {
sharp(fixtures.inputPngWithOneColor)
.embed()