mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Add support for stand-alone, pre-resize, and post-resize extraction with a topOffset, leftOffset, width, and height.
This commit is contained in:
parent
6190ca4307
commit
83b72a1ede
17
index.js
17
index.js
@ -19,6 +19,14 @@ var Sharp = function(input) {
|
|||||||
streamIn: false,
|
streamIn: false,
|
||||||
sequentialRead: false,
|
sequentialRead: false,
|
||||||
// resize options
|
// resize options
|
||||||
|
topOffsetPre: -1,
|
||||||
|
leftOffsetPre: -1,
|
||||||
|
widthPre: -1,
|
||||||
|
heightPre: -1,
|
||||||
|
topOffsetPost: -1,
|
||||||
|
leftOffsetPost: -1,
|
||||||
|
widthPost: -1,
|
||||||
|
heightPost: -1,
|
||||||
width: -1,
|
width: -1,
|
||||||
height: -1,
|
height: -1,
|
||||||
canvas: 'c',
|
canvas: 'c',
|
||||||
@ -102,6 +110,15 @@ Sharp.prototype.crop = function(gravity) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Sharp.prototype.extract = function(topOffset, leftOffset, width, height) {
|
||||||
|
var suffix = this.options.width === -1 && this.options.height === -1 ? 'Pre' : 'Post';
|
||||||
|
var values = arguments;
|
||||||
|
['topOffset', 'leftOffset', 'width', 'height'].forEach(function(name, index) {
|
||||||
|
this.options[name + suffix] = values[index];
|
||||||
|
}.bind(this));
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Deprecated embed* methods, to be removed in v0.8.0
|
Deprecated embed* methods, to be removed in v0.8.0
|
||||||
*/
|
*/
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
"Juliano Julio <julianojulio@gmail.com>",
|
"Juliano Julio <julianojulio@gmail.com>",
|
||||||
"Daniel Gasienica <daniel@gasienica.ch>",
|
"Daniel Gasienica <daniel@gasienica.ch>",
|
||||||
"Julian Walker <julian@fiftythree.com>",
|
"Julian Walker <julian@fiftythree.com>",
|
||||||
"Amit Pitaru <pitaru.amit@gmail.com>"
|
"Amit Pitaru <pitaru.amit@gmail.com>",
|
||||||
|
"Brandon Aaron <hello.brandon@aaron.sh>"
|
||||||
],
|
],
|
||||||
"description": "High performance Node.js module to resize JPEG, PNG and WebP images using the libvips library",
|
"description": "High performance Node.js module to resize JPEG, PNG and WebP images using the libvips library",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -30,6 +31,7 @@
|
|||||||
"thumbnail",
|
"thumbnail",
|
||||||
"sharpen",
|
"sharpen",
|
||||||
"crop",
|
"crop",
|
||||||
|
"extract",
|
||||||
"embed",
|
"embed",
|
||||||
"libvips",
|
"libvips",
|
||||||
"vips",
|
"vips",
|
||||||
|
56
src/sharp.cc
56
src/sharp.cc
@ -25,6 +25,14 @@ struct resize_baton {
|
|||||||
std::string output_format;
|
std::string output_format;
|
||||||
void* buffer_out;
|
void* buffer_out;
|
||||||
size_t buffer_out_len;
|
size_t buffer_out_len;
|
||||||
|
int topOffsetPre;
|
||||||
|
int leftOffsetPre;
|
||||||
|
int widthPre;
|
||||||
|
int heightPre;
|
||||||
|
int topOffsetPost;
|
||||||
|
int leftOffsetPost;
|
||||||
|
int widthPost;
|
||||||
|
int heightPost;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
Canvas canvas;
|
Canvas canvas;
|
||||||
@ -48,6 +56,8 @@ struct resize_baton {
|
|||||||
buffer_in_len(0),
|
buffer_in_len(0),
|
||||||
output_format(""),
|
output_format(""),
|
||||||
buffer_out_len(0),
|
buffer_out_len(0),
|
||||||
|
topOffsetPre(-1),
|
||||||
|
topOffsetPost(-1),
|
||||||
canvas(CROP),
|
canvas(CROP),
|
||||||
gravity(0),
|
gravity(0),
|
||||||
background{0.0, 0.0, 0.0, 255.0},
|
background{0.0, 0.0, 0.0, 255.0},
|
||||||
@ -417,6 +427,17 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
return resize_error(baton, hook);
|
return resize_error(baton, hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pre extraction
|
||||||
|
if (baton->topOffsetPre != -1) {
|
||||||
|
VipsImage *extractedPre = vips_image_new();
|
||||||
|
vips_object_local(hook, extractedPre);
|
||||||
|
if (vips_extract_area(image, &extractedPre, baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre, NULL)) {
|
||||||
|
return resize_error(baton, hook);
|
||||||
|
}
|
||||||
|
g_object_unref(image);
|
||||||
|
image = extractedPre;
|
||||||
|
}
|
||||||
|
|
||||||
// Get input image width and height
|
// Get input image width and height
|
||||||
int inputWidth = image->Xsize;
|
int inputWidth = image->Xsize;
|
||||||
int inputHeight = image->Ysize;
|
int inputHeight = image->Ysize;
|
||||||
@ -691,6 +712,17 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
image = canvased;
|
image = canvased;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Post extraction
|
||||||
|
if (baton->topOffsetPost != -1) {
|
||||||
|
VipsImage *extractedPost = vips_image_new();
|
||||||
|
vips_object_local(hook, extractedPost);
|
||||||
|
if (vips_extract_area(image, &extractedPost, baton->leftOffsetPost, baton->topOffsetPost, baton->widthPost, baton->heightPost, NULL)) {
|
||||||
|
return resize_error(baton, hook);
|
||||||
|
}
|
||||||
|
g_object_unref(image);
|
||||||
|
image = extractedPost;
|
||||||
|
}
|
||||||
|
|
||||||
// Mild sharpen
|
// Mild sharpen
|
||||||
if (baton->sharpen) {
|
if (baton->sharpen) {
|
||||||
VipsImage *sharpened = vips_image_new();
|
VipsImage *sharpened = vips_image_new();
|
||||||
@ -818,11 +850,21 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
// Error
|
// Error
|
||||||
argv[0] = NanNew<String>(baton->err.data(), baton->err.size());
|
argv[0] = NanNew<String>(baton->err.data(), baton->err.size());
|
||||||
} else {
|
} else {
|
||||||
|
int width = baton->width;
|
||||||
|
int height = baton->height;
|
||||||
|
if (baton->topOffsetPre != -1 && (baton->width == -1 || baton->height == -1)) {
|
||||||
|
width = baton->widthPre;
|
||||||
|
height = baton->heightPre;
|
||||||
|
}
|
||||||
|
if (baton->topOffsetPost != -1) {
|
||||||
|
width = baton->widthPost;
|
||||||
|
height = baton->heightPost;
|
||||||
|
}
|
||||||
// Info Object
|
// Info Object
|
||||||
Local<Object> info = NanNew<Object>();
|
Local<Object> info = NanNew<Object>();
|
||||||
info->Set(NanNew<String>("format"), NanNew<String>(baton->output_format));
|
info->Set(NanNew<String>("format"), NanNew<String>(baton->output_format));
|
||||||
info->Set(NanNew<String>("width"), NanNew<Number>(baton->width));
|
info->Set(NanNew<String>("width"), NanNew<Number>(width));
|
||||||
info->Set(NanNew<String>("height"), NanNew<Number>(baton->height));
|
info->Set(NanNew<String>("height"), NanNew<Number>(height));
|
||||||
|
|
||||||
if (baton->buffer_out_len > 0) {
|
if (baton->buffer_out_len > 0) {
|
||||||
// Buffer
|
// Buffer
|
||||||
@ -847,6 +889,7 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
resize_baton* baton;
|
resize_baton* baton;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
resize(options, output, callback)
|
resize(options, output, callback)
|
||||||
*/
|
*/
|
||||||
@ -866,6 +909,15 @@ NAN_METHOD(resize) {
|
|||||||
baton->buffer_in_len = Buffer::Length(buffer);
|
baton->buffer_in_len = Buffer::Length(buffer);
|
||||||
baton->buffer_in = Buffer::Data(buffer);
|
baton->buffer_in = Buffer::Data(buffer);
|
||||||
}
|
}
|
||||||
|
// Extract image options
|
||||||
|
baton->topOffsetPre = options->Get(NanNew<String>("topOffsetPre"))->Int32Value();
|
||||||
|
baton->leftOffsetPre = options->Get(NanNew<String>("leftOffsetPre"))->Int32Value();
|
||||||
|
baton->widthPre = options->Get(NanNew<String>("widthPre"))->Int32Value();
|
||||||
|
baton->heightPre = options->Get(NanNew<String>("heightPre"))->Int32Value();
|
||||||
|
baton->topOffsetPost = options->Get(NanNew<String>("topOffsetPost"))->Int32Value();
|
||||||
|
baton->leftOffsetPost = options->Get(NanNew<String>("leftOffsetPost"))->Int32Value();
|
||||||
|
baton->widthPost = options->Get(NanNew<String>("widthPost"))->Int32Value();
|
||||||
|
baton->heightPost = options->Get(NanNew<String>("heightPost"))->Int32Value();
|
||||||
// Output image dimensions
|
// Output image dimensions
|
||||||
baton->width = options->Get(NanNew<String>("width"))->Int32Value();
|
baton->width = options->Get(NanNew<String>("width"))->Int32Value();
|
||||||
baton->height = options->Get(NanNew<String>("height"))->Int32Value();
|
baton->height = options->Get(NanNew<String>("height"))->Int32Value();
|
||||||
|
@ -563,6 +563,69 @@ async.series([
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// Extract jpg
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpg).extract(2,2,20,20).toFile(path.join(fixturesPath, 'output.extract.jpg'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(20, info.width);
|
||||||
|
assert.strictEqual(20, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Extract png
|
||||||
|
function(done) {
|
||||||
|
sharp(inputPng).extract(300,200,400,200).toFile(path.join(fixturesPath, 'output.extract.png'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(400, info.width);
|
||||||
|
assert.strictEqual(200, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Extract webp
|
||||||
|
function(done) {
|
||||||
|
sharp(inputWebP).extract(50, 100, 125, 200).toFile(path.join(fixturesPath, 'output.extract.webp'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(125, info.width);
|
||||||
|
assert.strictEqual(200, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Extract tiff
|
||||||
|
function(done) {
|
||||||
|
sharp(inputTiff).extract(63, 34, 341, 529).toFile(path.join(fixturesPath, 'output.extract.tiff'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(341, info.width);
|
||||||
|
assert.strictEqual(529, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Extract before resize
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpg).extract(10, 10, 10, 500, 500).resize(100, 100).toFile(path.join(fixturesPath, 'output.extract.resize.jpg'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(100, info.width);
|
||||||
|
assert.strictEqual(100, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Extract after resize and crop
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpg).resize(500, 500).crop(sharp.gravity.north).extract(10, 10, 100, 100).toFile(path.join(fixturesPath, 'output.resize.crop.extract.jpg'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(100, info.width);
|
||||||
|
assert.strictEqual(100, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Extract before and after resize and crop
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpg).extract(0, 0, 700, 700).resize(500, 500).crop(sharp.gravity.north).extract(10, 10, 100, 100).toFile(path.join(fixturesPath, 'output.extract.resize.crop.extract.jpg'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(100, info.width);
|
||||||
|
assert.strictEqual(100, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
// Keeps Metadata after a resize
|
// Keeps Metadata after a resize
|
||||||
function(done) {
|
function(done) {
|
||||||
sharp(inputJpgWithExif).resize(320, 240).withMetadata().toBuffer(function(err, buffer) {
|
sharp(inputJpgWithExif).resize(320, 240).withMetadata().toBuffer(function(err, buffer) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user