mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Add support for greyscale conversion #43
This commit is contained in:
parent
d41321254a
commit
1c79d6fb5d
12
README.md
12
README.md
@ -283,7 +283,7 @@ This is equivalent to GraphicsMagick's `>` geometry option: "change the dimensio
|
|||||||
|
|
||||||
#### sharpen()
|
#### sharpen()
|
||||||
|
|
||||||
Perform a mild sharpen of the resultant image. This typically reduces performance by 30%.
|
Perform a mild sharpen of the output image. This typically reduces performance by 10%.
|
||||||
|
|
||||||
#### interpolateWith(interpolator)
|
#### interpolateWith(interpolator)
|
||||||
|
|
||||||
@ -308,6 +308,14 @@ This can improve the perceived brightness of a resized image in non-linear colou
|
|||||||
|
|
||||||
JPEG input images will not take advantage of the shrink-on-load performance optimisation when applying a gamma correction.
|
JPEG input images will not take advantage of the shrink-on-load performance optimisation when applying a gamma correction.
|
||||||
|
|
||||||
|
#### grayscale() / greyscale()
|
||||||
|
|
||||||
|
Convert to 8-bit greyscale; 256 shades of grey.
|
||||||
|
|
||||||
|
This is a linear operation. If the input image is in a non-linear colourspace such as sRGB, use `gamma()` with `greyscale()` for the best results.
|
||||||
|
|
||||||
|
The output image will still be web-friendly sRGB and contain three (identical) channels.
|
||||||
|
|
||||||
### Output options
|
### Output options
|
||||||
|
|
||||||
#### jpeg()
|
#### jpeg()
|
||||||
@ -332,7 +340,7 @@ The output quality to use for lossy JPEG, WebP and TIFF output formats. The defa
|
|||||||
|
|
||||||
Use progressive (interlace) scan for JPEG and PNG output. This typically reduces compression performance by 30% but results in an image that can be rendered sooner when decompressed.
|
Use progressive (interlace) scan for JPEG and PNG output. This typically reduces compression performance by 30% but results in an image that can be rendered sooner when decompressed.
|
||||||
|
|
||||||
#### withMetadata([boolean])
|
#### withMetadata()
|
||||||
|
|
||||||
Include all metadata (ICC, EXIF, XMP) from the input image in the output image. The default behaviour is to strip all metadata.
|
Include all metadata (ICC, EXIF, XMP) from the input image in the output image. The default behaviour is to strip all metadata.
|
||||||
|
|
||||||
|
10
index.js
10
index.js
@ -21,6 +21,7 @@ var Sharp = function(input) {
|
|||||||
sharpen: false,
|
sharpen: false,
|
||||||
interpolator: 'bilinear',
|
interpolator: 'bilinear',
|
||||||
gamma: 0,
|
gamma: 0,
|
||||||
|
greyscale: false,
|
||||||
progressive: false,
|
progressive: false,
|
||||||
sequentialRead: false,
|
sequentialRead: false,
|
||||||
quality: 80,
|
quality: 80,
|
||||||
@ -181,6 +182,15 @@ Sharp.prototype.gamma = function(gamma) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Convert to greyscale
|
||||||
|
*/
|
||||||
|
Sharp.prototype.greyscale = function(greyscale) {
|
||||||
|
this.options.greyscale = (typeof greyscale === 'boolean') ? greyscale : true;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
Sharp.prototype.grayscale = Sharp.prototype.greyscale;
|
||||||
|
|
||||||
Sharp.prototype.progressive = function(progressive) {
|
Sharp.prototype.progressive = function(progressive) {
|
||||||
this.options.progressive = (typeof progressive === 'boolean') ? progressive : true;
|
this.options.progressive = (typeof progressive === 'boolean') ? progressive : true;
|
||||||
return this;
|
return this;
|
||||||
|
12
src/sharp.cc
12
src/sharp.cc
@ -28,6 +28,7 @@ struct resize_baton {
|
|||||||
bool sharpen;
|
bool sharpen;
|
||||||
std::string interpolator;
|
std::string interpolator;
|
||||||
double gamma;
|
double gamma;
|
||||||
|
bool greyscale;
|
||||||
bool progressive;
|
bool progressive;
|
||||||
bool without_enlargement;
|
bool without_enlargement;
|
||||||
VipsAccess access_method;
|
VipsAccess access_method;
|
||||||
@ -483,6 +484,16 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
shrunk_on_load = gamma_encoded;
|
shrunk_on_load = gamma_encoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert to greyscale (linear, therefore after gamma encoding, if any)
|
||||||
|
if (baton->greyscale) {
|
||||||
|
VipsImage *greyscale = vips_image_new();
|
||||||
|
if (vips_colourspace(shrunk_on_load, &greyscale, VIPS_INTERPRETATION_B_W, NULL)) {
|
||||||
|
return resize_error(baton, shrunk_on_load);
|
||||||
|
}
|
||||||
|
g_object_unref(shrunk_on_load);
|
||||||
|
shrunk_on_load = greyscale;
|
||||||
|
}
|
||||||
|
|
||||||
VipsImage *shrunk = vips_image_new();
|
VipsImage *shrunk = vips_image_new();
|
||||||
if (shrink > 1) {
|
if (shrink > 1) {
|
||||||
// Use vips_shrink with the integral reduction
|
// Use vips_shrink with the integral reduction
|
||||||
@ -750,6 +761,7 @@ NAN_METHOD(resize) {
|
|||||||
baton->sharpen = options->Get(NanNew<String>("sharpen"))->BooleanValue();
|
baton->sharpen = options->Get(NanNew<String>("sharpen"))->BooleanValue();
|
||||||
baton->interpolator = *String::Utf8Value(options->Get(NanNew<String>("interpolator"))->ToString());
|
baton->interpolator = *String::Utf8Value(options->Get(NanNew<String>("interpolator"))->ToString());
|
||||||
baton->gamma = options->Get(NanNew<String>("gamma"))->NumberValue();
|
baton->gamma = options->Get(NanNew<String>("gamma"))->NumberValue();
|
||||||
|
baton->greyscale = options->Get(NanNew<String>("greyscale"))->BooleanValue();
|
||||||
baton->progressive = options->Get(NanNew<String>("progressive"))->BooleanValue();
|
baton->progressive = options->Get(NanNew<String>("progressive"))->BooleanValue();
|
||||||
baton->without_enlargement = options->Get(NanNew<String>("withoutEnlargement"))->BooleanValue();
|
baton->without_enlargement = options->Get(NanNew<String>("withoutEnlargement"))->BooleanValue();
|
||||||
baton->access_method = options->Get(NanNew<String>("sequentialRead"))->BooleanValue() ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
|
baton->access_method = options->Get(NanNew<String>("sequentialRead"))->BooleanValue() ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
|
||||||
|
@ -257,6 +257,30 @@ async.series({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}).add("sharp-file-buffer-greyscale", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp(inputJpg).resize(width, height).greyscale().toBuffer(function(err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add("sharp-file-buffer-greyscale-gamma", {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp(inputJpg).resize(width, height).gamma().greyscale().toBuffer(function(err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}).add("sharp-file-buffer-progressive", {
|
}).add("sharp-file-buffer-progressive", {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
|
@ -24,6 +24,8 @@ var inputPng = path.join(fixturesPath, "50020484-00001.png"); // http://c.searsp
|
|||||||
var inputWebP = path.join(fixturesPath, "4.webp"); // http://www.gstatic.com/webp/gallery/4.webp
|
var inputWebP = path.join(fixturesPath, "4.webp"); // http://www.gstatic.com/webp/gallery/4.webp
|
||||||
var inputGif = path.join(fixturesPath, "Crash_test.gif"); // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif
|
var inputGif = path.join(fixturesPath, "Crash_test.gif"); // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif
|
||||||
|
|
||||||
|
var outputZoinks = path.join(fixturesPath, 'output.zoinks'); // an "unknown" file extension
|
||||||
|
|
||||||
// Ensure cache limits can be set
|
// Ensure cache limits can be set
|
||||||
sharp.cache(0); // Disable
|
sharp.cache(0); // Disable
|
||||||
sharp.cache(50, 500); // 50MB, 500 items
|
sharp.cache(50, 500); // 50MB, 500 items
|
||||||
@ -425,6 +427,7 @@ async.series([
|
|||||||
anErrorWasEmitted = !!err;
|
anErrorWasEmitted = !!err;
|
||||||
}).on('end', function() {
|
}).on('end', function() {
|
||||||
assert(anErrorWasEmitted);
|
assert(anErrorWasEmitted);
|
||||||
|
fs.unlinkSync(outputJpg);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
var readableButNotAnImage = fs.createReadStream(__filename);
|
var readableButNotAnImage = fs.createReadStream(__filename);
|
||||||
@ -439,6 +442,7 @@ async.series([
|
|||||||
anErrorWasEmitted = !!err;
|
anErrorWasEmitted = !!err;
|
||||||
}).on('end', function() {
|
}).on('end', function() {
|
||||||
assert(anErrorWasEmitted);
|
assert(anErrorWasEmitted);
|
||||||
|
fs.unlinkSync(outputJpg);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
var writable = fs.createWriteStream(outputJpg);
|
var writable = fs.createWriteStream(outputJpg);
|
||||||
@ -522,44 +526,49 @@ async.series([
|
|||||||
},
|
},
|
||||||
// Output filename without extension should mirror input format
|
// Output filename without extension should mirror input format
|
||||||
function(done) {
|
function(done) {
|
||||||
sharp(inputJpg).resize(320, 80).toFile(path.join(fixturesPath, 'output.zoinks'), function(err, info) {
|
sharp(inputJpg).resize(320, 80).toFile(outputZoinks, function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(80, info.height);
|
assert.strictEqual(80, info.height);
|
||||||
|
fs.unlinkSync(outputZoinks);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(done) {
|
function(done) {
|
||||||
sharp(inputPng).resize(320, 80).toFile(path.join(fixturesPath, 'output.zoinks'), function(err, info) {
|
sharp(inputPng).resize(320, 80).toFile(outputZoinks, function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual('png', info.format);
|
assert.strictEqual('png', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(80, info.height);
|
assert.strictEqual(80, info.height);
|
||||||
|
fs.unlinkSync(outputZoinks);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(done) {
|
function(done) {
|
||||||
sharp(inputWebP).resize(320, 80).toFile(path.join(fixturesPath, 'output.zoinks'), function(err, info) {
|
sharp(inputWebP).resize(320, 80).toFile(outputZoinks, function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual('webp', info.format);
|
assert.strictEqual('webp', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(80, info.height);
|
assert.strictEqual(80, info.height);
|
||||||
|
fs.unlinkSync(outputZoinks);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(done) {
|
function(done) {
|
||||||
sharp(inputTiff).resize(320, 80).toFile(path.join(fixturesPath, 'output.zoinks'), function(err, info) {
|
sharp(inputTiff).resize(320, 80).toFile(outputZoinks, function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual('tiff', info.format);
|
assert.strictEqual('tiff', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(80, info.height);
|
assert.strictEqual(80, info.height);
|
||||||
|
fs.unlinkSync(outputZoinks);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(done) {
|
function(done) {
|
||||||
sharp(inputGif).resize(320, 80).toFile(path.join(fixturesPath, 'output.zoinks'), function(err, info) {
|
sharp(inputGif).resize(320, 80).toFile(outputZoinks, function(err, info) {
|
||||||
assert(!!err);
|
assert(!!err);
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// Metadata - JPEG
|
// Metadata - JPEG
|
||||||
@ -681,7 +690,7 @@ async.series([
|
|||||||
},
|
},
|
||||||
// Gamma correction
|
// Gamma correction
|
||||||
function(done) {
|
function(done) {
|
||||||
sharp(inputJpgWithGammaHoliness).resize(129, 111).toFile(path.join(fixturesPath, 'output.gamma-0.0.jpg'), function(err) {
|
sharp(inputJpgWithGammaHoliness).resize(129, 111).toFile(path.join(fixturesPath, 'output.gamma-0.0.jpg'), function(err, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -698,6 +707,19 @@ async.series([
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// Greyscale conversion
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpg).resize(320, 240).greyscale().toFile(path.join(fixturesPath, 'output.greyscale-gamma-0.0.jpg'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpg).resize(320, 240).gamma().greyscale().toFile(path.join(fixturesPath, 'output.greyscale-gamma-2.2.jpg'), function(err) {
|
||||||
|
if (err) throw err;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
// Verify internal counters
|
// Verify internal counters
|
||||||
function(done) {
|
function(done) {
|
||||||
var counters = sharp.counters();
|
var counters = sharp.counters();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user