From 4bf1a27abd7cea9f804911b33e9bde035cb91669 Mon Sep 17 00:00:00 2001 From: DarthSim Date: Sun, 19 Feb 2023 18:58:30 +0300 Subject: [PATCH] Add extend_aspect_ratio processing option --- CHANGELOG.md | 2 ++ docs/generating_the_url.md | 13 +++++++++++ options/processing_options.go | 44 ++++++++++++++++++++++------------- processing/crop.go | 15 +++++++----- processing/extend.go | 38 ++++++++++++++++++++++++++---- processing/processing.go | 1 + 6 files changed, 86 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f1b5332..8c54c121 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] +## Add +- Add [extend_aspect_ratio](https://docs.imgproxy.net/latest/generating_the_url?id=extend-aspect-ratio) processing option. ## [3.13.2] - 2023-02-15 ### Change diff --git a/docs/generating_the_url.md b/docs/generating_the_url.md index 7e232fae..5e051b56 100644 --- a/docs/generating_the_url.md +++ b/docs/generating_the_url.md @@ -174,6 +174,19 @@ ex:%extend:%gravity Default: `false:ce:0:0` +### Extend aspect ratio + +``` +extend_aspect_ratio:%extend:%gravity +extend_ar:%extend:%gravity +exar:%extend:%gravity +``` + +* When `extend` is set to `1`, `t` or `true`, imgproxy will extend the image to the requested aspect ratio. +* `gravity` _(optional)_ accepts the same values as the [gravity](#gravity) option, except `sm`. When `gravity` is not set, imgproxy will use `ce` gravity without offsets. + +Default: `false:ce:0:0` + ### Gravity ``` diff --git a/options/processing_options.go b/options/processing_options.go index 0bc76da4..d8faa1e9 100644 --- a/options/processing_options.go +++ b/options/processing_options.go @@ -70,6 +70,7 @@ type ProcessingOptions struct { Gravity GravityOptions Enlarge bool Extend ExtendOptions + ExtendAspectRatio ExtendOptions Crop CropOptions Padding PaddingOptions Trim TrimOptions @@ -120,6 +121,7 @@ func NewProcessingOptions() *ProcessingOptions { Gravity: GravityOptions{Type: GravityCenter}, Enlarge: false, Extend: ExtendOptions{Enabled: false, Gravity: GravityOptions{Type: GravityCenter}}, + ExtendAspectRatio: ExtendOptions{Enabled: false, Gravity: GravityOptions{Type: GravityCenter}}, Padding: PaddingOptions{Enabled: false}, Trim: TrimOptions{Enabled: false, Threshold: 10, Smart: true}, Rotate: 0, @@ -250,6 +252,26 @@ func parseGravity(g *GravityOptions, args []string) error { return nil } +func parseExtend(opts *ExtendOptions, name string, args []string) error { + if len(args) > 4 { + return fmt.Errorf("Invalid %s arguments: %v", name, args) + } + + opts.Enabled = parseBoolOption(args[0]) + + if len(args) > 1 { + if err := parseGravity(&opts.Gravity, args[1:]); err != nil { + return err + } + + if opts.Gravity.Type == GravitySmart { + return fmt.Errorf("%s doesn't support smart gravity", name) + } + } + + return nil +} + func applyWidthOption(po *ProcessingOptions, args []string) error { if len(args) > 1 { return fmt.Errorf("Invalid width arguments: %v", args) @@ -293,23 +315,11 @@ func applyEnlargeOption(po *ProcessingOptions, args []string) error { } func applyExtendOption(po *ProcessingOptions, args []string) error { - if len(args) > 4 { - return fmt.Errorf("Invalid extend arguments: %v", args) - } + return parseExtend(&po.Extend, "extend", args) +} - po.Extend.Enabled = parseBoolOption(args[0]) - - if len(args) > 1 { - if err := parseGravity(&po.Extend.Gravity, args[1:]); err != nil { - return err - } - - if po.Extend.Gravity.Type == GravitySmart { - return errors.New("extend doesn't support smart gravity") - } - } - - return nil +func applyExtendAspectRatioOption(po *ProcessingOptions, args []string) error { + return parseExtend(&po.ExtendAspectRatio, "extend_aspect_ratio", args) } func applySizeOption(po *ProcessingOptions, args []string) (err error) { @@ -898,6 +908,8 @@ func applyURLOption(po *ProcessingOptions, name string, args []string) error { return applyEnlargeOption(po, args) case "extend", "ex": return applyExtendOption(po, args) + case "extend_aspect_ratio", "extend_ar", "exar": + return applyExtendAspectRatioOption(po, args) case "gravity", "g": return applyGravityOption(po, args) case "crop", "c": diff --git a/processing/crop.go b/processing/crop.go index f983f567..41e9f750 100644 --- a/processing/crop.go +++ b/processing/crop.go @@ -56,13 +56,16 @@ func cropToResult(pctx *pipelineContext, img *vips.Image, po *options.Processing resultWidth, resultHeight := resultSize(po) if po.ResizingType == options.ResizeFillDown { - if resultWidth > img.Width() { - resultHeight = imath.Scale(resultHeight, float64(img.Width())/float64(resultWidth)) - resultWidth = img.Width() - } + diffW := float64(resultWidth) / float64(img.Width()) + diffH := float64(resultHeight) / float64(img.Height()) - if resultHeight > img.Height() { - resultWidth = imath.Scale(resultWidth, float64(img.Height())/float64(resultHeight)) + switch { + case diffW > diffH && diffW > 1.0: + resultHeight = imath.Scale(img.Width(), float64(resultHeight)/float64(resultWidth)) + resultWidth = img.Width() + + case diffH > diffW && diffH > 1.0: + resultWidth = imath.Scale(img.Height(), float64(resultWidth)/float64(resultHeight)) resultHeight = img.Height() } } diff --git a/processing/extend.go b/processing/extend.go index d357515d..b19add48 100644 --- a/processing/extend.go +++ b/processing/extend.go @@ -2,17 +2,45 @@ package processing import ( "github.com/imgproxy/imgproxy/v3/imagedata" + "github.com/imgproxy/imgproxy/v3/imath" "github.com/imgproxy/imgproxy/v3/options" "github.com/imgproxy/imgproxy/v3/vips" ) -func extend(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error { - resultWidth, resultHeight := resultSize(po) - - if !po.Extend.Enabled || (resultWidth <= img.Width() && resultHeight <= img.Height()) { +func extendImage(img *vips.Image, resultWidth, resultHeight int, opts *options.ExtendOptions, extendAr bool) error { + if !opts.Enabled || (resultWidth <= img.Width() && resultHeight <= img.Height()) { return nil } - offX, offY := calcPosition(resultWidth, resultHeight, img.Width(), img.Height(), &po.Extend.Gravity, false) + if extendAr && resultWidth > img.Width() && resultHeight > img.Height() { + diffW := float64(resultWidth) / float64(img.Width()) + diffH := float64(resultHeight) / float64(img.Height()) + + switch { + case diffH > diffW: + resultHeight = imath.Scale(img.Width(), float64(resultHeight)/float64(resultWidth)) + resultWidth = img.Width() + + case diffW > diffH: + resultWidth = imath.Scale(img.Height(), float64(resultWidth)/float64(resultHeight)) + resultHeight = img.Height() + + default: + // The image has the requested arpect ratio + return nil + } + } + + offX, offY := calcPosition(resultWidth, resultHeight, img.Width(), img.Height(), &opts.Gravity, false) return img.Embed(resultWidth, resultHeight, offX, offY) } + +func extend(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error { + resultWidth, resultHeight := resultSize(po) + return extendImage(img, resultWidth, resultHeight, &po.Extend, false) +} + +func extendAspectRatio(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error { + resultWidth, resultHeight := resultSize(po) + return extendImage(img, resultWidth, resultHeight, &po.ExtendAspectRatio, true) +} diff --git a/processing/processing.go b/processing/processing.go index a05cca57..a4b81af9 100644 --- a/processing/processing.go +++ b/processing/processing.go @@ -30,6 +30,7 @@ var mainPipeline = pipeline{ cropToResult, applyFilters, extend, + extendAspectRatio, padding, fixSize, flatten,