mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Add withoutEnlargement option #36
This commit is contained in:
parent
906311d403
commit
9a05684302
@ -175,6 +175,12 @@ Rotate the output image by either an explicit angle or auto-orient based on the
|
|||||||
|
|
||||||
Use this method without `angle` to determine the angle from EXIF data.
|
Use this method without `angle` to determine the angle from EXIF data.
|
||||||
|
|
||||||
|
### withoutEnlargement()
|
||||||
|
|
||||||
|
Do not enlarge the output image if the input image width *or* height are already less than the required dimensions.
|
||||||
|
|
||||||
|
This is equivalent to GraphicsMagick's `>` geometry option: "change the dimensions of the image only if its width or height exceeds the geometry specification".
|
||||||
|
|
||||||
### sharpen()
|
### sharpen()
|
||||||
|
|
||||||
Perform a mild sharpen of the resultant image. This typically reduces performance by 30%.
|
Perform a mild sharpen of the resultant image. This typically reduces performance by 30%.
|
||||||
|
29
index.js
29
index.js
@ -12,6 +12,7 @@ var Sharp = function(input) {
|
|||||||
height: -1,
|
height: -1,
|
||||||
canvas: 'c',
|
canvas: 'c',
|
||||||
angle: 0,
|
angle: 0,
|
||||||
|
withoutEnlargement: false,
|
||||||
sharpen: false,
|
sharpen: false,
|
||||||
progressive: false,
|
progressive: false,
|
||||||
sequentialRead: false,
|
sequentialRead: false,
|
||||||
@ -20,9 +21,9 @@ var Sharp = function(input) {
|
|||||||
output: '__jpeg'
|
output: '__jpeg'
|
||||||
};
|
};
|
||||||
if (typeof input === 'string') {
|
if (typeof input === 'string') {
|
||||||
this.options.inFile = input;
|
this.options.fileIn = input;
|
||||||
} else if (typeof input ==='object' && input instanceof Buffer) {
|
} else if (typeof input ==='object' && input instanceof Buffer) {
|
||||||
this.options.inBuffer = input;
|
this.options.bufferIn = input;
|
||||||
} else {
|
} else {
|
||||||
throw 'Unsupported input ' + typeof input;
|
throw 'Unsupported input ' + typeof input;
|
||||||
}
|
}
|
||||||
@ -65,6 +66,16 @@ Sharp.prototype.rotate = function(angle) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Do not enlarge the output if the input width *or* height are already less than the required dimensions
|
||||||
|
This is equivalent to GraphicsMagick's ">" geometry option:
|
||||||
|
"change the dimensions of the image only if its width or height exceeds the geometry specification"
|
||||||
|
*/
|
||||||
|
Sharp.prototype.withoutEnlargement = function(withoutEnlargement) {
|
||||||
|
this.options.withoutEnlargement = (typeof withoutEnlargement === 'boolean') ? withoutEnlargement : true;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
Sharp.prototype.sharpen = function(sharpen) {
|
Sharp.prototype.sharpen = function(sharpen) {
|
||||||
this.options.sharpen = (typeof sharpen === 'boolean') ? sharpen : true;
|
this.options.sharpen = (typeof sharpen === 'boolean') ? sharpen : true;
|
||||||
return this;
|
return this;
|
||||||
@ -127,7 +138,7 @@ Sharp.prototype.toFile = function(output, callback) {
|
|||||||
if (!output || output.length === 0) {
|
if (!output || output.length === 0) {
|
||||||
callback('Invalid output');
|
callback('Invalid output');
|
||||||
} else {
|
} else {
|
||||||
if (this.options.inFile === output) {
|
if (this.options.fileIn === output) {
|
||||||
callback('Cannot use same file for input and output');
|
callback('Cannot use same file for input and output');
|
||||||
} else {
|
} else {
|
||||||
this._sharp(output, callback);
|
this._sharp(output, callback);
|
||||||
@ -157,18 +168,8 @@ Sharp.prototype.webp = function(callback) {
|
|||||||
|
|
||||||
Sharp.prototype._sharp = function(output, callback) {
|
Sharp.prototype._sharp = function(output, callback) {
|
||||||
sharp.resize(
|
sharp.resize(
|
||||||
this.options.inFile,
|
this.options,
|
||||||
this.options.inBuffer,
|
|
||||||
output,
|
output,
|
||||||
this.options.width,
|
|
||||||
this.options.height,
|
|
||||||
this.options.canvas,
|
|
||||||
this.options.sharpen,
|
|
||||||
this.options.progressive,
|
|
||||||
this.options.sequentialRead,
|
|
||||||
this.options.quality,
|
|
||||||
this.options.compressionLevel,
|
|
||||||
this.options.angle,
|
|
||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
return this;
|
return this;
|
||||||
|
64
src/sharp.cc
64
src/sharp.cc
@ -24,13 +24,21 @@ struct resize_baton {
|
|||||||
VipsExtend extend;
|
VipsExtend extend;
|
||||||
bool sharpen;
|
bool sharpen;
|
||||||
bool progressive;
|
bool progressive;
|
||||||
|
bool without_enlargement;
|
||||||
VipsAccess access_method;
|
VipsAccess access_method;
|
||||||
int quality;
|
int quality;
|
||||||
int compressionLevel;
|
int compressionLevel;
|
||||||
int angle;
|
int angle;
|
||||||
std::string err;
|
std::string err;
|
||||||
|
|
||||||
resize_baton(): buffer_in_len(0), buffer_out_len(0), crop(false), max(false), sharpen(false), progressive(false) {}
|
resize_baton():
|
||||||
|
buffer_in_len(0),
|
||||||
|
buffer_out_len(0),
|
||||||
|
crop(false),
|
||||||
|
max(false),
|
||||||
|
sharpen(false),
|
||||||
|
progressive(false),
|
||||||
|
without_enlargement(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -213,6 +221,17 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
}
|
}
|
||||||
double residual = static_cast<double>(shrink) / factor;
|
double residual = static_cast<double>(shrink) / factor;
|
||||||
|
|
||||||
|
// Do not enlarge the output if the input width *or* height are already less than the required dimensions
|
||||||
|
if (baton->without_enlargement) {
|
||||||
|
if (inputWidth < baton->width || inputHeight < baton->height) {
|
||||||
|
factor = 1;
|
||||||
|
shrink = 1;
|
||||||
|
residual = 0;
|
||||||
|
baton->width = inputWidth;
|
||||||
|
baton->height = inputHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try to use libjpeg shrink-on-load
|
// Try to use libjpeg shrink-on-load
|
||||||
int shrink_on_load = 1;
|
int shrink_on_load = 1;
|
||||||
if (inputImageType == JPEG) {
|
if (inputImageType == JPEG) {
|
||||||
@ -403,20 +422,29 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
resize_baton* baton;
|
resize_baton* baton;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
resize(options, output, callback)
|
||||||
|
*/
|
||||||
NAN_METHOD(resize) {
|
NAN_METHOD(resize) {
|
||||||
NanScope();
|
NanScope();
|
||||||
|
|
||||||
|
// V8 objects are converted to non-V8 types held in the baton struct
|
||||||
resize_baton *baton = new resize_baton;
|
resize_baton *baton = new resize_baton;
|
||||||
baton->file_in = *String::Utf8Value(args[0]->ToString());
|
Local<Object> options = args[0]->ToObject();
|
||||||
if (args[1]->IsObject()) {
|
|
||||||
Local<Object> buffer = args[1]->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_len = Buffer::Length(buffer);
|
||||||
baton->buffer_in = Buffer::Data(buffer);
|
baton->buffer_in = Buffer::Data(buffer);
|
||||||
}
|
}
|
||||||
baton->file_out = *String::Utf8Value(args[2]->ToString());
|
// Output image dimensions
|
||||||
baton->width = args[3]->Int32Value();
|
baton->width = options->Get(NanNew<String>("width"))->Int32Value();
|
||||||
baton->height = args[4]->Int32Value();
|
baton->height = options->Get(NanNew<String>("height"))->Int32Value();
|
||||||
Local<String> canvas = args[5]->ToString();
|
// Canvas options
|
||||||
|
Local<String> canvas = options->Get(NanNew<String>("canvas"))->ToString();
|
||||||
if (canvas->Equals(NanNew<String>("c"))) {
|
if (canvas->Equals(NanNew<String>("c"))) {
|
||||||
baton->crop = true;
|
baton->crop = true;
|
||||||
} else if (canvas->Equals(NanNew<String>("w"))) {
|
} else if (canvas->Equals(NanNew<String>("w"))) {
|
||||||
@ -426,15 +454,19 @@ NAN_METHOD(resize) {
|
|||||||
} else if (canvas->Equals(NanNew<String>("m"))) {
|
} else if (canvas->Equals(NanNew<String>("m"))) {
|
||||||
baton->max = true;
|
baton->max = true;
|
||||||
}
|
}
|
||||||
baton->sharpen = args[6]->BooleanValue();
|
// Other options
|
||||||
baton->progressive = args[7]->BooleanValue();
|
baton->sharpen = options->Get(NanNew<String>("sharpen"))->BooleanValue();
|
||||||
baton->access_method = args[8]->BooleanValue() ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
|
baton->progressive = options->Get(NanNew<String>("progressive"))->BooleanValue();
|
||||||
baton->quality = args[9]->Int32Value();
|
baton->without_enlargement = options->Get(NanNew<String>("withoutEnlargement"))->BooleanValue();
|
||||||
baton->compressionLevel = args[10]->Int32Value();
|
baton->access_method = options->Get(NanNew<String>("sequentialRead"))->BooleanValue() ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
|
||||||
baton->angle = args[11]->Int32Value();
|
baton->quality = options->Get(NanNew<String>("quality"))->Int32Value();
|
||||||
|
baton->compressionLevel = options->Get(NanNew<String>("compressionLevel"))->Int32Value();
|
||||||
NanCallback *callback = new NanCallback(args[12].As<v8::Function>());
|
baton->angle = options->Get(NanNew<String>("angle"))->Int32Value();
|
||||||
|
// Output filename or __format for Buffer
|
||||||
|
baton->file_out = *String::Utf8Value(args[1]->ToString());
|
||||||
|
|
||||||
|
// Join queue for worker thread
|
||||||
|
NanCallback *callback = new NanCallback(args[2].As<v8::Function>());
|
||||||
NanAsyncQueueWorker(new ResizeWorker(callback, baton));
|
NanAsyncQueueWorker(new ResizeWorker(callback, baton));
|
||||||
NanReturnUndefined();
|
NanReturnUndefined();
|
||||||
}
|
}
|
||||||
|
@ -212,6 +212,30 @@ async.series([
|
|||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
assert(!isValid);
|
assert(!isValid);
|
||||||
done();
|
done();
|
||||||
|
},
|
||||||
|
// Do not enlarge the output if the input width is already less than the output width
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpg).resize(2800).withoutEnlargement().write(outputJpg, function(err) {
|
||||||
|
if (err) throw err;
|
||||||
|
imagemagick.identify(outputJpg, function(err, features) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(2725, features.width);
|
||||||
|
assert.strictEqual(2225, features.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// Do not enlarge the output if the input height is already less than the output height
|
||||||
|
function(done) {
|
||||||
|
sharp(inputJpg).resize(null, 2300).withoutEnlargement().write(outputJpg, function(err) {
|
||||||
|
if (err) throw err;
|
||||||
|
imagemagick.identify(outputJpg, function(err, features) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(2725, features.width);
|
||||||
|
assert.strictEqual(2225, features.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user