mirror of
https://github.com/lovell/sharp.git
synced 2025-12-19 07:15:08 +01:00
Switch to libvips' resize, make fastShrinkOnLoad optional (#977)
This commit is contained in:
committed by
Lovell Fuller
parent
ebc2a741f6
commit
d0f66c3734
118
src/pipeline.cc
118
src/pipeline.cc
@@ -222,20 +222,27 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
// If integral x and y shrink are equal, try to use shrink-on-load for JPEG and WebP,
|
||||
// but not when applying gamma correction, pre-resize extract or trim
|
||||
int shrink_on_load = 1;
|
||||
|
||||
int shrink_on_load_factor = 1;
|
||||
// Leave at least a factor of two for the final resize step, when fastShrinkOnLoad: false
|
||||
// for more consistent results and avoid occasional small image shifting
|
||||
if (!baton->fastShrinkOnLoad) {
|
||||
shrink_on_load_factor = 2;
|
||||
}
|
||||
if (
|
||||
xshrink == yshrink && xshrink >= 2 &&
|
||||
xshrink == yshrink && xshrink >= 2 * shrink_on_load_factor &&
|
||||
(inputImageType == ImageType::JPEG || inputImageType == ImageType::WEBP) &&
|
||||
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimTolerance == 0
|
||||
) {
|
||||
if (xshrink >= 8) {
|
||||
if (xshrink >= 8 * shrink_on_load_factor) {
|
||||
xfactor = xfactor / 8;
|
||||
yfactor = yfactor / 8;
|
||||
shrink_on_load = 8;
|
||||
} else if (xshrink >= 4) {
|
||||
} else if (xshrink >= 4 * shrink_on_load_factor) {
|
||||
xfactor = xfactor / 4;
|
||||
yfactor = yfactor / 4;
|
||||
shrink_on_load = 4;
|
||||
} else if (xshrink >= 2) {
|
||||
} else if (xshrink >= 2 * shrink_on_load_factor) {
|
||||
xfactor = xfactor / 2;
|
||||
yfactor = yfactor / 2;
|
||||
shrink_on_load = 2;
|
||||
@@ -282,23 +289,6 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
}
|
||||
xfactor = static_cast<double>(shrunkOnLoadWidth) / static_cast<double>(targetResizeWidth);
|
||||
yfactor = static_cast<double>(shrunkOnLoadHeight) / static_cast<double>(targetResizeHeight);
|
||||
xshrink = std::max(1, static_cast<int>(floor(xfactor)));
|
||||
yshrink = std::max(1, static_cast<int>(floor(yfactor)));
|
||||
xresidual = static_cast<double>(xshrink) / xfactor;
|
||||
yresidual = static_cast<double>(yshrink) / yfactor;
|
||||
if (
|
||||
!baton->rotateBeforePreExtract &&
|
||||
(rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270)
|
||||
) {
|
||||
std::swap(xresidual, yresidual);
|
||||
}
|
||||
}
|
||||
// Help ensure a final kernel-based reduction to prevent shrink aliasing
|
||||
if (xshrink > 1 && yshrink > 1 && (xresidual == 1.0 || yresidual == 1.0)) {
|
||||
xshrink = xshrink / 2;
|
||||
yshrink = yshrink / 2;
|
||||
xresidual = static_cast<double>(xshrink) / xfactor;
|
||||
yresidual = static_cast<double>(yshrink) / yfactor;
|
||||
}
|
||||
|
||||
// Ensure we're using a device-independent colour space
|
||||
@@ -364,13 +354,12 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
}
|
||||
}
|
||||
|
||||
bool const shouldShrink = xshrink > 1 || yshrink > 1;
|
||||
bool const shouldReduce = xresidual != 1.0 || yresidual != 1.0;
|
||||
bool const shouldResize = xfactor != 1.0 || yfactor != 1.0;
|
||||
bool const shouldBlur = baton->blurSigma != 0.0;
|
||||
bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0;
|
||||
bool const shouldSharpen = baton->sharpenSigma != 0.0;
|
||||
bool const shouldPremultiplyAlpha = HasAlpha(image) &&
|
||||
(shouldShrink || shouldReduce || shouldBlur || shouldConv || shouldSharpen || shouldOverlayWithAlpha);
|
||||
(shouldResize || shouldBlur || shouldConv || shouldSharpen || shouldOverlayWithAlpha);
|
||||
|
||||
// Premultiply image alpha channel before all transformations to avoid
|
||||
// dark fringing around bright pixels
|
||||
@@ -379,79 +368,21 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
image = image.premultiply();
|
||||
}
|
||||
|
||||
// Fast, integral box-shrink
|
||||
if (shouldShrink) {
|
||||
if (yshrink > 1) {
|
||||
image = image.shrinkv(yshrink);
|
||||
}
|
||||
if (xshrink > 1) {
|
||||
image = image.shrinkh(xshrink);
|
||||
}
|
||||
// Recalculate residual float based on dimensions of required vs shrunk images
|
||||
int shrunkWidth = image.width();
|
||||
int shrunkHeight = image.height();
|
||||
if (!baton->rotateBeforePreExtract &&
|
||||
(rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270)) {
|
||||
// Swap input output width and height when rotating by 90 or 270 degrees
|
||||
std::swap(shrunkWidth, shrunkHeight);
|
||||
}
|
||||
xresidual = static_cast<double>(targetResizeWidth) / static_cast<double>(shrunkWidth);
|
||||
yresidual = static_cast<double>(targetResizeHeight) / static_cast<double>(shrunkHeight);
|
||||
// Resize
|
||||
if (shouldResize) {
|
||||
VipsKernel kernel = static_cast<VipsKernel>(
|
||||
vips_enum_from_nick(nullptr, VIPS_TYPE_KERNEL, baton->kernel.data()));
|
||||
if (
|
||||
!baton->rotateBeforePreExtract &&
|
||||
(rotation == VIPS_ANGLE_D90 || rotation == VIPS_ANGLE_D270)
|
||||
kernel != VIPS_KERNEL_NEAREST && kernel != VIPS_KERNEL_CUBIC && kernel != VIPS_KERNEL_LANCZOS2 &&
|
||||
kernel != VIPS_KERNEL_LANCZOS3
|
||||
) {
|
||||
std::swap(xresidual, yresidual);
|
||||
throw vips::VError("Unknown kernel");
|
||||
}
|
||||
}
|
||||
|
||||
// Use affine increase or kernel reduce with the remaining float part
|
||||
if (xresidual != 1.0 || yresidual != 1.0) {
|
||||
// Insert tile cache to prevent over-computation of previous operations
|
||||
if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL) {
|
||||
image = sharp::TileCache(image, yresidual);
|
||||
}
|
||||
// Perform kernel-based reduction
|
||||
if (yresidual < 1.0 || xresidual < 1.0) {
|
||||
VipsKernel kernel = static_cast<VipsKernel>(
|
||||
vips_enum_from_nick(nullptr, VIPS_TYPE_KERNEL, baton->kernel.data()));
|
||||
if (
|
||||
kernel != VIPS_KERNEL_NEAREST && kernel != VIPS_KERNEL_CUBIC && kernel != VIPS_KERNEL_LANCZOS2 &&
|
||||
kernel != VIPS_KERNEL_LANCZOS3
|
||||
) {
|
||||
throw vips::VError("Unknown kernel");
|
||||
}
|
||||
if (yresidual < 1.0) {
|
||||
image = image.reducev(1.0 / yresidual, VImage::option()
|
||||
->set("kernel", kernel)
|
||||
->set("centre", baton->centreSampling));
|
||||
}
|
||||
if (xresidual < 1.0) {
|
||||
image = image.reduceh(1.0 / xresidual, VImage::option()
|
||||
->set("kernel", kernel)
|
||||
->set("centre", baton->centreSampling));
|
||||
}
|
||||
}
|
||||
// Perform enlargement
|
||||
if (yresidual > 1.0 || xresidual > 1.0) {
|
||||
if (trunc(xresidual) == xresidual && trunc(yresidual) == yresidual && baton->interpolator == "nearest") {
|
||||
// Fast, integral nearest neighbour enlargement
|
||||
image = image.zoom(static_cast<int>(xresidual), static_cast<int>(yresidual));
|
||||
} else {
|
||||
// Floating point affine transformation
|
||||
vips::VInterpolate interpolator = vips::VInterpolate::new_from_name(baton->interpolator.data());
|
||||
if (yresidual > 1.0 && xresidual > 1.0) {
|
||||
image = image.affine({xresidual, 0.0, 0.0, yresidual}, VImage::option()
|
||||
->set("interpolate", interpolator));
|
||||
} else if (yresidual > 1.0) {
|
||||
image = image.affine({1.0, 0.0, 0.0, yresidual}, VImage::option()
|
||||
->set("interpolate", interpolator));
|
||||
} else if (xresidual > 1.0) {
|
||||
image = image.affine({xresidual, 0.0, 0.0, 1.0}, VImage::option()
|
||||
->set("interpolate", interpolator));
|
||||
}
|
||||
}
|
||||
}
|
||||
image = image.resize(1.0 / xfactor, VImage::option()
|
||||
->set("vscale", 1.0 / yfactor)
|
||||
->set("kernel", kernel)
|
||||
->set("centre", baton->centreSampling));
|
||||
}
|
||||
|
||||
// Rotate
|
||||
@@ -1211,6 +1142,7 @@ NAN_METHOD(pipeline) {
|
||||
baton->kernel = AttrAsStr(options, "kernel");
|
||||
baton->interpolator = AttrAsStr(options, "interpolator");
|
||||
baton->centreSampling = AttrTo<bool>(options, "centreSampling");
|
||||
baton->fastShrinkOnLoad = AttrTo<bool>(options, "fastShrinkOnLoad");
|
||||
// Join Channel Options
|
||||
if (HasAttr(options, "joinChannelIn")) {
|
||||
v8::Local<v8::Object> joinChannelObject = Nan::Get(options, Nan::New("joinChannelIn").ToLocalChecked())
|
||||
|
||||
@@ -69,6 +69,7 @@ struct PipelineBaton {
|
||||
std::string kernel;
|
||||
std::string interpolator;
|
||||
bool centreSampling;
|
||||
bool fastShrinkOnLoad;
|
||||
double background[4];
|
||||
bool flatten;
|
||||
bool negate;
|
||||
|
||||
Reference in New Issue
Block a user