mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Shrink less, affine more, maintain performance #75
Affects interpolators with 4x4+ window size e.g. Bicubic, LBB, Nohalo Introduces blur before large affine to improve large PNG reductions
This commit is contained in:
parent
7537adf399
commit
47927ef47d
@ -116,3 +116,15 @@ sharp_image_has_alpha(VipsImage *image) {
|
|||||||
(image->Bands == 5 && image->Type == VIPS_INTERPRETATION_CMYK)
|
(image->Bands == 5 && image->Type == VIPS_INTERPRETATION_CMYK)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns the window size for the named interpolator. For example,
|
||||||
|
a window size of 3 means a 3x3 pixel grid is used for the calculation.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sharp_interpolator_window_size(char const *name) {
|
||||||
|
VipsInterpolate *interpolator = vips_interpolate_new(name);
|
||||||
|
int window_size = vips_interpolate_get_window_size(interpolator);
|
||||||
|
g_object_unref(interpolator);
|
||||||
|
return window_size;
|
||||||
|
}
|
||||||
|
@ -43,4 +43,11 @@ sharp_init_image_from_file(VipsImage **image, char const *file, VipsAccess const
|
|||||||
bool
|
bool
|
||||||
sharp_image_has_alpha(VipsImage *image);
|
sharp_image_has_alpha(VipsImage *image);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns the window size for the named interpolator. For example,
|
||||||
|
a window size of 3 means a 3x3 pixel grid is used for the calculation.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sharp_interpolator_window_size(char const *name);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -159,6 +159,9 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
baton->flip = TRUE;
|
baton->flip = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get window size of interpolator, used for determining shrink vs affine
|
||||||
|
int interpolatorWindowSize = sharp_interpolator_window_size(baton->interpolator.c_str());
|
||||||
|
|
||||||
// Scaling calculations
|
// Scaling calculations
|
||||||
double factor;
|
double factor;
|
||||||
if (baton->width > 0 && baton->height > 0) {
|
if (baton->width > 0 && baton->height > 0) {
|
||||||
@ -188,10 +191,20 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
baton->width = inputWidth;
|
baton->width = inputWidth;
|
||||||
baton->height = inputHeight;
|
baton->height = inputHeight;
|
||||||
}
|
}
|
||||||
int shrink = floor(factor);
|
|
||||||
|
// Calculate integral box shrink
|
||||||
|
int shrink = 1;
|
||||||
|
if (factor >= 2 && interpolatorWindowSize > 3) {
|
||||||
|
// Shrink less, affine more with interpolators that use at least 4x4 pixel window, e.g. bicubic
|
||||||
|
shrink = floor(factor * 3.0 / interpolatorWindowSize);
|
||||||
|
} else {
|
||||||
|
shrink = floor(factor);
|
||||||
|
}
|
||||||
if (shrink < 1) {
|
if (shrink < 1) {
|
||||||
shrink = 1;
|
shrink = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate residual float affine transformation
|
||||||
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
|
// Do not enlarge the output if the input width *or* height are already less than the required dimensions
|
||||||
@ -207,7 +220,7 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
|
|
||||||
// Try to use libjpeg shrink-on-load, but not when applying gamma correction or pre-resize extract
|
// Try to use libjpeg shrink-on-load, but not when applying gamma correction or pre-resize extract
|
||||||
int shrink_on_load = 1;
|
int shrink_on_load = 1;
|
||||||
if (inputImageType == JPEG && baton->gamma == 0 && baton->topOffsetPre == -1) {
|
if (inputImageType == JPEG && shrink >= 2 && baton->gamma == 0 && baton->topOffsetPre == -1) {
|
||||||
if (shrink >= 8) {
|
if (shrink >= 8) {
|
||||||
factor = factor / 8;
|
factor = factor / 8;
|
||||||
shrink_on_load = 8;
|
shrink_on_load = 8;
|
||||||
@ -222,7 +235,11 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
if (shrink_on_load > 1) {
|
if (shrink_on_load > 1) {
|
||||||
// Recalculate integral shrink and double residual
|
// Recalculate integral shrink and double residual
|
||||||
factor = std::max(factor, 1.0);
|
factor = std::max(factor, 1.0);
|
||||||
shrink = floor(factor);
|
if (factor >= 2 && interpolatorWindowSize > 3) {
|
||||||
|
shrink = floor(factor * 3.0 / interpolatorWindowSize);
|
||||||
|
} else {
|
||||||
|
shrink = floor(factor);
|
||||||
|
}
|
||||||
residual = static_cast<double>(shrink) / factor;
|
residual = static_cast<double>(shrink) / factor;
|
||||||
// Reload input using shrink-on-load
|
// Reload input using shrink-on-load
|
||||||
g_object_unref(image);
|
g_object_unref(image);
|
||||||
@ -340,11 +357,21 @@ class ResizeWorker : public NanAsyncWorker {
|
|||||||
|
|
||||||
// Use vips_affine with the remaining float part
|
// Use vips_affine with the remaining float part
|
||||||
if (residual != 0) {
|
if (residual != 0) {
|
||||||
VipsImage *affined = vips_image_new();
|
// Apply variable blur radius of floor(residual) before large affine reductions
|
||||||
vips_object_local(hook, affined);
|
if (residual >= 1) {
|
||||||
|
VipsImage *blurred = vips_image_new();
|
||||||
|
vips_object_local(hook, blurred);
|
||||||
|
if (vips_gaussblur(image, &blurred, floor(residual), NULL)) {
|
||||||
|
return Error(baton, hook);
|
||||||
|
}
|
||||||
|
g_object_unref(image);
|
||||||
|
image = blurred;
|
||||||
|
}
|
||||||
// Create interpolator - "bilinear" (default), "bicubic" or "nohalo"
|
// Create interpolator - "bilinear" (default), "bicubic" or "nohalo"
|
||||||
VipsInterpolate *interpolator = vips_interpolate_new(baton->interpolator.c_str());
|
VipsInterpolate *interpolator = vips_interpolate_new(baton->interpolator.c_str());
|
||||||
// Perform affine transformation
|
// Perform affine transformation
|
||||||
|
VipsImage *affined = vips_image_new();
|
||||||
|
vips_object_local(hook, affined);
|
||||||
if (vips_affine(image, &affined, residual, 0, 0, residual, "interpolate", interpolator, NULL)) {
|
if (vips_affine(image, &affined, residual, 0, 0, residual, "interpolate", interpolator, NULL)) {
|
||||||
g_object_unref(interpolator);
|
g_object_unref(interpolator);
|
||||||
return Error(baton, hook);
|
return Error(baton, hook);
|
||||||
|
@ -17,7 +17,8 @@ describe('Sharpen', function() {
|
|||||||
assert.strictEqual('jpeg', info.format);
|
assert.strictEqual('jpeg', info.format);
|
||||||
assert.strictEqual(320, info.width);
|
assert.strictEqual(320, info.width);
|
||||||
assert.strictEqual(240, info.height);
|
assert.strictEqual(240, info.height);
|
||||||
sharp(notSharpened)
|
sharp(fixtures.inputJpg)
|
||||||
|
.resize(320, 240)
|
||||||
.sharpen()
|
.sharpen()
|
||||||
.toBuffer(function(err, sharpened, info) {
|
.toBuffer(function(err, sharpened, info) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user