mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 10:30:15 +02:00
Add 'fast' blur and Gaussian blur feature #108
This commit is contained in:
parent
df5cf402e3
commit
32d9bc204a
12
README.md
12
README.md
@ -319,11 +319,19 @@ Do not enlarge the output image if the input image width *or* height are already
|
|||||||
|
|
||||||
This is equivalent to GraphicsMagick's `>` geometry option: "change the dimensions of the image only if its width or height exceeds the geometry specification".
|
This is equivalent to GraphicsMagick's `>` geometry option: "change the dimensions of the image only if its width or height exceeds the geometry specification".
|
||||||
|
|
||||||
|
#### blur([radius])
|
||||||
|
|
||||||
|
When used without parameters, performs a fast, mild blur of the output image. This typically reduces performance by 10%.
|
||||||
|
|
||||||
|
When a `radius` is provided, performs a slower, more accurate Gaussian blur. This typically reduces performance by 30%.
|
||||||
|
|
||||||
|
* `radius`, if present, is an integral Number representing the approximate blur mask radius in pixels.
|
||||||
|
|
||||||
#### sharpen([radius], [flat], [jagged])
|
#### sharpen([radius], [flat], [jagged])
|
||||||
|
|
||||||
When used without parameters, perform a fast, mild sharpen of the output image. This typically reduces performance by 10%.
|
When used without parameters, performs a fast, mild sharpen of the output image. This typically reduces performance by 10%.
|
||||||
|
|
||||||
When a `radius` is provided, perform a slower, more accurate sharpen of the L channel in the LAB colour space. Separate control over the level of sharpening in "flat" and "jagged" areas is available. This typically reduces performance by 50%.
|
When a `radius` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space. Separate control over the level of sharpening in "flat" and "jagged" areas is available. This typically reduces performance by 50%.
|
||||||
|
|
||||||
* `radius`, if present, is an integral Number representing the sharpen mask radius in pixels.
|
* `radius`, if present, is an integral Number representing the sharpen mask radius in pixels.
|
||||||
* `flat`, if present, is a Number representing the level of sharpening to apply to "flat" areas, defaulting to a value of 1.0.
|
* `flat`, if present, is a Number representing the level of sharpening to apply to "flat" areas, defaulting to a value of 1.0.
|
||||||
|
22
index.js
22
index.js
@ -43,6 +43,7 @@ var Sharp = function(input) {
|
|||||||
// operations
|
// operations
|
||||||
background: [0, 0, 0, 255],
|
background: [0, 0, 0, 255],
|
||||||
flatten: false,
|
flatten: false,
|
||||||
|
blurRadius: 0,
|
||||||
sharpenRadius: 0,
|
sharpenRadius: 0,
|
||||||
sharpenFlat: 1,
|
sharpenFlat: 1,
|
||||||
sharpenJagged: 2,
|
sharpenJagged: 2,
|
||||||
@ -207,6 +208,27 @@ Sharp.prototype.withoutEnlargement = function(withoutEnlargement) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
Blur the output image.
|
||||||
|
Call without a radius to use a fast, mild blur.
|
||||||
|
Call with a radius to use a slower, more accurate Gaussian blur.
|
||||||
|
*/
|
||||||
|
Sharp.prototype.blur = function(radius) {
|
||||||
|
if (typeof radius === 'undefined') {
|
||||||
|
// No arguments: default to mild blur
|
||||||
|
this.options.blurRadius = -1;
|
||||||
|
} else if (typeof radius === 'boolean') {
|
||||||
|
// Boolean argument: apply mild blur?
|
||||||
|
this.options.blurRadius = radius ? -1 : 0;
|
||||||
|
} else if (typeof radius === 'number' && !Number.isNaN(radius) && (radius % 1 === 0)) {
|
||||||
|
// Numeric argument: specific radius
|
||||||
|
this.options.blurRadius = radius;
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid integral blur radius ' + radius);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Sharpen the output image.
|
Sharpen the output image.
|
||||||
Call without a radius to use a fast, mild sharpen.
|
Call without a radius to use a fast, mild sharpen.
|
||||||
|
@ -50,6 +50,7 @@ struct ResizeBaton {
|
|||||||
std::string interpolator;
|
std::string interpolator;
|
||||||
double background[4];
|
double background[4];
|
||||||
bool flatten;
|
bool flatten;
|
||||||
|
int blurRadius;
|
||||||
int sharpenRadius;
|
int sharpenRadius;
|
||||||
double sharpenFlat;
|
double sharpenFlat;
|
||||||
double sharpenJagged;
|
double sharpenJagged;
|
||||||
@ -76,6 +77,7 @@ struct ResizeBaton {
|
|||||||
canvas(CROP),
|
canvas(CROP),
|
||||||
gravity(0),
|
gravity(0),
|
||||||
flatten(false),
|
flatten(false),
|
||||||
|
blurRadius(0),
|
||||||
sharpenRadius(0),
|
sharpenRadius(0),
|
||||||
sharpenFlat(1.0),
|
sharpenFlat(1.0),
|
||||||
sharpenJagged(2.0),
|
sharpenJagged(2.0),
|
||||||
@ -510,6 +512,31 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
image = extractedPost;
|
image = extractedPost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Blur
|
||||||
|
if (baton->blurRadius != 0) {
|
||||||
|
VipsImage *blurred = vips_image_new();
|
||||||
|
vips_object_local(hook, blurred);
|
||||||
|
if (baton->blurRadius == -1) {
|
||||||
|
// Fast, mild blur
|
||||||
|
VipsImage *blur = vips_image_new_matrixv(3, 3,
|
||||||
|
1.0, 1.0, 1.0,
|
||||||
|
1.0, 1.0, 1.0,
|
||||||
|
1.0, 1.0, 1.0);
|
||||||
|
vips_image_set_double(blur, "scale", 9);
|
||||||
|
vips_object_local(hook, blur);
|
||||||
|
if (vips_conv(image, &blurred, blur, NULL)) {
|
||||||
|
return Error(baton, hook);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Slower, accurate Gaussian blur
|
||||||
|
if (vips_gaussblur(image, &blurred, baton->blurRadius, NULL)) {
|
||||||
|
return Error(baton, hook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_object_unref(image);
|
||||||
|
image = blurred;
|
||||||
|
}
|
||||||
|
|
||||||
// Sharpen
|
// Sharpen
|
||||||
if (baton->sharpenRadius != 0) {
|
if (baton->sharpenRadius != 0) {
|
||||||
VipsImage *sharpened = vips_image_new();
|
VipsImage *sharpened = vips_image_new();
|
||||||
@ -850,6 +877,7 @@ NAN_METHOD(resize) {
|
|||||||
baton->interpolator = *String::Utf8Value(options->Get(NanNew<String>("interpolator"))->ToString());
|
baton->interpolator = *String::Utf8Value(options->Get(NanNew<String>("interpolator"))->ToString());
|
||||||
// Operators
|
// Operators
|
||||||
baton->flatten = options->Get(NanNew<String>("flatten"))->BooleanValue();
|
baton->flatten = options->Get(NanNew<String>("flatten"))->BooleanValue();
|
||||||
|
baton->blurRadius = options->Get(NanNew<String>("blurRadius"))->Int32Value();
|
||||||
baton->sharpenRadius = options->Get(NanNew<String>("sharpenRadius"))->Int32Value();
|
baton->sharpenRadius = options->Get(NanNew<String>("sharpenRadius"))->Int32Value();
|
||||||
baton->sharpenFlat = options->Get(NanNew<String>("sharpenFlat"))->NumberValue();
|
baton->sharpenFlat = options->Get(NanNew<String>("sharpenFlat"))->NumberValue();
|
||||||
baton->sharpenJagged = options->Get(NanNew<String>("sharpenJagged"))->NumberValue();
|
baton->sharpenJagged = options->Get(NanNew<String>("sharpenJagged"))->NumberValue();
|
||||||
|
@ -187,6 +187,30 @@ async.series({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}).add('sharp-blur-mild', {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp(inputJpgBuffer).resize(width, height).blur().toBuffer(function(err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).add('sharp-blur-radius', {
|
||||||
|
defer: true,
|
||||||
|
fn: function(deferred) {
|
||||||
|
sharp(inputJpgBuffer).resize(width, height).blur(3).toBuffer(function(err, buffer) {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
} else {
|
||||||
|
assert.notStrictEqual(null, buffer);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}).add('sharp-nearest-neighbour', {
|
}).add('sharp-nearest-neighbour', {
|
||||||
defer: true,
|
defer: true,
|
||||||
fn: function(deferred) {
|
fn: function(deferred) {
|
||||||
|
72
test/unit/blur.js
Executable file
72
test/unit/blur.js
Executable file
@ -0,0 +1,72 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
|
var sharp = require('../../index');
|
||||||
|
var fixtures = require('../fixtures');
|
||||||
|
|
||||||
|
describe('Blur', function() {
|
||||||
|
|
||||||
|
it('specific radius', function(done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.blur(1)
|
||||||
|
.toFile(fixtures.path('output.blur-1.jpg'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('mild blur', function(done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.blur()
|
||||||
|
.toFile(fixtures.path('output.blur-mild.jpg'), function(err, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid radius', function(done) {
|
||||||
|
var isValid = true;
|
||||||
|
try {
|
||||||
|
sharp(fixtures.inputJpg).blur(1.5);
|
||||||
|
} catch (err) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
assert.strictEqual(false, isValid);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('blurred image is smaller than non-blurred', function(done) {
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.blur(false)
|
||||||
|
.toBuffer(function(err, notBlurred, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, notBlurred.length > 0);
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
|
.blur(true)
|
||||||
|
.toBuffer(function(err, blurred, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual(true, blurred.length > 0);
|
||||||
|
assert.strictEqual(true, blurred.length < notBlurred.length);
|
||||||
|
assert.strictEqual('jpeg', info.format);
|
||||||
|
assert.strictEqual(320, info.width);
|
||||||
|
assert.strictEqual(240, info.height);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user