mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Add method for fast access to image metadata #32
This commit is contained in:
parent
5fe945fca8
commit
f2f3eb76e1
15
README.md
15
README.md
@ -169,6 +169,21 @@ JPEG, PNG or WebP format image data can be streamed into the object when `input`
|
|||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
#### metadata([callback])
|
||||||
|
|
||||||
|
Fast access to image metadata without decoding any compressed image data.
|
||||||
|
|
||||||
|
`callback`, if present, gets the arguments `(err, metadata)` where `metadata` has the attributes:
|
||||||
|
|
||||||
|
* `format`: Name of decoder to be used to decompress image data e.g. `jpeg`, `png`, `webp` (for file-based input additionally `tiff` and `magick`)
|
||||||
|
* `width`: Number of pixels wide
|
||||||
|
* `height`: Number of pixels high
|
||||||
|
* `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `scrgb`, `cmyk`, `lab`, `xyz`, `b-w` [...](https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L502)
|
||||||
|
* `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
|
||||||
|
* `orientation`: Number value of the EXIF Orientation header, if present
|
||||||
|
|
||||||
|
A Promises/A+ promise is returned when `callback` is not provided.
|
||||||
|
|
||||||
#### sequentialRead()
|
#### sequentialRead()
|
||||||
|
|
||||||
An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`. This will reduce memory usage and can improve performance on some systems.
|
An advanced setting that switches the libvips access method to `VIPS_ACCESS_SEQUENTIAL`. This will reduce memory usage and can improve performance on some systems.
|
||||||
|
42
index.js
42
index.js
@ -347,6 +347,48 @@ Sharp.prototype._sharp = function(callback) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reads the image header and returns metadata
|
||||||
|
Supports callback, stream and promise variants
|
||||||
|
*/
|
||||||
|
Sharp.prototype.metadata = function(callback) {
|
||||||
|
var that = this;
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
if (this.options.streamIn) {
|
||||||
|
this.on('finish', function() {
|
||||||
|
sharp.metadata(that.options, callback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
sharp.metadata(this.options, callback);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
if (this.options.streamIn) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
that.on('finish', function() {
|
||||||
|
sharp.metadata(that.options, function(err, data) {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
sharp.metadata(that.options, function(err, data) {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get and set cache memory and item limits
|
Get and set cache memory and item limits
|
||||||
*/
|
*/
|
||||||
|
260
src/sharp.cc
260
src/sharp.cc
@ -46,6 +46,7 @@ struct resize_baton {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
UNKNOWN,
|
||||||
JPEG,
|
JPEG,
|
||||||
PNG,
|
PNG,
|
||||||
WEBP,
|
WEBP,
|
||||||
@ -153,69 +154,223 @@ sharp_calc_crop(int const inWidth, int const inHeight, int const outWidth, int c
|
|||||||
return std::make_tuple(left, top);
|
return std::make_tuple(left, top);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ResizeWorker : public NanAsyncWorker {
|
/*
|
||||||
public:
|
Initialise a VipsImage from a buffer. Supports JPEG, PNG and WebP.
|
||||||
ResizeWorker(NanCallback *callback, resize_baton *baton)
|
Returns the ImageType detected, if any.
|
||||||
: NanAsyncWorker(callback), baton(baton) {}
|
*/
|
||||||
~ResizeWorker() {}
|
static ImageType
|
||||||
|
sharp_init_image_from_buffer(VipsImage **image, void *buffer, size_t const length, VipsAccess const access) {
|
||||||
|
ImageType imageType = UNKNOWN;
|
||||||
|
if (memcmp(MARKER_JPEG, buffer, 2) == 0) {
|
||||||
|
if (!vips_jpegload_buffer(buffer, length, image, "access", access, NULL)) {
|
||||||
|
imageType = JPEG;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
if (!vips_webpload_buffer(buffer, length, image, "access", access, NULL)) {
|
||||||
|
imageType = WEBP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return imageType;
|
||||||
|
}
|
||||||
|
|
||||||
void Execute () {
|
/*
|
||||||
|
Initialise a VipsImage from a file.
|
||||||
|
Returns the ImageType detected, if any.
|
||||||
|
*/
|
||||||
|
static ImageType
|
||||||
|
sharp_init_image_from_file(VipsImage **image, char const *file, VipsAccess const access) {
|
||||||
|
ImageType imageType = UNKNOWN;
|
||||||
|
if (vips_foreign_is_a("jpegload", file)) {
|
||||||
|
if (!vips_jpegload(file, image, "access", access, NULL)) {
|
||||||
|
imageType = JPEG;
|
||||||
|
}
|
||||||
|
} else if (vips_foreign_is_a("pngload", file)) {
|
||||||
|
if (!vips_pngload(file, image, "access", access, NULL)) {
|
||||||
|
imageType = PNG;
|
||||||
|
}
|
||||||
|
} else if (vips_foreign_is_a("webpload", file)) {
|
||||||
|
if (!vips_webpload(file, image, "access", access, NULL)) {
|
||||||
|
imageType = WEBP;
|
||||||
|
}
|
||||||
|
} else if (vips_foreign_is_a("tiffload", file)) {
|
||||||
|
if (!vips_tiffload(file, image, "access", access, NULL)) {
|
||||||
|
imageType = TIFF;
|
||||||
|
}
|
||||||
|
} else if(vips_foreign_is_a("magickload", file)) {
|
||||||
|
if (!vips_magickload(file, image, "access", access, NULL)) {
|
||||||
|
imageType = MAGICK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return imageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata
|
||||||
|
|
||||||
|
struct metadata_baton {
|
||||||
|
// Input
|
||||||
|
std::string file_in;
|
||||||
|
void* buffer_in;
|
||||||
|
size_t buffer_in_len;
|
||||||
|
// Output
|
||||||
|
std::string format;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
std::string space;
|
||||||
|
int channels;
|
||||||
|
int orientation;
|
||||||
|
std::string err;
|
||||||
|
|
||||||
|
metadata_baton():
|
||||||
|
buffer_in_len(0),
|
||||||
|
orientation(0) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MetadataWorker : public NanAsyncWorker {
|
||||||
|
|
||||||
|
public:
|
||||||
|
MetadataWorker(NanCallback *callback, metadata_baton *baton) : NanAsyncWorker(callback), baton(baton) {}
|
||||||
|
~MetadataWorker() {}
|
||||||
|
|
||||||
|
void Execute() {
|
||||||
|
// Decrement queued task counter
|
||||||
|
g_atomic_int_dec_and_test(&counter_queue);
|
||||||
|
|
||||||
|
ImageType imageType = UNKNOWN;
|
||||||
|
VipsImage *image = vips_image_new();
|
||||||
|
if (baton->buffer_in_len > 1) {
|
||||||
|
// From buffer
|
||||||
|
imageType = sharp_init_image_from_buffer(&image, baton->buffer_in, baton->buffer_in_len, VIPS_ACCESS_RANDOM);
|
||||||
|
if (imageType == UNKNOWN) {
|
||||||
|
(baton->err).append("Input buffer contains unsupported image format");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// From file
|
||||||
|
imageType = sharp_init_image_from_file(&image, baton->file_in.c_str(), VIPS_ACCESS_RANDOM);
|
||||||
|
if (imageType == UNKNOWN) {
|
||||||
|
(baton->err).append("File is of an unsupported image format");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (imageType != UNKNOWN) {
|
||||||
|
// Image type
|
||||||
|
switch (imageType) {
|
||||||
|
case JPEG: baton->format = "jpeg"; break;
|
||||||
|
case PNG: baton->format = "png"; break;
|
||||||
|
case WEBP: baton->format = "webp"; break;
|
||||||
|
case TIFF: baton->format = "tiff"; break;
|
||||||
|
case MAGICK: baton->format = "magick"; break;
|
||||||
|
case UNKNOWN: default: baton->format = "";
|
||||||
|
}
|
||||||
|
// VipsImage attributes
|
||||||
|
baton->width = image->Xsize;
|
||||||
|
baton->height = image->Ysize;
|
||||||
|
baton->space = vips_enum_nick(VIPS_TYPE_INTERPRETATION, image->Type);
|
||||||
|
baton->channels = image->Bands;
|
||||||
|
// EXIF Orientation
|
||||||
|
const char *exif;
|
||||||
|
if (!vips_image_get_string(image, "exif-ifd0-Orientation", &exif)) {
|
||||||
|
baton->orientation = atoi(&exif[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clean up
|
||||||
|
g_object_unref(image);
|
||||||
|
vips_error_clear();
|
||||||
|
vips_thread_shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleOKCallback () {
|
||||||
|
NanScope();
|
||||||
|
|
||||||
|
Handle<Value> argv[2] = { NanNull(), NanNull() };
|
||||||
|
if (!baton->err.empty()) {
|
||||||
|
// Error
|
||||||
|
argv[0] = NanNew<String>(baton->err.data(), baton->err.size());
|
||||||
|
} else {
|
||||||
|
// Metadata Object
|
||||||
|
Local<Object> info = NanNew<Object>();
|
||||||
|
info->Set(NanNew<String>("format"), NanNew<String>(baton->format));
|
||||||
|
info->Set(NanNew<String>("width"), NanNew<Number>(baton->width));
|
||||||
|
info->Set(NanNew<String>("height"), NanNew<Number>(baton->height));
|
||||||
|
info->Set(NanNew<String>("space"), NanNew<String>(baton->space));
|
||||||
|
info->Set(NanNew<String>("channels"), NanNew<Number>(baton->channels));
|
||||||
|
if (baton->orientation > 0) {
|
||||||
|
info->Set(NanNew<String>("orientation"), NanNew<Number>(baton->orientation));
|
||||||
|
}
|
||||||
|
argv[1] = info;
|
||||||
|
}
|
||||||
|
delete baton;
|
||||||
|
|
||||||
|
// Return to JavaScript
|
||||||
|
callback->Call(2, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
metadata_baton* baton;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
metadata(options, callback)
|
||||||
|
*/
|
||||||
|
NAN_METHOD(metadata) {
|
||||||
|
NanScope();
|
||||||
|
|
||||||
|
// V8 objects are converted to non-V8 types held in the baton struct
|
||||||
|
metadata_baton *baton = new metadata_baton;
|
||||||
|
Local<Object> options = args[0]->ToObject();
|
||||||
|
|
||||||
|
// Input filename
|
||||||
|
baton->file_in = *String::Utf8Value(options->Get(NanNew<String>("fileIn"))->ToString());
|
||||||
|
// Input Buffer object
|
||||||
|
if (options->Get(NanNew<String>("bufferIn"))->IsObject()) {
|
||||||
|
Local<Object> buffer = options->Get(NanNew<String>("bufferIn"))->ToObject();
|
||||||
|
baton->buffer_in_len = Buffer::Length(buffer);
|
||||||
|
baton->buffer_in = Buffer::Data(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join queue for worker thread
|
||||||
|
NanCallback *callback = new NanCallback(args[1].As<v8::Function>());
|
||||||
|
NanAsyncQueueWorker(new MetadataWorker(callback, baton));
|
||||||
|
|
||||||
|
// Increment queued task counter
|
||||||
|
g_atomic_int_inc(&counter_queue);
|
||||||
|
|
||||||
|
NanReturnUndefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize
|
||||||
|
|
||||||
|
class ResizeWorker : public NanAsyncWorker {
|
||||||
|
public:
|
||||||
|
ResizeWorker(NanCallback *callback, resize_baton *baton) : NanAsyncWorker(callback), baton(baton) {}
|
||||||
|
~ResizeWorker() {}
|
||||||
|
|
||||||
|
void Execute() {
|
||||||
// Decrement queued task counter
|
// Decrement queued task counter
|
||||||
g_atomic_int_dec_and_test(&counter_queue);
|
g_atomic_int_dec_and_test(&counter_queue);
|
||||||
// Increment processing task counter
|
// Increment processing task counter
|
||||||
g_atomic_int_inc(&counter_process);
|
g_atomic_int_inc(&counter_process);
|
||||||
|
|
||||||
// Input
|
// Input
|
||||||
ImageType inputImageType = JPEG;
|
ImageType inputImageType = UNKNOWN;
|
||||||
VipsImage *in = vips_image_new();
|
VipsImage *in = vips_image_new();
|
||||||
if (baton->buffer_in_len > 1) {
|
if (baton->buffer_in_len > 1) {
|
||||||
if (memcmp(MARKER_JPEG, baton->buffer_in, 2) == 0) {
|
// From buffer
|
||||||
if (vips_jpegload_buffer(baton->buffer_in, baton->buffer_in_len, &in, "access", baton->access_method, NULL)) {
|
inputImageType = sharp_init_image_from_buffer(&in, baton->buffer_in, baton->buffer_in_len, baton->access_method);
|
||||||
return resize_error(baton, in);
|
if (inputImageType == UNKNOWN) {
|
||||||
}
|
(baton->err).append("Input buffer contains unsupported image format");
|
||||||
} else if(memcmp(MARKER_PNG, baton->buffer_in, 2) == 0) {
|
|
||||||
inputImageType = PNG;
|
|
||||||
if (vips_pngload_buffer(baton->buffer_in, baton->buffer_in_len, &in, "access", baton->access_method, NULL)) {
|
|
||||||
return resize_error(baton, in);
|
|
||||||
}
|
|
||||||
} else if(memcmp(MARKER_WEBP, baton->buffer_in, 2) == 0) {
|
|
||||||
inputImageType = WEBP;
|
|
||||||
if (vips_webpload_buffer(baton->buffer_in, baton->buffer_in_len, &in, "access", baton->access_method, NULL)) {
|
|
||||||
return resize_error(baton, in);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resize_error(baton, in);
|
|
||||||
(baton->err).append("Unsupported input buffer");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (vips_foreign_is_a("jpegload", baton->file_in.c_str())) {
|
|
||||||
if (vips_jpegload((baton->file_in).c_str(), &in, "access", baton->access_method, NULL)) {
|
|
||||||
return resize_error(baton, in);
|
|
||||||
}
|
|
||||||
} else if (vips_foreign_is_a("pngload", baton->file_in.c_str())) {
|
|
||||||
inputImageType = PNG;
|
|
||||||
if (vips_pngload((baton->file_in).c_str(), &in, "access", baton->access_method, NULL)) {
|
|
||||||
return resize_error(baton, in);
|
|
||||||
}
|
|
||||||
} else if (vips_foreign_is_a("webpload", baton->file_in.c_str())) {
|
|
||||||
inputImageType = WEBP;
|
|
||||||
if (vips_webpload((baton->file_in).c_str(), &in, "access", baton->access_method, NULL)) {
|
|
||||||
return resize_error(baton, in);
|
|
||||||
}
|
|
||||||
} else if (vips_foreign_is_a("tiffload", baton->file_in.c_str())) {
|
|
||||||
inputImageType = TIFF;
|
|
||||||
if (vips_tiffload((baton->file_in).c_str(), &in, "access", baton->access_method, NULL)) {
|
|
||||||
return resize_error(baton, in);
|
|
||||||
}
|
|
||||||
} else if(vips_foreign_is_a("magickload", (baton->file_in).c_str())) {
|
|
||||||
inputImageType = MAGICK;
|
|
||||||
if (vips_magickload((baton->file_in).c_str(), &in, "access", baton->access_method, NULL)) {
|
|
||||||
return resize_error(baton, in);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resize_error(baton, in);
|
// From file
|
||||||
(baton->err).append("Unsupported input file " + baton->file_in);
|
inputImageType = sharp_init_image_from_file(&in, baton->file_in.c_str(), baton->access_method);
|
||||||
return;
|
if (inputImageType == UNKNOWN) {
|
||||||
|
(baton->err).append("File is of an unsupported image format");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inputImageType == UNKNOWN) {
|
||||||
|
return resize_error(baton, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get input image width and height
|
// Get input image width and height
|
||||||
@ -613,6 +768,7 @@ extern "C" void init(Handle<Object> target) {
|
|||||||
vips_cache_set_max_mem(100 * 1048576); // 100 MB
|
vips_cache_set_max_mem(100 * 1048576); // 100 MB
|
||||||
vips_cache_set_max(500); // 500 operations
|
vips_cache_set_max(500); // 500 operations
|
||||||
// Methods available to JavaScript
|
// Methods available to JavaScript
|
||||||
|
NODE_SET_METHOD(target, "metadata", metadata);
|
||||||
NODE_SET_METHOD(target, "resize", resize);
|
NODE_SET_METHOD(target, "resize", resize);
|
||||||
NODE_SET_METHOD(target, "cache", cache);
|
NODE_SET_METHOD(target, "cache", cache);
|
||||||
NODE_SET_METHOD(target, "counters", counters);
|
NODE_SET_METHOD(target, "counters", counters);
|
||||||
|
122
tests/unit.js
122
tests/unit.js
@ -18,6 +18,10 @@ var outputTiff = path.join(fixturesPath, "output.tiff");
|
|||||||
|
|
||||||
var inputJpgWithExif = path.join(fixturesPath, "Landscape_8.jpg"); // https://github.com/recurser/exif-orientation-examples/blob/master/Landscape_8.jpg
|
var inputJpgWithExif = path.join(fixturesPath, "Landscape_8.jpg"); // https://github.com/recurser/exif-orientation-examples/blob/master/Landscape_8.jpg
|
||||||
|
|
||||||
|
var inputPng = path.join(fixturesPath, "50020484-00001.png"); // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
|
||||||
|
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
|
||||||
|
|
||||||
// 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
|
||||||
@ -398,6 +402,124 @@ async.series([
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// Metadata - JPEG
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpg).metadata(function(err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', metadata.format);
|
||||||
|
assert.strictEqual(2725, metadata.width);
|
||||||
|
assert.strictEqual(2225, metadata.height);
|
||||||
|
assert.strictEqual('srgb', metadata.space);
|
||||||
|
assert.strictEqual(3, metadata.channels);
|
||||||
|
assert.strictEqual('undefined', typeof metadata.orientation);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Metadata - JPEG with EXIF
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpgWithExif).metadata(function(err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', metadata.format);
|
||||||
|
assert.strictEqual(450, metadata.width);
|
||||||
|
assert.strictEqual(600, metadata.height);
|
||||||
|
assert.strictEqual('srgb', metadata.space);
|
||||||
|
assert.strictEqual(3, metadata.channels);
|
||||||
|
assert.strictEqual(8, metadata.orientation);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Metadata - TIFF
|
||||||
|
function(done) {
|
||||||
|
sharp(inputTiff).metadata(function(err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('tiff', metadata.format);
|
||||||
|
assert.strictEqual(2464, metadata.width);
|
||||||
|
assert.strictEqual(3248, metadata.height);
|
||||||
|
assert.strictEqual('b-w', metadata.space);
|
||||||
|
assert.strictEqual(1, metadata.channels);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Metadata - PNG
|
||||||
|
function(done) {
|
||||||
|
sharp(inputPng).metadata(function(err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', metadata.format);
|
||||||
|
assert.strictEqual(2809, metadata.width);
|
||||||
|
assert.strictEqual(2074, metadata.height);
|
||||||
|
assert.strictEqual('b-w', metadata.space);
|
||||||
|
assert.strictEqual(1, metadata.channels);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Metadata - WebP
|
||||||
|
function(done) {
|
||||||
|
sharp(inputWebp).metadata(function(err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('webp', metadata.format);
|
||||||
|
assert.strictEqual(1024, metadata.width);
|
||||||
|
assert.strictEqual(772, metadata.height);
|
||||||
|
assert.strictEqual('srgb', metadata.space);
|
||||||
|
assert.strictEqual(3, metadata.channels);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Metadata - GIF (via libmagick)
|
||||||
|
function(done) {
|
||||||
|
sharp(inputGif).metadata(function(err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('magick', metadata.format);
|
||||||
|
assert.strictEqual(800, metadata.width);
|
||||||
|
assert.strictEqual(533, metadata.height);
|
||||||
|
assert.strictEqual('srgb', metadata.space);
|
||||||
|
assert.strictEqual(3, metadata.channels);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Metadata - Promise
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpg).metadata().then(function(metadata) {
|
||||||
|
assert.strictEqual('jpeg', metadata.format);
|
||||||
|
assert.strictEqual(2725, metadata.width);
|
||||||
|
assert.strictEqual(2225, metadata.height);
|
||||||
|
assert.strictEqual('srgb', metadata.space);
|
||||||
|
assert.strictEqual(3, metadata.channels);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Metadata - Stream
|
||||||
|
function(done) {
|
||||||
|
var readable = fs.createReadStream(inputJpg);
|
||||||
|
var pipeline = sharp().metadata(function(err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', metadata.format);
|
||||||
|
assert.strictEqual(2725, metadata.width);
|
||||||
|
assert.strictEqual(2225, metadata.height);
|
||||||
|
assert.strictEqual('srgb', metadata.space);
|
||||||
|
assert.strictEqual(3, metadata.channels);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
readable.pipe(pipeline);
|
||||||
|
},
|
||||||
|
// Get metadata then resize to half width
|
||||||
|
function(done) {
|
||||||
|
var image = sharp(inputJpg);
|
||||||
|
image.metadata(function(err, metadata) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', metadata.format);
|
||||||
|
assert.strictEqual(2725, metadata.width);
|
||||||
|
assert.strictEqual(2225, metadata.height);
|
||||||
|
assert.strictEqual('srgb', metadata.space);
|
||||||
|
assert.strictEqual(3, metadata.channels);
|
||||||
|
image.resize(metadata.width / 2).toBuffer(function(err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, data.length > 0);
|
||||||
|
assert.strictEqual(1362, info.width);
|
||||||
|
assert.strictEqual(1112, info.height);
|
||||||
|
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