Skip to content

Commit

Permalink
Add features from libvips 7.40+
Browse files Browse the repository at this point in the history
Load TIFF from Buffer/Stream

Interlaced PNG output no longer needs tilecache

Option to disable PNG adaptive row filtering
  • Loading branch information
lovell committed Oct 29, 2014
1 parent 333e878 commit fa8e3c9
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 17 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ This module is powered by the blazingly fast [libvips](https://github.com/jcupit
### Prerequisites

* Node.js v0.10+
* [libvips](https://github.com/jcupitt/libvips) v7.38.5+
* [libvips](https://github.com/jcupitt/libvips) v7.38.5+ (7.40.9+ recommended)

To install the latest version of libvips on the following Operating Systems:

Expand Down Expand Up @@ -214,12 +214,12 @@ sharp(inputBuffer)

Constructor to which further methods are chained. `input`, if present, can be one of:

* Buffer containing JPEG, PNG or WebP image data, or
* Buffer containing JPEG, PNG, WebP or TIFF (libvips 7.40.0+) image data, or
* String containing the filename of an image, with most major formats supported.

The object returned implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.

JPEG, PNG or WebP format image data can be streamed into the object when `input` is not provided.
JPEG, PNG, WebP or TIFF (libvips 7.40.0+) format image data can be streamed into the object when `input` is not provided.

JPEG, PNG or WebP format image data can be streamed out from this object.

Expand Down Expand Up @@ -388,6 +388,12 @@ An advanced setting for the _zlib_ compression level of the lossless PNG output

`compressionLevel` is a Number between 0 and 9.

#### withoutAdaptiveFiltering()

_Requires libvips 7.41.0+_

An advanced and experimental PNG output setting to disable adaptive row filtering.

### Output methods

#### toFile(filename, [callback])
Expand Down
43 changes: 38 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
var util = require('util');
var stream = require('stream');

var semver = require('semver');
var color = require('color');
var BluebirdPromise = require('bluebird');

var sharp = require('./build/Release/sharp');
var libvipsVersion = sharp.libvipsVersion();

var Sharp = function(input) {
if (!(this instanceof Sharp)) {
Expand Down Expand Up @@ -46,6 +48,7 @@ var Sharp = function(input) {
progressive: false,
quality: 80,
compressionLevel: 6,
withoutAdaptiveFiltering: false,
streamOut: false,
withMetadata: false
};
Expand All @@ -55,14 +58,22 @@ var Sharp = function(input) {
} else if (typeof input === 'object' && input instanceof Buffer) {
// input=buffer
if (
(input.length > 1) &&
(input[0] === 0xff && input[1] === 0xd8) || // JPEG
(input[0] === 0x89 && input[1] === 0x50) || // PNG
(input[0] === 0x52 && input[1] === 0x49) // WebP
(input.length > 3) &&
// JPEG
(input[0] === 0xFF && input[1] === 0xD8) ||
// PNG
(input[0] === 0x89 && input[1] === 0x50) ||
// WebP
(input[0] === 0x52 && input[1] === 0x49) ||
// TIFF - requires libvips 7.40.0+
(semver.gte(libvipsVersion, '7.40.0') && (
(input[0] === 0x4D && input[1] === 0x4D && input[2] === 0x00 && (input[3] === 0x2A || input[3] === 0x2B)) ||
(input[0] === 0x49 && input[1] === 0x49 && (input[2] === 0x2A || input[2] === 0x2B) && input[3] === 0x00)
))
) {
this.options.bufferIn = input;
} else {
throw new Error('Buffer contains an unsupported image format. JPEG, PNG and WebP are currently supported.');
throw new Error('Buffer contains an unsupported image format. JPEG, PNG, WebP and TIFF are currently supported.');
}
} else {
// input=stream
Expand Down Expand Up @@ -266,6 +277,9 @@ Sharp.prototype.quality = function(quality) {
return this;
};

/*
zlib compression level for PNG output
*/
Sharp.prototype.compressionLevel = function(compressionLevel) {
if (!Number.isNaN(compressionLevel) && compressionLevel >= 0 && compressionLevel <= 9) {
this.options.compressionLevel = compressionLevel;
Expand All @@ -275,6 +289,18 @@ Sharp.prototype.compressionLevel = function(compressionLevel) {
return this;
};

/*
Disable the use of adaptive row filtering for PNG output - requires libvips 7.41.0+
*/
Sharp.prototype.withoutAdaptiveFiltering = function(withoutAdaptiveFiltering) {
if (semver.gte(libvipsVersion, '7.41.0')) {
this.options.withoutAdaptiveFiltering = (typeof withoutAdaptiveFiltering === 'boolean') ? withoutAdaptiveFiltering : true;
} else {
console.error('withoutAdaptiveFiltering requires libvips 7.41.0+');
}
return this;
};

Sharp.prototype.withMetadata = function(withMetadata) {
this.options.withMetadata = (typeof withMetadata === 'boolean') ? withMetadata : true;
return this;
Expand Down Expand Up @@ -503,3 +529,10 @@ module.exports.concurrency = function(concurrency) {
module.exports.counters = function() {
return sharp.counters();
};

/*
Get the version of the libvips library
*/
module.exports.libvipsVersion = function() {
return libvipsVersion;
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"dependencies": {
"bluebird": "^2.3.9",
"color": "^0.7.1",
"nan": "^1.3.0"
"nan": "^1.3.0",
"semver": "^4.1.0"
},
"devDependencies": {
"mocha": "^2.0.1",
Expand Down
22 changes: 19 additions & 3 deletions src/common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,22 @@ bool is_tiff(std::string const &str) {
return ends_with(str, ".tif") || ends_with(str, ".tiff") || ends_with(str, ".TIF") || ends_with(str, ".TIFF");
}

// Buffer content checkers
static bool buffer_is_tiff(char *buffer, size_t len) {
return (
len >= 4 && (
(buffer[0] == 'M' && buffer[1] == 'M' && buffer[2] == '\0' && (buffer[3] == '*' || buffer[3] == '+')) ||
(buffer[0] == 'I' && buffer[1] == 'I' && (buffer[2] == '*' || buffer[2] == '+') && buffer[3] == '\0')
)
);
}

unsigned char const MARKER_JPEG[] = {0xff, 0xd8};
unsigned char const MARKER_PNG[] = {0x89, 0x50};
unsigned char const MARKER_WEBP[] = {0x52, 0x49};

/*
Initialise a VipsImage from a buffer. Supports JPEG, PNG and WebP.
Initialise a VipsImage from a buffer. Supports JPEG, PNG, WebP and TIFF.
Returns the ImageType detected, if any.
*/
ImageType
Expand All @@ -42,14 +52,20 @@ sharp_init_image_from_buffer(VipsImage **image, void *buffer, size_t const lengt
if (!vips_jpegload_buffer(buffer, length, image, "access", access, NULL)) {
imageType = JPEG;
}
} else if(memcmp(MARKER_PNG, buffer, 2) == 0) {
} else if (memcmp(MARKER_PNG, buffer, 2) == 0) {
if (!vips_pngload_buffer(buffer, length, image, "access", access, NULL)) {
imageType = PNG;
}
} else if(memcmp(MARKER_WEBP, buffer, 2) == 0) {
} else if (memcmp(MARKER_WEBP, buffer, 2) == 0) {
if (!vips_webpload_buffer(buffer, length, image, "access", access, NULL)) {
imageType = WEBP;
}
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40)
} else if (buffer_is_tiff(static_cast<char*>(buffer), length)) {
if (!vips_tiffload_buffer(buffer, length, image, "access", access, NULL)) {
imageType = TIFF;
}
#endif
}
return imageType;
}
Expand Down
26 changes: 25 additions & 1 deletion src/resize.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ struct ResizeBaton {
VipsAccess accessMethod;
int quality;
int compressionLevel;
bool withoutAdaptiveFiltering;
std::string err;
bool withMetadata;

Expand Down Expand Up @@ -504,7 +505,8 @@ class ResizeWorker : public NanAsyncWorker {
image = colourspaced;
}

// Generate image tile cache when interlace output is required
#if !(VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 40 && VIPS_MINOR_VERSION >= 5)
// Generate image tile cache when interlace output is required - no longer required as of libvips 7.40.5+
if (baton->progressive) {
VipsImage *cached = vips_image_new();
vips_object_local(hook, cached);
Expand All @@ -514,6 +516,7 @@ class ResizeWorker : public NanAsyncWorker {
g_object_unref(image);
image = cached;
}
#endif

// Output
if (baton->output == "__jpeg" || (baton->output == "__input" && inputImageType == JPEG)) {
Expand All @@ -524,11 +527,21 @@ class ResizeWorker : public NanAsyncWorker {
}
baton->outputFormat = "jpeg";
} else if (baton->output == "__png" || (baton->output == "__input" && inputImageType == PNG)) {
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 41)
// Select PNG row filter
int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL;
// Write PNG to buffer
if (vips_pngsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata,
"compression", baton->compressionLevel, "interlace", baton->progressive, "filter", filter, NULL)) {
return Error(baton, hook);
}
#else
// Write PNG to buffer
if (vips_pngsave_buffer(image, &baton->bufferOut, &baton->bufferOutLength, "strip", !baton->withMetadata,
"compression", baton->compressionLevel, "interlace", baton->progressive, NULL)) {
return Error(baton, hook);
}
#endif
baton->outputFormat = "png";
} else if (baton->output == "__webp" || (baton->output == "__input" && inputImageType == WEBP)) {
// Write WEBP to buffer
Expand All @@ -551,11 +564,21 @@ class ResizeWorker : public NanAsyncWorker {
}
baton->outputFormat = "jpeg";
} else if (output_png || (match_input && inputImageType == PNG)) {
#if (VIPS_MAJOR_VERSION >= 7 && VIPS_MINOR_VERSION >= 41)
// Select PNG row filter
int filter = baton->withoutAdaptiveFiltering ? VIPS_FOREIGN_PNG_FILTER_NONE : VIPS_FOREIGN_PNG_FILTER_ALL;
// Write PNG to file
if (vips_pngsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
"compression", baton->compressionLevel, "interlace", baton->progressive, "filter", filter, NULL)) {
return Error(baton, hook);
}
#else
// Write PNG to file
if (vips_pngsave(image, baton->output.c_str(), "strip", !baton->withMetadata,
"compression", baton->compressionLevel, "interlace", baton->progressive, NULL)) {
return Error(baton, hook);
}
#endif
baton->outputFormat = "png";
} else if (output_webp || (match_input && inputImageType == WEBP)) {
// Write WEBP to file
Expand Down Expand Up @@ -780,6 +803,7 @@ NAN_METHOD(resize) {
baton->progressive = options->Get(NanNew<String>("progressive"))->BooleanValue();
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->withMetadata = options->Get(NanNew<String>("withMetadata"))->BooleanValue();
// Output filename or __format for Buffer
baton->output = *String::Utf8Value(options->Get(NanNew<String>("output"))->ToString());
Expand Down
1 change: 1 addition & 0 deletions src/sharp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ extern "C" void init(Handle<Object> target) {
NODE_SET_METHOD(target, "cache", cache);
NODE_SET_METHOD(target, "concurrency", concurrency);
NODE_SET_METHOD(target, "counters", counters);
NODE_SET_METHOD(target, "libvipsVersion", libvipsVersion);
}

NODE_MODULE(sharp, init)
10 changes: 10 additions & 0 deletions src/utilities.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,13 @@ NAN_METHOD(counters) {
counters->Set(NanNew<String>("process"), NanNew<Number>(counter_process));
NanReturnValue(counters);
}

/*
Get libvips version
*/
NAN_METHOD(libvipsVersion) {
NanScope();
char version[9];
snprintf(version, 9, "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
NanReturnValue(NanNew<String>(version));
}
1 change: 1 addition & 0 deletions src/utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
NAN_METHOD(cache);
NAN_METHOD(concurrency);
NAN_METHOD(counters);
NAN_METHOD(libvipsVersion);

#endif
2 changes: 1 addition & 1 deletion test/bench/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"devDependencies": {
"imagemagick": "^0.1.3",
"imagemagick-native": "^1.4.0",
"gm": "^1.16.0",
"gm": "^1.17.0",
"async": "^0.9.0",
"benchmark": "^1.0.0"
},
Expand Down
12 changes: 12 additions & 0 deletions test/bench/perf.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,18 @@ async.series({
}
});
}
}).add('sharp-withoutAdaptiveFiltering', {
defer: true,
fn: function(deferred) {
sharp(inputPngBuffer).resize(width, height).withoutAdaptiveFiltering().toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).on('cycle', function(event) {
console.log(' png ' + String(event.target));
}).on('complete', function() {
Expand Down
Loading

0 comments on commit fa8e3c9

Please sign in to comment.