mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
Ensure 16-bit+alpha input images work with vips_premultiply #301
Improves SVG support as *magick serves these as 16-bit Add automated tests for SVG and 16-bit+alpha PNG inputs
This commit is contained in:
parent
c9ecc7a517
commit
05dd191e17
@ -437,17 +437,22 @@ class PipelineWorker : public AsyncWorker {
|
|||||||
image = transformed;
|
image = transformed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate maximum alpha value based on input image pixel depth
|
||||||
|
double maxAlpha = (image->BandFmt == VIPS_FORMAT_USHORT) ? 65535.0 : 255.0;
|
||||||
|
|
||||||
// Flatten image to remove alpha channel
|
// Flatten image to remove alpha channel
|
||||||
if (baton->flatten && HasAlpha(image)) {
|
if (baton->flatten && HasAlpha(image)) {
|
||||||
|
// Scale up 8-bit values to match 16-bit input image
|
||||||
|
double multiplier = (image->Type == VIPS_INTERPRETATION_RGB16) ? 256.0 : 1.0;
|
||||||
// Background colour
|
// Background colour
|
||||||
VipsArrayDouble *background = vips_array_double_newv(
|
VipsArrayDouble *background = vips_array_double_newv(
|
||||||
3, // Ignore alpha channel as we're about to remove it
|
3, // Ignore alpha channel as we're about to remove it
|
||||||
baton->background[0],
|
baton->background[0] * multiplier,
|
||||||
baton->background[1],
|
baton->background[1] * multiplier,
|
||||||
baton->background[2]
|
baton->background[2] * multiplier
|
||||||
);
|
);
|
||||||
VipsImage *flattened;
|
VipsImage *flattened;
|
||||||
if (vips_flatten(image, &flattened, "background", background, nullptr)) {
|
if (vips_flatten(image, &flattened, "background", background, "max_alpha", maxAlpha, nullptr)) {
|
||||||
vips_area_unref(reinterpret_cast<VipsArea*>(background));
|
vips_area_unref(reinterpret_cast<VipsArea*>(background));
|
||||||
return Error();
|
return Error();
|
||||||
}
|
}
|
||||||
@ -526,7 +531,7 @@ class PipelineWorker : public AsyncWorker {
|
|||||||
// See: http://entropymine.com/imageworsener/resizealpha/
|
// See: http://entropymine.com/imageworsener/resizealpha/
|
||||||
if (shouldPremultiplyAlpha) {
|
if (shouldPremultiplyAlpha) {
|
||||||
VipsImage *imagePremultiplied;
|
VipsImage *imagePremultiplied;
|
||||||
if (vips_premultiply(image, &imagePremultiplied, nullptr)) {
|
if (vips_premultiply(image, &imagePremultiplied, "max_alpha", maxAlpha, nullptr)) {
|
||||||
(baton->err).append("Failed to premultiply alpha channel.");
|
(baton->err).append("Failed to premultiply alpha channel.");
|
||||||
return Error();
|
return Error();
|
||||||
}
|
}
|
||||||
@ -792,7 +797,7 @@ class PipelineWorker : public AsyncWorker {
|
|||||||
// Reverse premultiplication after all transformations:
|
// Reverse premultiplication after all transformations:
|
||||||
if (shouldPremultiplyAlpha) {
|
if (shouldPremultiplyAlpha) {
|
||||||
VipsImage *imageUnpremultiplied;
|
VipsImage *imageUnpremultiplied;
|
||||||
if (vips_unpremultiply(image, &imageUnpremultiplied, nullptr)) {
|
if (vips_unpremultiply(image, &imageUnpremultiplied, "max_alpha", maxAlpha, nullptr)) {
|
||||||
(baton->err).append("Failed to unpremultiply alpha channel");
|
(baton->err).append("Failed to unpremultiply alpha channel");
|
||||||
return Error();
|
return Error();
|
||||||
}
|
}
|
||||||
@ -820,7 +825,24 @@ class PipelineWorker : public AsyncWorker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert image to sRGB, if not already
|
// Convert image to sRGB, if not already
|
||||||
if (image->Type != VIPS_INTERPRETATION_sRGB) {
|
if (image->Type == VIPS_INTERPRETATION_RGB16) {
|
||||||
|
// Ensure 16-bit integer
|
||||||
|
VipsImage *ushort;
|
||||||
|
if (vips_cast_ushort(image, &ushort, nullptr)) {
|
||||||
|
return Error();
|
||||||
|
}
|
||||||
|
vips_object_local(hook, ushort);
|
||||||
|
image = ushort;
|
||||||
|
// Fast conversion to 8-bit integer by discarding least-significant byte
|
||||||
|
VipsImage *msb;
|
||||||
|
if (vips_msb(image, &msb, nullptr)) {
|
||||||
|
return Error();
|
||||||
|
}
|
||||||
|
vips_object_local(hook, msb);
|
||||||
|
image = msb;
|
||||||
|
// Explicitly set to sRGB
|
||||||
|
image->Type = VIPS_INTERPRETATION_sRGB;
|
||||||
|
} else if (image->Type != VIPS_INTERPRETATION_sRGB) {
|
||||||
// Switch interpretation to sRGB
|
// Switch interpretation to sRGB
|
||||||
VipsImage *rgb;
|
VipsImage *rgb;
|
||||||
if (vips_colourspace(image, &rgb, VIPS_INTERPRETATION_sRGB, nullptr)) {
|
if (vips_colourspace(image, &rgb, VIPS_INTERPRETATION_sRGB, nullptr)) {
|
||||||
|
BIN
test/fixtures/expected/flatten-rgb16-orange.jpg
vendored
Normal file
BIN
test/fixtures/expected/flatten-rgb16-orange.jpg
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 840 B |
BIN
test/fixtures/expected/svg.png
vendored
Normal file
BIN
test/fixtures/expected/svg.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
1
test/fixtures/index.js
vendored
Executable file → Normal file
1
test/fixtures/index.js
vendored
Executable file → Normal file
@ -71,6 +71,7 @@ module.exports = {
|
|||||||
inputPngWithTransparency: getPath('blackbug.png'), // public domain
|
inputPngWithTransparency: getPath('blackbug.png'), // public domain
|
||||||
inputPngWithGreyAlpha: getPath('grey-8bit-alpha.png'),
|
inputPngWithGreyAlpha: getPath('grey-8bit-alpha.png'),
|
||||||
inputPngWithOneColor: getPath('2x2_fdcce6.png'),
|
inputPngWithOneColor: getPath('2x2_fdcce6.png'),
|
||||||
|
inputPngWithTransparency16bit: getPath('tbgn2c16.png'), // http://www.schaik.com/pngsuite/tbgn2c16.png
|
||||||
inputPngOverlayLayer0: getPath('alpha-layer-0-background.png'),
|
inputPngOverlayLayer0: getPath('alpha-layer-0-background.png'),
|
||||||
inputPngOverlayLayer1: getPath('alpha-layer-1-fill.png'),
|
inputPngOverlayLayer1: getPath('alpha-layer-1-fill.png'),
|
||||||
inputPngOverlayLayer2: getPath('alpha-layer-2-ink.png'),
|
inputPngOverlayLayer2: getPath('alpha-layer-2-ink.png'),
|
||||||
|
BIN
test/fixtures/tbgn2c16.png
vendored
Normal file
BIN
test/fixtures/tbgn2c16.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
13
test/unit/alpha.js
Executable file → Normal file
13
test/unit/alpha.js
Executable file → Normal file
@ -46,6 +46,19 @@ describe('Alpha transparency', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Flatten 16-bit PNG with transparency to orange', function(done) {
|
||||||
|
sharp(fixtures.inputPngWithTransparency16bit)
|
||||||
|
.flatten()
|
||||||
|
.background({r: 255, g: 102, b: 0})
|
||||||
|
.toBuffer(function(err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, info.size > 0);
|
||||||
|
assert.strictEqual(32, info.width);
|
||||||
|
assert.strictEqual(32, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('flatten-rgb16-orange.jpg'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Do not flatten', function(done) {
|
it('Do not flatten', function(done) {
|
||||||
sharp(fixtures.inputPngWithTransparency)
|
sharp(fixtures.inputPngWithTransparency)
|
||||||
.flatten(false)
|
.flatten(false)
|
||||||
|
5
test/unit/io.js
Executable file → Normal file
5
test/unit/io.js
Executable file → Normal file
@ -638,16 +638,17 @@ describe('Input/output', function() {
|
|||||||
sharp(fixtures.inputSvg)
|
sharp(fixtures.inputSvg)
|
||||||
.resize(100, 100)
|
.resize(100, 100)
|
||||||
.toFormat('png')
|
.toFormat('png')
|
||||||
.toFile(fixtures.path('output.svg.png'), function(err, info) {
|
.toBuffer(function(err, data, info) {
|
||||||
if (err) {
|
if (err) {
|
||||||
assert.strictEqual(0, err.message.indexOf('Input file is of an unsupported image format'));
|
assert.strictEqual(0, err.message.indexOf('Input file is of an unsupported image format'));
|
||||||
|
done();
|
||||||
} else {
|
} else {
|
||||||
assert.strictEqual(true, info.size > 0);
|
assert.strictEqual(true, info.size > 0);
|
||||||
assert.strictEqual('png', info.format);
|
assert.strictEqual('png', info.format);
|
||||||
assert.strictEqual(100, info.width);
|
assert.strictEqual(100, info.width);
|
||||||
assert.strictEqual(100, info.height);
|
assert.strictEqual(100, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('svg.png'), data, done);
|
||||||
}
|
}
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user