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".
|
||||
|
||||
#### 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])
|
||||
|
||||
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.
|
||||
* `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
|
||||
background: [0, 0, 0, 255],
|
||||
flatten: false,
|
||||
blurRadius: 0,
|
||||
sharpenRadius: 0,
|
||||
sharpenFlat: 1,
|
||||
sharpenJagged: 2,
|
||||
@ -207,6 +208,27 @@ Sharp.prototype.withoutEnlargement = function(withoutEnlargement) {
|
||||
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.
|
||||
Call without a radius to use a fast, mild sharpen.
|
||||
|
@ -50,6 +50,7 @@ struct ResizeBaton {
|
||||
std::string interpolator;
|
||||
double background[4];
|
||||
bool flatten;
|
||||
int blurRadius;
|
||||
int sharpenRadius;
|
||||
double sharpenFlat;
|
||||
double sharpenJagged;
|
||||
@ -76,6 +77,7 @@ struct ResizeBaton {
|
||||
canvas(CROP),
|
||||
gravity(0),
|
||||
flatten(false),
|
||||
blurRadius(0),
|
||||
sharpenRadius(0),
|
||||
sharpenFlat(1.0),
|
||||
sharpenJagged(2.0),
|
||||
@ -510,6 +512,31 @@ class ResizeWorker : public NanAsyncWorker {
|
||||
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
|
||||
if (baton->sharpenRadius != 0) {
|
||||
VipsImage *sharpened = vips_image_new();
|
||||
@ -850,6 +877,7 @@ NAN_METHOD(resize) {
|
||||
baton->interpolator = *String::Utf8Value(options->Get(NanNew<String>("interpolator"))->ToString());
|
||||
// Operators
|
||||
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->sharpenFlat = options->Get(NanNew<String>("sharpenFlat"))->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', {
|
||||
defer: true,
|
||||
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