diff --git a/CHANGELOG.md b/CHANGELOG.md index 38427c7b..9a90a965 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - [expires](https://docs.imgproxy.net/#/generating_the_url?id=expires) processing option. - [skip processing](https://docs.imgproxy.net/#/generating_the_url?id=skip-processing) processing option. - [Datadog](./docs/datadog.md) metrics. +- `force` resizing type. ### Removed - Removed basic URL format, use [advanced one](./docs/generating_the_url.md) instead. diff --git a/docs/generating_the_url.md b/docs/generating_the_url.md index 9d3d2328..009e1285 100644 --- a/docs/generating_the_url.md +++ b/docs/generating_the_url.md @@ -56,6 +56,7 @@ Defines how imgproxy will resize the source image. Supported resizing types are: * `fit`: resizes the image while keeping aspect ratio to fit given size; * `fill`: resizes the image while keeping aspect ratio to fill given size and cropping projecting parts; +* `force`: resizes the image without keeping aspect ratio; * `auto`: if both source and resulting dimensions have the same orientation (portrait or landscape), imgproxy will use `fill`. Otherwise, it will use `fit`. Default: `fit` diff --git a/process.go b/process.go index d96b1119..3a64aefc 100644 --- a/process.go +++ b/process.go @@ -70,8 +70,8 @@ func extractMeta(img *vipsImage, baseAngle int, useOrientation bool) (int, int, return width, height, angle, flip } -func calcScale(width, height int, po *processingOptions, imgtype imageType) float64 { - var shrink float64 +func calcScale(width, height int, po *processingOptions, imgtype imageType) (float64, float64) { + var wshrink, hshrink float64 srcW, srcH := float64(width), float64(height) dstW, dstH := float64(po.Width), float64(po.Height) @@ -80,21 +80,28 @@ func calcScale(width, height int, po *processingOptions, imgtype imageType) floa dstW = srcW } + if dstW == srcW { + wshrink = 1 + } else { + wshrink = srcW / dstW + } + if po.Height == 0 { dstH = srcH } - if dstW == srcW && dstH == srcH { - shrink = 1 + if dstH == srcH { + hshrink = 1 } else { - wshrink := srcW / dstW - hshrink := srcH / dstH + hshrink = srcH / dstH + } + if wshrink != 1 || hshrink != 1 { rt := po.ResizingType if rt == resizeAuto { - srcD := width - height - dstD := po.Width - po.Height + srcD := srcW - srcH + dstD := dstW - dstH if (srcD >= 0 && dstD >= 0) || (srcD < 0 && dstD < 0) { rt = resizeFill @@ -105,31 +112,41 @@ func calcScale(width, height int, po *processingOptions, imgtype imageType) floa switch { case po.Width == 0: - shrink = hshrink + wshrink = hshrink case po.Height == 0: - shrink = wshrink + hshrink = wshrink case rt == resizeFit: - shrink = math.Max(wshrink, hshrink) - default: - shrink = math.Min(wshrink, hshrink) + wshrink = math.Max(wshrink, hshrink) + hshrink = wshrink + case rt == resizeFill: + wshrink = math.Min(wshrink, hshrink) + hshrink = wshrink } } - if !po.Enlarge && shrink < 1 && imgtype != imageTypeSVG { - shrink = 1 + if !po.Enlarge && imgtype != imageTypeSVG { + if wshrink < 1 { + hshrink /= wshrink + wshrink = 1 + } + if hshrink < 1 { + wshrink /= hshrink + hshrink = 1 + } } - shrink /= po.Dpr + wshrink /= po.Dpr + hshrink /= po.Dpr - if shrink > srcW { - shrink = srcW + if wshrink > srcW { + wshrink = srcW } - if shrink > srcH { - shrink = srcH + if hshrink > srcH { + hshrink = srcH } - return 1.0 / shrink + return 1.0 / wshrink, 1.0 / hshrink } func canScaleOnLoad(imgtype imageType, scale float64) bool { @@ -353,38 +370,42 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces widthToScale := minNonZeroInt(cropWidth, srcWidth) heightToScale := minNonZeroInt(cropHeight, srcHeight) - scale := calcScale(widthToScale, heightToScale, po, imgtype) + wscale, hscale := calcScale(widthToScale, heightToScale, po, imgtype) if cropWidth > 0 { - cropWidth = maxInt(1, scaleInt(cropWidth, scale)) + cropWidth = maxInt(1, scaleInt(cropWidth, wscale)) } if cropHeight > 0 { - cropHeight = maxInt(1, scaleInt(cropHeight, scale)) + cropHeight = maxInt(1, scaleInt(cropHeight, hscale)) } if cropGravity.Type != gravityFocusPoint { - cropGravity.X *= scale - cropGravity.Y *= scale + cropGravity.X *= wscale + cropGravity.Y *= hscale } - if !trimmed && scale != 1 && data != nil && canScaleOnLoad(imgtype, scale) { - jpegShrink := calcJpegShink(scale, imgtype) + prescale := math.Max(wscale, hscale) + + if !trimmed && prescale != 1 && data != nil && canScaleOnLoad(imgtype, prescale) { + jpegShrink := calcJpegShink(prescale, imgtype) if imgtype != imageTypeJPEG || jpegShrink != 1 { // Do some scale-on-load - if err = img.Load(data, imgtype, jpegShrink, scale, 1); err != nil { + if err = img.Load(data, imgtype, jpegShrink, prescale, 1); err != nil { return err } } - // Update scale after scale-on-load + // Update scales after scale-on-load newWidth, newHeight, _, _ := extractMeta(img, po.Rotate, po.AutoRotate) - if srcWidth > srcHeight { - scale = float64(srcWidth) * scale / float64(newWidth) - } else { - scale = float64(srcHeight) * scale / float64(newHeight) + + wscale = float64(srcWidth) * wscale / float64(newWidth) + if srcWidth == scaleInt(srcWidth, wscale) { + wscale = 1.0 } - if srcWidth == scaleInt(srcWidth, scale) && srcHeight == scaleInt(srcHeight, scale) { - scale = 1.0 + + hscale = float64(srcHeight) * hscale / float64(newHeight) + if srcHeight == scaleInt(srcHeight, hscale) { + hscale = 1.0 } } @@ -393,7 +414,7 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces } iccImported := false - convertToLinear := conf.UseLinearColorspace && scale != 1 + convertToLinear := conf.UseLinearColorspace && (wscale != 1 || hscale != 1) if convertToLinear || !img.IsSRGB() { if err = img.ImportColourProfile(); err != nil { @@ -414,8 +435,8 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces hasAlpha := img.HasAlpha() - if scale != 1 { - if err = img.Resize(scale, hasAlpha); err != nil { + if wscale != 1 || hscale != 1 { + if err = img.Resize(wscale, hscale, hasAlpha); err != nil { return err } } @@ -452,7 +473,8 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces webpLimitShrink := float64(maxInt(img.Width(), img.Height())) / webpMaxDimension if webpLimitShrink > 1.0 { - if err = img.Resize(1.0/webpLimitShrink, hasAlpha); err != nil { + scale := 1.0 / webpLimitShrink + if err = img.Resize(scale, scale, hasAlpha); err != nil { return err } logWarning("WebP dimension size is limited to %d. The image is rescaled to %dx%d", int(webpMaxDimension), img.Width(), img.Height()) diff --git a/processing_options.go b/processing_options.go index f2753674..f1bb2900 100644 --- a/processing_options.go +++ b/processing_options.go @@ -65,13 +65,15 @@ type resizeType int const ( resizeFit resizeType = iota resizeFill + resizeForce resizeAuto ) var resizeTypes = map[string]resizeType{ - "fit": resizeFit, - "fill": resizeFill, - "auto": resizeAuto, + "fit": resizeFit, + "fill": resizeFill, + "force": resizeForce, + "auto": resizeAuto, } type rgbColor struct{ R, G, B uint8 } diff --git a/vips.c b/vips.c index df80766a..99c428b2 100644 --- a/vips.c +++ b/vips.c @@ -211,12 +211,12 @@ vips_rad2float_go(VipsImage *in, VipsImage **out) { } int -vips_resize_go(VipsImage *in, VipsImage **out, double scale) { - return vips_resize(in, out, scale, NULL); +vips_resize_go(VipsImage *in, VipsImage **out, double wscale, double hscale) { + return vips_resize(in, out, wscale, "vscale", hscale, NULL); } int -vips_resize_with_premultiply(VipsImage *in, VipsImage **out, double scale) { +vips_resize_with_premultiply(VipsImage *in, VipsImage **out, double wscale, double hscale) { VipsBandFormat format; VipsImage *tmp1, *tmp2; @@ -225,7 +225,7 @@ vips_resize_with_premultiply(VipsImage *in, VipsImage **out, double scale) { if (vips_premultiply(in, &tmp1, NULL)) return 1; - if (vips_resize(tmp1, &tmp2, scale, NULL)) { + if (vips_resize(tmp1, &tmp2, wscale, "vscale", hscale, NULL)) { clear_image(&tmp1); return 1; } diff --git a/vips.go b/vips.go index 6afecf0a..b814834d 100644 --- a/vips.go +++ b/vips.go @@ -403,15 +403,15 @@ func (img *vipsImage) Rad2Float() error { return nil } -func (img *vipsImage) Resize(scale float64, hasAlpa bool) error { +func (img *vipsImage) Resize(wscale, hscale float64, hasAlpa bool) error { var tmp *C.VipsImage if hasAlpa { - if C.vips_resize_with_premultiply(img.VipsImage, &tmp, C.double(scale)) != 0 { + if C.vips_resize_with_premultiply(img.VipsImage, &tmp, C.double(wscale), C.double(hscale)) != 0 { return vipsError() } } else { - if C.vips_resize_go(img.VipsImage, &tmp, C.double(scale)) != 0 { + if C.vips_resize_go(img.VipsImage, &tmp, C.double(wscale), C.double(hscale)) != 0 { return vipsError() } } diff --git a/vips.h b/vips.h index 18f69683..522508cf 100644 --- a/vips.h +++ b/vips.h @@ -55,8 +55,8 @@ int vips_copy_go(VipsImage *in, VipsImage **out); int vips_cast_go(VipsImage *in, VipsImage **out, VipsBandFormat format); int vips_rad2float_go(VipsImage *in, VipsImage **out); -int vips_resize_go(VipsImage *in, VipsImage **out, double scale); -int vips_resize_with_premultiply(VipsImage *in, VipsImage **out, double scale); +int vips_resize_go(VipsImage *in, VipsImage **out, double wscale, double hscale); +int vips_resize_with_premultiply(VipsImage *in, VipsImage **out, double wscale, double hscale); int vips_icc_is_srgb_iec61966(VipsImage *in); int vips_has_embedded_icc(VipsImage *in);