Compare commits

...

15 Commits

Author SHA1 Message Date
Lovell Fuller
065ce6454b Explicit cast of size_t to 32 bit integer
Ensures compilation with nan 1.5.0+
2015-01-15 15:16:01 +00:00
Lovell Fuller
850c2ecdd6 Version bumps 2014-12-15 14:02:30 +00:00
Lovell Fuller
926c5603aa Improve documentation on concurrency/parallelism 2014-12-15 14:00:23 +00:00
Lovell Fuller
d3225fa193 Add 'size' attribute to callback's info Object #138 2014-12-15 13:54:19 +00:00
Lovell Fuller
f026a835fd Move unref of input Buffer to C++ #138 2014-12-14 10:31:25 +00:00
Lovell Fuller
47241db789 Let V8 garbage collect the Buffer earlier #138 2014-12-13 08:48:24 +00:00
Lovell Fuller
34a9970bd9 Remove useless re-definition of image #139 2014-12-12 22:04:55 +00:00
Lovell Fuller
57203f841a Copy input Buffer to avoid V8 heap compaction #138 2014-12-12 22:02:42 +00:00
Lovell Fuller
bd20bd1881 Version bumps 2014-12-11 13:32:52 +00:00
Lovell Fuller
60f1fda7ee Change interpretation to sRGB before transformation #133 2014-12-11 13:32:36 +00:00
Lovell Fuller
ea1013f6ec Add support for latest Amazon Linux 2014-12-08 10:52:59 +00:00
Lovell Fuller
247b607afd Add SVG and PSD fixtures and tests 2014-12-05 21:35:18 +00:00
Lovell Fuller
a56102a209 Ensure ICC transform of withMetadata output #133 2014-12-04 11:28:09 +00:00
Lovell Fuller
940b6f505f Add test for Promise rejection path 2014-12-04 10:48:45 +00:00
Lovell Fuller
e1b5574c4a Handle broken, embedded ICC profile #131 2014-12-03 10:23:35 +00:00
10 changed files with 167 additions and 60 deletions

View File

@@ -45,6 +45,7 @@ To install the most suitable version of libvips on the following Operating Syste
* Red Hat Linux * Red Hat Linux
* RHEL/Centos/Scientific 6, 7 * RHEL/Centos/Scientific 6, 7
* Fedora 21, 22 * Fedora 21, 22
* Amazon Linux 2014.09
run the following as a user with `sudo` access: run the following as a user with `sudo` access:
@@ -403,7 +404,7 @@ Use progressive (interlace) scan for JPEG and PNG output. This typically reduces
#### withMetadata() #### withMetadata()
Include all metadata (ICC, EXIF, XMP) from the input image in the output image. Include all metadata (EXIF, XMP, IPTC) from the input image in the output image. This will also convert to and add the latest web-friendly v2 sRGB ICC profile.
The default behaviour is to strip all metadata and convert to the device-independent sRGB colour space. The default behaviour is to strip all metadata and convert to the device-independent sRGB colour space.
@@ -428,7 +429,7 @@ An advanced setting to disable adaptive row filtering for the lossless PNG outpu
`callback`, if present, is called with two arguments `(err, info)` where: `callback`, if present, is called with two arguments `(err, info)` where:
* `err` contains an error message, if any. * `err` contains an error message, if any.
* `info` contains the output image `format`, `width` and `height`. * `info` contains the output image `format`, `size` (bytes), `width` and `height`.
A Promises/A+ promise is returned when `callback` is not provided. A Promises/A+ promise is returned when `callback` is not provided.
@@ -440,7 +441,7 @@ Write image data to a Buffer, the format of which will match the input image by
* `err` is an error message, if any. * `err` is an error message, if any.
* `buffer` is the output image data. * `buffer` is the output image data.
* `info` contains the output image `format`, `width` and `height`. * `info` contains the output image `format`, `size` (bytes), `width` and `height`.
A Promises/A+ promise is returned when `callback` is not provided. A Promises/A+ promise is returned when `callback` is not provided.
@@ -463,7 +464,7 @@ sharp.cache(50, 200); // { current: 49, high: 99, memory: 50, items: 200}
#### sharp.concurrency([threads]) #### sharp.concurrency([threads])
`threads`, if provided, is the Number of threads _libvips'_ should create for image processing. The default value is the number of CPU cores. A value of `0` will reset to this default. `threads`, if provided, is the Number of threads _libvips'_ should create for processing each image. The default value is the number of CPU cores. A value of `0` will reset to this default.
This method always returns the current concurrency. This method always returns the current concurrency.
@@ -473,6 +474,8 @@ sharp.concurrency(2); // 2
sharp.concurrency(0); // 4 sharp.concurrency(0); // 4
``` ```
The maximum number of images that can be processed in parallel is limited by libuv's `UV_THREADPOOL_SIZE` environment variable.
#### sharp.counters() #### sharp.counters()
Provides access to internal task counters. Provides access to internal task counters.

View File

@@ -18,6 +18,7 @@ var Sharp = function(input) {
stream.Duplex.call(this); stream.Duplex.call(this);
this.options = { this.options = {
// input options // input options
bufferIn: null,
streamIn: false, streamIn: false,
sequentialRead: false, sequentialRead: false,
// ICC profiles // ICC profiles
@@ -95,16 +96,16 @@ Sharp.prototype._write = function(chunk, encoding, callback) {
/*jslint unused: false */ /*jslint unused: false */
if (this.options.streamIn) { if (this.options.streamIn) {
if (typeof chunk === 'object' && chunk instanceof Buffer) { if (typeof chunk === 'object' && chunk instanceof Buffer) {
if (typeof this.options.bufferIn === 'undefined') { if (this.options.bufferIn instanceof Buffer) {
// Create new Buffer
this.options.bufferIn = new Buffer(chunk.length);
chunk.copy(this.options.bufferIn);
} else {
// Append to existing Buffer // Append to existing Buffer
this.options.bufferIn = Buffer.concat( this.options.bufferIn = Buffer.concat(
[this.options.bufferIn, chunk], [this.options.bufferIn, chunk],
this.options.bufferIn.length + chunk.length this.options.bufferIn.length + chunk.length
); );
} else {
// Create new Buffer
this.options.bufferIn = new Buffer(chunk.length);
chunk.copy(this.options.bufferIn);
} }
callback(); callback();
} else { } else {

View File

@@ -1,6 +1,6 @@
{ {
"name": "sharp", "name": "sharp",
"version": "0.8.1", "version": "0.8.4",
"author": "Lovell Fuller <npm@lovell.info>", "author": "Lovell Fuller <npm@lovell.info>",
"contributors": [ "contributors": [
"Pierre Inglebert <pierre.inglebert@gmail.com>", "Pierre Inglebert <pierre.inglebert@gmail.com>",
@@ -34,15 +34,15 @@
"vips" "vips"
], ],
"dependencies": { "dependencies": {
"bluebird": "^2.3.11", "bluebird": "^2.6.4",
"color": "^0.7.1", "color": "^0.7.3",
"nan": "^1.4.1", "nan": "^1.5.1",
"semver": "^4.1.0" "semver": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"mocha": "^2.0.1", "mocha": "^2.0.1",
"mocha-jshint": "^0.0.9", "mocha-jshint": "^0.0.9",
"istanbul": "^0.3.2", "istanbul": "^0.3.5",
"coveralls": "^2.11.2" "coveralls": "^2.11.2"
}, },
"license": "Apache 2.0", "license": "Apache 2.0",

View File

@@ -10,6 +10,7 @@
# * Red Hat Linux # * Red Hat Linux
# * RHEL/Centos/Scientific 6, 7 # * RHEL/Centos/Scientific 6, 7
# * Fedora 21, 22 # * Fedora 21, 22
# * Amazon Linux 2014.09
vips_version_minimum=7.40.0 vips_version_minimum=7.40.0
vips_version_latest_major=7.42 vips_version_latest_major=7.42
@@ -143,6 +144,19 @@ case $(uname -s) in
sorry "$RELEASE" sorry "$RELEASE"
;; ;;
esac esac
elif [ -f /etc/system-release ]; then
# Probably Amazon Linux
RELEASE=$(cat /etc/system-release)
case $RELEASE in
"Amazon Linux AMI release 2014.09")
# Amazon Linux
echo "Detected '$RELEASE'"
echo "Installing libvips dependencies via yum"
yum groupinstall -y "Development Tools"
yum install -y gtk-doc libxml2-devel libjpeg-turbo-devel libpng-devel libtiff-devel libexif-devel lcms-devel ImageMagick-devel gobject-introspection-devel libwebp-devel curl
install_libvips_from_source "--prefix=/usr"
;;
esac
else else
# Unsupported OS # Unsupported OS
sorry "$(uname -a)" sorry "$(uname -a)"

View File

@@ -115,6 +115,9 @@ class ResizeWorker : public NanAsyncWorker {
// Increment processing task counter // Increment processing task counter
g_atomic_int_inc(&counterProcess); g_atomic_int_inc(&counterProcess);
// Latest v2 sRGB ICC profile
std::string srgbProfile = baton->iccProfilePath + "sRGB_IEC61966-2-1_black_scaled.icc";
// Hang image references from this hook object // Hang image references from this hook object
VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new()); VipsObject *hook = reinterpret_cast<VipsObject*>(vips_image_new());
@@ -272,16 +275,14 @@ class ResizeWorker : public NanAsyncWorker {
// Ensure we're using a device-independent colour space // Ensure we're using a device-independent colour space
if (HasProfile(image)) { if (HasProfile(image)) {
// Convert to sRGB using embedded profile // Convert to sRGB using embedded profile
std::string srgbProfile = baton->iccProfilePath + "sRGB_IEC61966-2-1_black_scaled.icc";
VipsImage *transformed; VipsImage *transformed;
if (vips_icc_transform(image, &transformed, srgbProfile.c_str(), "embedded", TRUE, NULL)) { if (!vips_icc_transform(image, &transformed, srgbProfile.c_str(), "embedded", TRUE, NULL)) {
return Error(baton, hook); // Embedded profile can fail, so only update references on success
vips_object_local(hook, transformed);
image = transformed;
} }
vips_object_local(hook, transformed);
image = transformed;
} else if (image->Type == VIPS_INTERPRETATION_CMYK) { } else if (image->Type == VIPS_INTERPRETATION_CMYK) {
// Convert to sRGB using default "USWebCoatedSWOP" CMYK profile // Convert to sRGB using default "USWebCoatedSWOP" CMYK profile
std::string srgbProfile = baton->iccProfilePath + "sRGB_IEC61966-2-1_black_scaled.icc";
std::string cmykProfile = baton->iccProfilePath + "USWebCoatedSWOP.icc"; std::string cmykProfile = baton->iccProfilePath + "USWebCoatedSWOP.icc";
VipsImage *transformed; VipsImage *transformed;
if (vips_icc_transform(image, &transformed, srgbProfile.c_str(), "input_profile", cmykProfile.c_str(), NULL)) { if (vips_icc_transform(image, &transformed, srgbProfile.c_str(), "input_profile", cmykProfile.c_str(), NULL)) {
@@ -528,7 +529,6 @@ class ResizeWorker : public NanAsyncWorker {
} }
vips_object_local(hook, gaussian); vips_object_local(hook, gaussian);
// Apply Gaussian function // Apply Gaussian function
VipsImage *blurred;
if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) { if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) {
return Error(baton, hook); return Error(baton, hook);
} }
@@ -571,22 +571,24 @@ class ResizeWorker : public NanAsyncWorker {
image = gammaDecoded; image = gammaDecoded;
} }
// Convert colour space to either sRGB or RGB-with-profile, if not already // Convert image to sRGB, if not already
if (image->Type != VIPS_INTERPRETATION_sRGB) { if (image->Type != VIPS_INTERPRETATION_sRGB) {
// Switch intrepretation to sRGB
VipsImage *rgb; VipsImage *rgb;
if (baton->withMetadata && HasProfile(image)) { if (vips_colourspace(image, &rgb, VIPS_INTERPRETATION_sRGB, NULL)) {
// Convert to device-dependent RGB using embedded profile of input return Error(baton, hook);
if (vips_icc_export(image, &rgb, NULL)) {
return Error(baton, hook);
}
} else {
// Convert to device-independent sRGB
if (vips_colourspace(image, &rgb, VIPS_INTERPRETATION_sRGB, NULL)) {
return Error(baton, hook);
}
} }
vips_object_local(hook, rgb); vips_object_local(hook, rgb);
image = rgb; image = rgb;
// Tranform colours from embedded profile to sRGB profile
if (baton->withMetadata && HasProfile(image)) {
VipsImage *profiled;
if (vips_icc_transform(image, &profiled, srgbProfile.c_str(), "embedded", TRUE, NULL)) {
return Error(baton, hook);
}
vips_object_local(hook, profiled);
image = profiled;
}
} }
#if !(VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40 && VIPS_MINOR_VERSION >= 5) #if !(VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40 && VIPS_MINOR_VERSION >= 5)
@@ -692,6 +694,11 @@ class ResizeWorker : public NanAsyncWorker {
void HandleOKCallback () { void HandleOKCallback () {
NanScope(); NanScope();
// Free input Buffer
if (baton->bufferInLength > 0) {
g_free(baton->bufferIn);
}
Handle<Value> argv[3] = { NanNull(), NanNull(), NanNull() }; Handle<Value> argv[3] = { NanNull(), NanNull(), NanNull() };
if (!baton->err.empty()) { if (!baton->err.empty()) {
// Error // Error
@@ -710,16 +717,21 @@ class ResizeWorker : public NanAsyncWorker {
// Info Object // Info Object
Local<Object> info = NanNew<Object>(); Local<Object> info = NanNew<Object>();
info->Set(NanNew<String>("format"), NanNew<String>(baton->outputFormat)); info->Set(NanNew<String>("format"), NanNew<String>(baton->outputFormat));
info->Set(NanNew<String>("width"), NanNew<Number>(width)); info->Set(NanNew<String>("width"), NanNew<Integer>(width));
info->Set(NanNew<String>("height"), NanNew<Number>(height)); info->Set(NanNew<String>("height"), NanNew<Integer>(height));
if (baton->bufferOutLength > 0) { if (baton->bufferOutLength > 0) {
// Buffer // Copy data to new Buffer
argv[1] = NanNewBufferHandle(static_cast<char*>(baton->bufferOut), baton->bufferOutLength); argv[1] = NanNewBufferHandle(static_cast<char*>(baton->bufferOut), baton->bufferOutLength);
g_free(baton->bufferOut); g_free(baton->bufferOut);
// Add buffer size to info
info->Set(NanNew<String>("size"), NanNew<Uint32>(static_cast<uint32_t>(baton->bufferOutLength)));
argv[2] = info; argv[2] = info;
} else { } else {
// File // Add file size to info
struct stat st;
g_stat(baton->output.c_str(), &st);
info->Set(NanNew<String>("size"), NanNew<Uint32>(static_cast<uint32_t>(st.st_size)));
argv[1] = info; argv[1] = info;
} }
} }
@@ -830,8 +842,11 @@ NAN_METHOD(resize) {
// Input Buffer object // Input Buffer object
if (options->Get(NanNew<String>("bufferIn"))->IsObject()) { if (options->Get(NanNew<String>("bufferIn"))->IsObject()) {
Local<Object> buffer = options->Get(NanNew<String>("bufferIn"))->ToObject(); Local<Object> buffer = options->Get(NanNew<String>("bufferIn"))->ToObject();
// Take a copy of the input Buffer to avoid problems with V8 heap compaction
baton->bufferInLength = node::Buffer::Length(buffer); baton->bufferInLength = node::Buffer::Length(buffer);
baton->bufferIn = node::Buffer::Data(buffer); baton->bufferIn = g_malloc(baton->bufferInLength);
memcpy(baton->bufferIn, node::Buffer::Data(buffer), baton->bufferInLength);
options->Set(NanNew<String>("bufferIn"), NanNull());
} }
// ICC profile to use when input CMYK image has no embedded profile // ICC profile to use when input CMYK image has no embedded profile
baton->iccProfilePath = *String::Utf8Value(options->Get(NanNew<String>("iccProfilePath"))->ToString()); baton->iccProfilePath = *String::Utf8Value(options->Get(NanNew<String>("iccProfilePath"))->ToString());

17
test/fixtures/Wikimedia-logo.svg vendored Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
id="Wikimedia logo"
viewBox="-599 -599 1198 1198" width="1024" height="1024">
<defs>
<clipPath id="mask">
<path d="M 47.5,-87.5 v 425 h -95 v -425 l -552,-552 v 1250 h 1199 v -1250 z" />
</clipPath>
</defs>
<g clip-path="url(#mask)">
<circle id="green parts" fill="#396" r="336.5"/>
<circle id="blue arc" fill="none" stroke="#069" r="480.25" stroke-width="135.5" />
</g>
<circle fill="#900" cy="-379.5" r="184.5" id="red circle"/>
</svg>

After

Width:  |  Height:  |  Size: 692 B

BIN
test/fixtures/free-gearhead-pack.psd vendored Normal file

Binary file not shown.

View File

@@ -21,6 +21,8 @@ module.exports = {
inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp inputWebP: getPath('4.webp'), // http://www.gstatic.com/webp/gallery/4.webp
inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm
inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif
inputSvg: getPath('Wikimedia-logo.svg'), // http://commons.wikimedia.org/wiki/File:Wikimedia-logo.svg
inputPsd: getPath('free-gearhead-pack.psd'), // https://dribbble.com/shots/1624241-Free-Gearhead-Vector-Pack
outputJpg: getPath('output.jpg'), outputJpg: getPath('output.jpg'),
outputPng: getPath('output.png'), outputPng: getPath('output.png'),

View File

@@ -18,6 +18,7 @@ describe('Input/output', function() {
sharp(fixtures.outputJpg).toBuffer(function(err, data, info) { sharp(fixtures.outputJpg).toBuffer(function(err, data, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
@@ -35,6 +36,7 @@ describe('Input/output', function() {
sharp(fixtures.outputJpg).toBuffer(function(err, data, info) { sharp(fixtures.outputJpg).toBuffer(function(err, data, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
@@ -49,6 +51,7 @@ describe('Input/output', function() {
var readable = fs.createReadStream(fixtures.inputJpg); var readable = fs.createReadStream(fixtures.inputJpg);
var pipeline = sharp().resize(320, 240).toFile(fixtures.outputJpg, function(err, info) { var pipeline = sharp().resize(320, 240).toFile(fixtures.outputJpg, function(err, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, info.size > 0);
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
@@ -63,6 +66,7 @@ describe('Input/output', function() {
var pipeline = sharp().resize(320, 240).toBuffer(function(err, data, info) { var pipeline = sharp().resize(320, 240).toBuffer(function(err, data, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
@@ -90,6 +94,7 @@ describe('Input/output', function() {
sharp(fixtures.outputJpg).toBuffer(function(err, data, info) { sharp(fixtures.outputJpg).toBuffer(function(err, data, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
@@ -137,6 +142,7 @@ describe('Input/output', function() {
.toBuffer(function(err, data, info) { .toBuffer(function(err, data, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
@@ -151,6 +157,7 @@ describe('Input/output', function() {
.toBuffer(function(err, data, info) { .toBuffer(function(err, data, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
@@ -221,6 +228,7 @@ describe('Input/output', function() {
sharp(data).toBuffer(function(err, data, info) { sharp(data).toBuffer(function(err, data, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);
@@ -262,21 +270,23 @@ describe('Input/output', function() {
.resize(320, 240) .resize(320, 240)
.png() .png()
.progressive(false) .progressive(false)
.toBuffer(function(err, nonProgressive, info) { .toBuffer(function(err, nonProgressiveData, nonProgressiveInfo) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, nonProgressive.length > 0); assert.strictEqual(true, nonProgressiveData.length > 0);
assert.strictEqual('png', info.format); assert.strictEqual(nonProgressiveData.length, nonProgressiveInfo.size);
assert.strictEqual(320, info.width); assert.strictEqual('png', nonProgressiveInfo.format);
assert.strictEqual(240, info.height); assert.strictEqual(320, nonProgressiveInfo.width);
sharp(nonProgressive) assert.strictEqual(240, nonProgressiveInfo.height);
sharp(nonProgressiveData)
.progressive() .progressive()
.toBuffer(function(err, progressive, info) { .toBuffer(function(err, progressiveData, progressiveInfo) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, progressive.length > 0); assert.strictEqual(true, progressiveData.length > 0);
assert.strictEqual(true, progressive.length > nonProgressive.length); assert.strictEqual(progressiveData.length, progressiveInfo.size);
assert.strictEqual('png', info.format); assert.strictEqual(true, progressiveData.length > nonProgressiveData.length);
assert.strictEqual(320, info.width); assert.strictEqual('png', progressiveInfo.format);
assert.strictEqual(240, info.height); assert.strictEqual(320, progressiveInfo.width);
assert.strictEqual(240, progressiveInfo.height);
done(); done();
}); });
}); });
@@ -287,6 +297,7 @@ describe('Input/output', function() {
it('JPEG', function(done) { it('JPEG', function(done) {
sharp(fixtures.inputJpg).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) { sharp(fixtures.inputJpg).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, info.size > 0);
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);
@@ -298,6 +309,7 @@ describe('Input/output', function() {
it('PNG', function(done) { it('PNG', function(done) {
sharp(fixtures.inputPng).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) { sharp(fixtures.inputPng).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, info.size > 0);
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);
@@ -309,6 +321,7 @@ describe('Input/output', function() {
it('Transparent PNG', function(done) { it('Transparent PNG', function(done) {
sharp(fixtures.inputPngWithTransparency).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) { sharp(fixtures.inputPngWithTransparency).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, info.size > 0);
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);
@@ -319,6 +332,7 @@ describe('Input/output', function() {
it('WebP', function(done) { it('WebP', function(done) {
sharp(fixtures.inputWebP).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) { sharp(fixtures.inputWebP).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, info.size > 0);
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);
@@ -330,6 +344,7 @@ describe('Input/output', function() {
it('TIFF', function(done) { it('TIFF', function(done) {
sharp(fixtures.inputTiff).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) { sharp(fixtures.inputTiff).resize(320, 80).toFile(fixtures.outputZoinks, function(err, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, info.size > 0);
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);
@@ -375,23 +390,25 @@ describe('Input/output', function() {
sharp(fixtures.inputPng) sharp(fixtures.inputPng)
.resize(320, 240) .resize(320, 240)
.withoutAdaptiveFiltering(false) .withoutAdaptiveFiltering(false)
.toBuffer(function(err, dataAdaptive, info) { .toBuffer(function(err, adaptiveData, adaptiveInfo) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, dataAdaptive.length > 0); assert.strictEqual(true, adaptiveData.length > 0);
assert.strictEqual('png', info.format); assert.strictEqual(adaptiveData.length, adaptiveInfo.size);
assert.strictEqual(320, info.width); assert.strictEqual('png', adaptiveInfo.format);
assert.strictEqual(240, info.height); assert.strictEqual(320, adaptiveInfo.width);
assert.strictEqual(240, adaptiveInfo.height);
// Then generate without // Then generate without
sharp(fixtures.inputPng) sharp(fixtures.inputPng)
.resize(320, 240) .resize(320, 240)
.withoutAdaptiveFiltering() .withoutAdaptiveFiltering()
.toBuffer(function(err, dataWithoutAdaptive, info) { .toBuffer(function(err, withoutAdaptiveData, withoutAdaptiveInfo) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, dataWithoutAdaptive.length > 0); assert.strictEqual(true, withoutAdaptiveData.length > 0);
assert.strictEqual('png', info.format); assert.strictEqual(withoutAdaptiveData.length, withoutAdaptiveInfo.size);
assert.strictEqual(320, info.width); assert.strictEqual('png', withoutAdaptiveInfo.format);
assert.strictEqual(240, info.height); assert.strictEqual(320, withoutAdaptiveInfo.width);
assert.strictEqual(true, dataWithoutAdaptive.length < dataAdaptive.length); assert.strictEqual(240, withoutAdaptiveInfo.height);
assert.strictEqual(true, withoutAdaptiveData.length < adaptiveData.length);
done(); done();
}); });
}); });
@@ -400,6 +417,34 @@ describe('Input/output', function() {
}); });
it('Convert SVG to PNG', function(done) {
sharp(fixtures.inputSvg)
.resize(100, 100)
.png()
.toFile(fixtures.path('output.svg.png'), function(err, info) {
if (err) throw err;
assert.strictEqual(true, info.size > 0);
assert.strictEqual('png', info.format);
assert.strictEqual(100, info.width);
assert.strictEqual(100, info.height);
done();
});
});
it('Convert PSD to PNG', function(done) {
sharp(fixtures.inputPsd)
.resize(320, 240)
.png()
.toFile(fixtures.path('output.psd.png'), function(err, info) {
if (err) throw err;
assert.strictEqual(true, info.size > 0);
assert.strictEqual('png', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
});
if (semver.gte(sharp.libvipsVersion(), '7.40.0')) { if (semver.gte(sharp.libvipsVersion(), '7.40.0')) {
it('Load TIFF from Buffer [libvips ' + sharp.libvipsVersion() + '>=7.40.0]', function(done) { it('Load TIFF from Buffer [libvips ' + sharp.libvipsVersion() + '>=7.40.0]', function(done) {
var inputTiffBuffer = fs.readFileSync(fixtures.inputTiff); var inputTiffBuffer = fs.readFileSync(fixtures.inputTiff);
@@ -409,6 +454,7 @@ describe('Input/output', function() {
.toBuffer(function(err, data, info) { .toBuffer(function(err, data, info) {
if (err) throw err; if (err) throw err;
assert.strictEqual(true, data.length > 0); assert.strictEqual(true, data.length > 0);
assert.strictEqual(data.length, info.size);
assert.strictEqual('jpeg', info.format); assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width); assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height); assert.strictEqual(240, info.height);

View File

@@ -122,6 +122,15 @@ describe('Image metadata', function() {
}); });
}); });
it('Non-existent file in, Promise out', function(done) {
sharp('fail').metadata().then(function(metadata) {
throw new Error('Non-existent file');
}, function (err) {
assert.ok(!!err);
done();
});
});
it('Stream in, Promise out', function(done) { it('Stream in, Promise out', function(done) {
var readable = fs.createReadStream(fixtures.inputJpg); var readable = fs.createReadStream(fixtures.inputJpg);
var pipeline = sharp(); var pipeline = sharp();