Expose runtime format availability

Aids addition of new format/method combos

Dogfood this in the test code
This commit is contained in:
Lovell Fuller 2015-02-26 19:41:33 +00:00
parent 1565522ecc
commit c7ccf6801d
7 changed files with 174 additions and 43 deletions

View File

@ -218,8 +218,42 @@ sharp(inputBuffer)
}); });
``` ```
```javascript
// Runtime discovery of available formats
console.dir(sharp.format);
```
## API ## API
### Attributes
#### format
An Object containing nested boolean values
representing the available input and output formats/methods,
for example:
```json
{ jpeg: { id: 'jpeg',
input: { file: true, buffer: true, stream: true },
output: { file: true, buffer: true, stream: true } },
png: { id: 'png',
input: { file: true, buffer: true, stream: true },
output: { file: true, buffer: true, stream: true } },
webp: { id: 'webp',
input: { file: true, buffer: true, stream: true },
output: { file: true, buffer: true, stream: true } },
tiff: { id: 'tiff',
input: { file: true, buffer: true, stream: true },
output: { file: true, buffer: false, stream: false } },
magick: { id: 'magick',
input: { file: true, buffer: true, stream: true },
output: { file: false, buffer: false, stream: false } },
raw: { id: 'raw',
input: { file: false, buffer: false, stream: false },
output: { file: false, buffer: true, stream: true } } }
```
### Input methods ### Input methods
#### sharp([input]) #### sharp([input])
@ -235,7 +269,7 @@ JPEG, PNG, WebP, GIF* or TIFF format image data can be streamed into the object
JPEG, PNG or WebP format image data can be streamed out from this object. JPEG, PNG or WebP format image data can be streamed out from this object.
\* GIF support requires libvips 8.0.0+. \* libvips 8.0.0+ is required for Buffer/Stream input of GIF and other `magick` formats.
#### metadata([callback]) #### metadata([callback])

View File

@ -100,6 +100,11 @@ var Sharp = function(input) {
module.exports = Sharp; module.exports = Sharp;
util.inherits(Sharp, stream.Duplex); util.inherits(Sharp, stream.Duplex);
/*
Supported image formats
*/
module.exports.format = sharp.format();
/* /*
Handle incoming chunk on Writable Stream Handle incoming chunk on Writable Stream
*/ */
@ -481,7 +486,8 @@ Sharp.prototype.webp = function() {
Force raw, uint8 output Force raw, uint8 output
*/ */
Sharp.prototype.raw = function() { Sharp.prototype.raw = function() {
if (semver.gte(libvipsVersion, '7.42.0')) { var supportsRawOutput = module.exports.format.raw.output;
if (supportsRawOutput.file || supportsRawOutput.buffer || supportsRawOutput.stream) {
this.options.output = '__raw'; this.options.output = '__raw';
} else { } else {
console.error('Raw output requires libvips 7.42.0+'); console.error('Raw output requires libvips 7.42.0+');
@ -491,15 +497,15 @@ Sharp.prototype.raw = function() {
/* /*
Force output to a given format Force output to a given format
@param format is either the id as a String or an Object with an 'id' attribute
*/ */
module.exports.format = {'jpeg': 'jpeg', 'png': 'png', 'webp': 'webp', 'raw': 'raw'};
Sharp.prototype.toFormat = function(format) { Sharp.prototype.toFormat = function(format) {
if ( var id = format;
typeof format === 'string' && if (typeof format === 'object') {
typeof module.exports.format[format] === 'string' && id = format.id;
typeof this[format] === 'function' }
) { if (typeof id === 'string' && typeof module.exports.format[id] === 'object' && typeof this[id] === 'function') {
this[format](); this[id]();
} else { } else {
throw new Error('Unsupported format ' + format); throw new Error('Unsupported format ' + format);
} }

View File

@ -23,6 +23,7 @@ extern "C" void init(v8::Handle<v8::Object> target) {
NODE_SET_METHOD(target, "concurrency", concurrency); NODE_SET_METHOD(target, "concurrency", concurrency);
NODE_SET_METHOD(target, "counters", counters); NODE_SET_METHOD(target, "counters", counters);
NODE_SET_METHOD(target, "libvipsVersion", libvipsVersion); NODE_SET_METHOD(target, "libvipsVersion", libvipsVersion);
NODE_SET_METHOD(target, "format", format);
} }
NODE_MODULE(sharp, init) NODE_MODULE(sharp, init)

View File

@ -10,6 +10,7 @@ using v8::Local;
using v8::Object; using v8::Object;
using v8::Number; using v8::Number;
using v8::String; using v8::String;
using v8::Boolean;
using sharp::counterQueue; using sharp::counterQueue;
using sharp::counterProcess; using sharp::counterProcess;
@ -78,3 +79,66 @@ NAN_METHOD(libvipsVersion) {
snprintf(version, sizeof(version), "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2)); snprintf(version, sizeof(version), "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
NanReturnValue(NanNew<String>(version)); NanReturnValue(NanNew<String>(version));
} }
/*
Get available input/output file/buffer/stream formats
*/
NAN_METHOD(format) {
NanScope();
// Attribute names
Local<String> attrId = NanNew<String>("id");
Local<String> attrInput = NanNew<String>("input");
Local<String> attrOutput = NanNew<String>("output");
Local<String> attrFile = NanNew<String>("file");
Local<String> attrBuffer = NanNew<String>("buffer");
Local<String> attrStream = NanNew<String>("stream");
// Which load/save operations are available for each compressed format?
Local<Object> format = NanNew<Object>();
for (std::string f : {"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz"}) {
// Input
Local<Object> input = NanNew<Object>();
input->Set(attrFile, NanNew<Boolean>(
vips_type_find("VipsOperation", (f + "load").c_str())));
input->Set(attrBuffer, NanNew<Boolean>(
vips_type_find("VipsOperation", (f + "load_buffer").c_str())));
input->Set(attrStream, input->Get(attrBuffer));
// Output
Local<Object> output = NanNew<Object>();
output->Set(attrFile, NanNew<Boolean>(
vips_type_find("VipsOperation", (f + "save").c_str())));
output->Set(attrBuffer, NanNew<Boolean>(
vips_type_find("VipsOperation", (f + "save_buffer").c_str())));
output->Set(attrStream, output->Get(attrBuffer));
// Other attributes
Local<Object> container = NanNew<Object>();
Local<String> formatId = NanNew<String>(f);
container->Set(attrId, formatId);
container->Set(attrInput, input);
container->Set(attrOutput, output);
// Add to set of formats
format->Set(formatId, container);
}
// Raw, uncompressed data
Local<Object> raw = NanNew<Object>();
raw->Set(attrId, NanNew<String>("raw"));
format->Set(NanNew<String>("raw"), raw);
// No support for raw input yet, so always false
Local<Boolean> unsupported = NanNew<Boolean>(false);
Local<Object> rawInput = NanNew<Object>();
rawInput->Set(attrFile, unsupported);
rawInput->Set(attrBuffer, unsupported);
rawInput->Set(attrStream, unsupported);
raw->Set(attrInput, rawInput);
// Raw output via Buffer/Stream is available in libvips >= 7.42.0
Local<Boolean> supportsRawOutput = NanNew<Boolean>(vips_version(0) >= 8 || (vips_version(0) == 7 && vips_version(1) >= 42));
Local<Object> rawOutput = NanNew<Object>();
rawOutput->Set(attrFile, unsupported);
rawOutput->Set(attrBuffer, supportsRawOutput);
rawOutput->Set(attrStream, supportsRawOutput);
raw->Set(attrOutput, rawOutput);
NanReturnValue(format);
}

View File

@ -7,5 +7,6 @@ NAN_METHOD(cache);
NAN_METHOD(concurrency); NAN_METHOD(concurrency);
NAN_METHOD(counters); NAN_METHOD(counters);
NAN_METHOD(libvipsVersion); NAN_METHOD(libvipsVersion);
NAN_METHOD(format);
#endif // SRC_UTILITIES_H_ #endif // SRC_UTILITIES_H_

View File

@ -488,6 +488,7 @@ describe('Input/output', function() {
}); });
}); });
if (sharp.format.magick.input.file) {
it('Convert SVG, if supported, to PNG', function(done) { it('Convert SVG, if supported, to PNG', function(done) {
sharp(fixtures.inputSvg) sharp(fixtures.inputSvg)
.resize(100, 100) .resize(100, 100)
@ -504,7 +505,9 @@ describe('Input/output', function() {
done(); done();
}); });
}); });
}
if (sharp.format.magick.input.file) {
it('Convert PSD to PNG', function(done) { it('Convert PSD to PNG', function(done) {
sharp(fixtures.inputPsd) sharp(fixtures.inputPsd)
.resize(320, 240) .resize(320, 240)
@ -518,9 +521,10 @@ describe('Input/output', function() {
done(); done();
}); });
}); });
}
if (semver.gte(sharp.libvipsVersion(), '7.40.0')) { if (sharp.format.tiff.input.buffer) {
it('Load TIFF from Buffer [libvips ' + sharp.libvipsVersion() + '>=7.40.0]', function(done) { it('Load TIFF from Buffer', function(done) {
var inputTiffBuffer = fs.readFileSync(fixtures.inputTiff); var inputTiffBuffer = fs.readFileSync(fixtures.inputTiff);
sharp(inputTiffBuffer) sharp(inputTiffBuffer)
.resize(320, 240) .resize(320, 240)
@ -537,8 +541,8 @@ describe('Input/output', function() {
}); });
} }
if (semver.gte(sharp.libvipsVersion(), '8.0.0')) { if (sharp.format.magick.input.buffer) {
it('Load GIF from Buffer [libvips ' + sharp.libvipsVersion() + '>=8.0.0]', function(done) { it('Load GIF from Buffer', function(done) {
var inputGifBuffer = fs.readFileSync(fixtures.inputGif); var inputGifBuffer = fs.readFileSync(fixtures.inputGif);
sharp(inputGifBuffer) sharp(inputGifBuffer)
.resize(320, 240) .resize(320, 240)
@ -555,8 +559,8 @@ describe('Input/output', function() {
}); });
} }
if (semver.gte(sharp.libvipsVersion(), '7.42.0')) { if (sharp.format.raw.output.buffer) {
describe('Ouput raw, uncompressed image data [libvips ' + sharp.libvipsVersion() + '>=7.42.0]', function() { describe('Ouput raw, uncompressed image data', function() {
it('1 channel greyscale image', function(done) { it('1 channel greyscale image', function(done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.greyscale() .greyscale()

View File

@ -50,4 +50,25 @@ describe('Utilities', function() {
}); });
}); });
describe('Format', function() {
it('Contains expected attributes', function() {
assert.strictEqual('object', typeof sharp.format);
Object.keys(sharp.format).forEach(function(format) {
assert.strictEqual(true, 'id' in sharp.format[format]);
assert.strictEqual(format, sharp.format[format].id);
['input', 'output'].forEach(function(direction) {
assert.strictEqual(true, direction in sharp.format[format]);
assert.strictEqual('object', typeof sharp.format[format][direction]);
assert.strictEqual(3, Object.keys(sharp.format[format][direction]).length);
assert.strictEqual(true, 'file' in sharp.format[format][direction]);
assert.strictEqual(true, 'buffer' in sharp.format[format][direction]);
assert.strictEqual(true, 'stream' in sharp.format[format][direction]);
assert.strictEqual('boolean', typeof sharp.format[format][direction].file);
assert.strictEqual('boolean', typeof sharp.format[format][direction].buffer);
assert.strictEqual('boolean', typeof sharp.format[format][direction].stream);
});
});
});
});
}); });