feature: min

This commit is contained in:
Linus Unnebäck 2015-02-27 13:43:37 +01:00
parent 749dc61f85
commit bcd82f4893
5 changed files with 82 additions and 19 deletions

View File

@ -332,6 +332,12 @@ Preserving aspect ratio, resize the image to the maximum `width` or `height` spe
Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`. Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`.
#### min()
Preserving aspect ratio, resize the image to the minimum `width` or `height` specified.
Both `width` and `height` must be provided via `resize` otherwise the behaviour will default to `crop`.
#### background(rgba) #### background(rgba)
Set the background for the `embed` and `flatten` operations. Set the background for the `embed` and `flatten` operations.

View File

@ -41,7 +41,7 @@ var Sharp = function(input) {
heightPost: -1, heightPost: -1,
width: -1, width: -1,
height: -1, height: -1,
canvas: 'c', canvas: 'crop',
gravity: 0, gravity: 0,
angle: 0, angle: 0,
rotateBeforePreExtract: false, rotateBeforePreExtract: false,
@ -136,7 +136,7 @@ Sharp.prototype._write = function(chunk, encoding, callback) {
module.exports.gravity = {'center': 0, 'centre': 0, 'north': 1, 'east': 2, 'south': 3, 'west': 4}; module.exports.gravity = {'center': 0, 'centre': 0, 'north': 1, 'east': 2, 'south': 3, 'west': 4};
Sharp.prototype.crop = function(gravity) { Sharp.prototype.crop = function(gravity) {
this.options.canvas = 'c'; this.options.canvas = 'crop';
if (typeof gravity === 'number' && !Number.isNaN(gravity) && gravity >= 0 && gravity <= 4) { if (typeof gravity === 'number' && !Number.isNaN(gravity) && gravity >= 0 && gravity <= 4) {
this.options.gravity = gravity; this.options.gravity = gravity;
} else { } else {
@ -176,12 +176,17 @@ Sharp.prototype.background = function(rgba) {
}; };
Sharp.prototype.embed = function() { Sharp.prototype.embed = function() {
this.options.canvas = 'e'; this.options.canvas = 'embed';
return this; return this;
}; };
Sharp.prototype.max = function() { Sharp.prototype.max = function() {
this.options.canvas = 'm'; this.options.canvas = 'max';
return this;
};
Sharp.prototype.min = function() {
this.options.canvas = 'min';
return this; return this;
}; };

View File

@ -12,7 +12,8 @@
"Amit Pitaru <pitaru.amit@gmail.com>", "Amit Pitaru <pitaru.amit@gmail.com>",
"Brandon Aaron <hello.brandon@aaron.sh>", "Brandon Aaron <hello.brandon@aaron.sh>",
"Andreas Lind <andreas@one.com>", "Andreas Lind <andreas@one.com>",
"Maurus Cuelenaere <mcuelenaere@gmail.com>" "Maurus Cuelenaere <mcuelenaere@gmail.com>",
"Linus Unnebäck <linus@folkdatorn.se>"
], ],
"description": "High performance Node.js module to resize JPEG, PNG, WebP and TIFF images using the libvips library", "description": "High performance Node.js module to resize JPEG, PNG, WebP and TIFF images using the libvips library",
"scripts": { "scripts": {

View File

@ -37,8 +37,9 @@ using sharp::counterQueue;
enum class Canvas { enum class Canvas {
CROP, CROP,
EMBED,
MAX, MAX,
EMBED MIN
}; };
enum class Angle { enum class Angle {
@ -252,14 +253,29 @@ class ResizeWorker : public NanAsyncWorker {
// Fixed width and height // Fixed width and height
double xfactor = static_cast<double>(inputWidth) / static_cast<double>(baton->width); double xfactor = static_cast<double>(inputWidth) / static_cast<double>(baton->width);
double yfactor = static_cast<double>(inputHeight) / static_cast<double>(baton->height); double yfactor = static_cast<double>(inputHeight) / static_cast<double>(baton->height);
factor = (baton->canvas == Canvas::CROP) ? std::min(xfactor, yfactor) : std::max(xfactor, yfactor); switch (baton->canvas) {
// if max is set, we need to compute the real size of the thumb image case Canvas::CROP:
if (baton->canvas == Canvas::MAX) { factor = std::min(xfactor, yfactor);
break;
case Canvas::EMBED:
factor = std::max(xfactor, yfactor);
break;
case Canvas::MAX:
factor = std::max(xfactor, yfactor);
if (xfactor > yfactor) { if (xfactor > yfactor) {
baton->height = round(static_cast<double>(inputHeight) / xfactor); baton->height = round(static_cast<double>(inputHeight) / xfactor);
} else { } else {
baton->width = round(static_cast<double>(inputWidth) / yfactor); baton->width = round(static_cast<double>(inputWidth) / yfactor);
} }
break;
case Canvas::MIN:
factor = std::min(xfactor, yfactor);
if (xfactor < yfactor) {
baton->height = round(static_cast<double>(inputHeight) / xfactor);
} else {
baton->width = round(static_cast<double>(inputWidth) / yfactor);
}
break;
} }
} else if (baton->width > 0) { } else if (baton->width > 0) {
// Fixed width, auto height // Fixed width, auto height
@ -549,7 +565,7 @@ class ResizeWorker : public NanAsyncWorker {
vips_object_local(hook, embedded); vips_object_local(hook, embedded);
image = embedded; image = embedded;
} else { } else {
// Crop/max // Crop/max/min
int left; int left;
int top; int top;
std::tie(left, top) = CalculateCrop(image->Xsize, image->Ysize, baton->width, baton->height, baton->gravity); std::tie(left, top) = CalculateCrop(image->Xsize, image->Ysize, baton->width, baton->height, baton->gravity);
@ -963,12 +979,14 @@ NAN_METHOD(resize) {
baton->height = options->Get(NanNew<String>("height"))->Int32Value(); baton->height = options->Get(NanNew<String>("height"))->Int32Value();
// Canvas option // Canvas option
Local<String> canvas = options->Get(NanNew<String>("canvas"))->ToString(); Local<String> canvas = options->Get(NanNew<String>("canvas"))->ToString();
if (canvas->Equals(NanNew<String>("c"))) { if (canvas->Equals(NanNew<String>("crop"))) {
baton->canvas = Canvas::CROP; baton->canvas = Canvas::CROP;
} else if (canvas->Equals(NanNew<String>("m"))) { } else if (canvas->Equals(NanNew<String>("embed"))) {
baton->canvas = Canvas::MAX;
} else if (canvas->Equals(NanNew<String>("e"))) {
baton->canvas = Canvas::EMBED; baton->canvas = Canvas::EMBED;
} else if (canvas->Equals(NanNew<String>("max"))) {
baton->canvas = Canvas::MAX;
} else if (canvas->Equals(NanNew<String>("min"))) {
baton->canvas = Canvas::MIN;
} }
// Background colour // Background colour
Local<Array> background = Local<Array>::Cast(options->Get(NanNew<String>("background"))); Local<Array> background = Local<Array>::Cast(options->Get(NanNew<String>("background")));

View File

@ -185,6 +185,39 @@ describe('Resize dimensions', function() {
}); });
}); });
it('Min width or height considering ratio (landscape)', function(done) {
sharp(fixtures.inputJpg).resize(320, 320).min().toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(392, info.width);
assert.strictEqual(320, info.height);
done();
});
});
it('Min width or height considering ratio (portrait)', function(done) {
sharp(fixtures.inputTiff).resize(320, 320).min().jpeg().toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(422, info.height);
done();
});
});
it('Provide only one dimension with min, should default to crop', function(done) {
sharp(fixtures.inputJpg).resize(320).min().toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(261, info.height);
done();
});
});
it('Do not enlarge when input width is already less than output width', function(done) { it('Do not enlarge when input width is already less than output width', function(done) {
sharp(fixtures.inputJpg) sharp(fixtures.inputJpg)
.resize(2800) .resize(2800)