mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
Add chroma subsampling options for JPEG output
This commit is contained in:
parent
0e91ca90d6
commit
1f7e80e581
@ -438,6 +438,15 @@ Include all metadata (EXIF, XMP, IPTC) from the input image in the output image.
|
||||
|
||||
The default behaviour is to strip all metadata and convert to the device-independent sRGB colour space.
|
||||
|
||||
#### withoutChromaSubsampling()
|
||||
|
||||
Disable the use of [chroma subsampling](http://en.wikipedia.org/wiki/Chroma_subsampling) with JPEG output (4:4:4).
|
||||
|
||||
This can improve colour representation at higher quality settings (90+),
|
||||
but usually increases output file size and typically reduces performance by 25%.
|
||||
|
||||
The default behaviour is to use chroma subsampling (4:2:0).
|
||||
|
||||
#### compressionLevel(compressionLevel)
|
||||
|
||||
An advanced setting for the _zlib_ compression level of the lossless PNG output format. The default level is `6`.
|
||||
|
9
index.js
9
index.js
@ -64,6 +64,7 @@ var Sharp = function(input) {
|
||||
quality: 80,
|
||||
compressionLevel: 6,
|
||||
withoutAdaptiveFiltering: false,
|
||||
withoutChromaSubsampling: false,
|
||||
streamOut: false,
|
||||
withMetadata: false
|
||||
};
|
||||
@ -368,6 +369,14 @@ Sharp.prototype.withoutAdaptiveFiltering = function(withoutAdaptiveFiltering) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/*
|
||||
Disable the use of chroma subsampling for JPEG output
|
||||
*/
|
||||
Sharp.prototype.withoutChromaSubsampling = function(withoutChromaSubsampling) {
|
||||
this.options.withoutChromaSubsampling = (typeof withoutChromaSubsampling === 'boolean') ? withoutChromaSubsampling : true;
|
||||
return this;
|
||||
};
|
||||
|
||||
Sharp.prototype.withMetadata = function(withMetadata) {
|
||||
this.options.withMetadata = (typeof withMetadata === 'boolean') ? withMetadata : true;
|
||||
return this;
|
||||
|
@ -34,10 +34,10 @@
|
||||
"vips"
|
||||
],
|
||||
"dependencies": {
|
||||
"bluebird": "^2.9.8",
|
||||
"bluebird": "^2.9.9",
|
||||
"color": "^0.7.3",
|
||||
"nan": "^1.6.2",
|
||||
"semver": "^4.2.2"
|
||||
"semver": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "^2.1.0",
|
||||
|
@ -90,6 +90,7 @@ struct ResizeBaton {
|
||||
int quality;
|
||||
int compressionLevel;
|
||||
bool withoutAdaptiveFiltering;
|
||||
bool withoutChromaSubsampling;
|
||||
std::string err;
|
||||
bool withMetadata;
|
||||
|
||||
@ -117,6 +118,7 @@ struct ResizeBaton {
|
||||
quality(80),
|
||||
compressionLevel(6),
|
||||
withoutAdaptiveFiltering(false),
|
||||
withoutChromaSubsampling(false),
|
||||
withMetadata(false) {
|
||||
background[0] = 0.0;
|
||||
background[1] = 0.0;
|
||||
@ -675,7 +677,8 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
if (baton->output == "__jpeg" || (baton->output == "__input" && inputImageType == ImageType::JPEG)) {
|
||||
// Write JPEG to buffer
|
||||
if (vips_jpegsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata,
|
||||
"Q", baton->quality, "optimize_coding", TRUE, "interlace", baton->progressive, NULL)) {
|
||||
"Q", baton->quality, "optimize_coding", TRUE, "no_subsample", baton->withoutChromaSubsampling,
|
||||
"interlace", baton->progressive, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
baton->outputFormat = "jpeg";
|
||||
@ -741,7 +744,8 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
if (outputJpeg || (matchInput && inputImageType == ImageType::JPEG)) {
|
||||
// Write JPEG to file
|
||||
if (vips_jpegsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
|
||||
"Q", baton->quality, "optimize_coding", TRUE, "interlace", baton->progressive, NULL)) {
|
||||
"Q", baton->quality, "optimize_coding", TRUE, "no_subsample", baton->withoutChromaSubsampling,
|
||||
"interlace", baton->progressive, NULL)) {
|
||||
return Error(baton, hook);
|
||||
}
|
||||
baton->outputFormat = "jpeg";
|
||||
@ -992,6 +996,7 @@ NAN_METHOD(resize) {
|
||||
baton->quality = options->Get(NanNew<String>("quality"))->Int32Value();
|
||||
baton->compressionLevel = options->Get(NanNew<String>("compressionLevel"))->Int32Value();
|
||||
baton->withoutAdaptiveFiltering = options->Get(NanNew<String>("withoutAdaptiveFiltering"))->BooleanValue();
|
||||
baton->withoutChromaSubsampling = options->Get(NanNew<String>("withoutChromaSubsampling"))->BooleanValue();
|
||||
baton->withMetadata = options->Get(NanNew<String>("withMetadata"))->BooleanValue();
|
||||
// Output filename or __format for Buffer
|
||||
baton->output = *String::Utf8Value(options->Get(NanNew<String>("output"))->ToString());
|
||||
|
@ -12,7 +12,7 @@
|
||||
"imagemagick-native": "^1.7.0",
|
||||
"gm": "^1.17.0",
|
||||
"async": "^0.9.0",
|
||||
"semver": "^4.2.0",
|
||||
"semver": "^4.3.0",
|
||||
"benchmark": "^1.0.0"
|
||||
},
|
||||
"license": "Apache 2.0",
|
||||
|
@ -347,6 +347,18 @@ async.series({
|
||||
}
|
||||
});
|
||||
}
|
||||
}).add('sharp-without-chroma-subsampling', {
|
||||
defer: true,
|
||||
fn: function(deferred) {
|
||||
sharp(inputJpgBuffer).resize(width, height).withoutChromaSubsampling().toBuffer(function(err, buffer) {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
assert.notStrictEqual(null, buffer);
|
||||
deferred.resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
}).add('sharp-rotate', {
|
||||
defer: true,
|
||||
fn: function(deferred) {
|
||||
|
@ -459,6 +459,35 @@ describe('Input/output', function() {
|
||||
|
||||
});
|
||||
|
||||
it('Without chroma subsampling generates larger file', function(done) {
|
||||
// First generate with chroma subsampling (default)
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.withoutChromaSubsampling(false)
|
||||
.toBuffer(function(err, withChromaSubsamplingData, withChromaSubsamplingInfo) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, withChromaSubsamplingData.length > 0);
|
||||
assert.strictEqual(withChromaSubsamplingData.length, withChromaSubsamplingInfo.size);
|
||||
assert.strictEqual('jpeg', withChromaSubsamplingInfo.format);
|
||||
assert.strictEqual(320, withChromaSubsamplingInfo.width);
|
||||
assert.strictEqual(240, withChromaSubsamplingInfo.height);
|
||||
// Then generate without
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.withoutChromaSubsampling()
|
||||
.toBuffer(function(err, withoutChromaSubsamplingData, withoutChromaSubsamplingInfo) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(true, withoutChromaSubsamplingData.length > 0);
|
||||
assert.strictEqual(withoutChromaSubsamplingData.length, withoutChromaSubsamplingInfo.size);
|
||||
assert.strictEqual('jpeg', withoutChromaSubsamplingInfo.format);
|
||||
assert.strictEqual(320, withoutChromaSubsamplingInfo.width);
|
||||
assert.strictEqual(240, withoutChromaSubsamplingInfo.height);
|
||||
assert.strictEqual(true, withChromaSubsamplingData.length < withoutChromaSubsamplingData.length);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Convert SVG, if supported, to PNG', function(done) {
|
||||
sharp(fixtures.inputSvg)
|
||||
.resize(100, 100)
|
||||
|
Loading…
x
Reference in New Issue
Block a user