Browse Source

Crop by percentage

DarthSim 4 years ago
parent
commit
9ffb0f514d
4 changed files with 36 additions and 11 deletions
  1. 1 0
      CHANGELOG.md
  2. 4 1
      docs/generating_the_url_advanced.md
  3. 21 4
      process.go
  4. 10 6
      processing_options.go

+ 1 - 0
CHANGELOG.md

@@ -8,6 +8,7 @@
 - `IMGPROXY_FORMAT_QUALITY` config.
 - `IMGPROXY_FORMAT_QUALITY` config.
 - `IMGPROXY_AUTO_ROTATE` config and [auto_rotate](https://docs.imgproxy.net/#/generating_the_url_advanced?id=auto-rotate) processing option.
 - `IMGPROXY_AUTO_ROTATE` config and [auto_rotate](https://docs.imgproxy.net/#/generating_the_url_advanced?id=auto-rotate) processing option.
 - [rotate](https://docs.imgproxy.net/#/generating_the_url_advanced?id=rotate) processing option.
 - [rotate](https://docs.imgproxy.net/#/generating_the_url_advanced?id=rotate) processing option.
+- `width` and `height` arguments of the [crop](https://docs.imgproxy.net/#/generating_the_url_advanced?id=crop) processing option can be less than `1` that is treated by imgproxy as a relative value (a.k.a. crop by percentage).
 - (pro) Remove Adobe Illustrator garbage from SVGs.
 - (pro) Remove Adobe Illustrator garbage from SVGs.
 
 
 ### Changed
 ### Changed

+ 4 - 1
docs/generating_the_url_advanced.md

@@ -167,7 +167,10 @@ c:%width:%height:%gravity
 
 
 Defines an area of the image to be processed (crop before resize).
 Defines an area of the image to be processed (crop before resize).
 
 
-* `width` and `height` define the size of the area. When `width` or `height` is set to `0`, imgproxy will use the full width/height of the source image.
+* `width` and `height` define the size of the area:
+  * When `width` or `height` is greater than or equal to `1`, imgproxy treats it as an absolute value.
+  * When `width` or `height` is less than `1`, imgproxy treats it as a relative value.
+  * When `width` or `height` is set to `0`, imgproxy will use the full width/height of the source image.
 * `gravity` _(optional)_ accepts the same values as [gravity](#gravity) option. When `gravity` is not set, imgproxy will use the value of the [gravity](#gravity) option.
 * `gravity` _(optional)_ accepts the same values as [gravity](#gravity) option. When `gravity` is not set, imgproxy will use the value of the [gravity](#gravity) option.
 
 
 #### Padding
 #### Padding

+ 21 - 4
process.go

@@ -163,6 +163,17 @@ func calcJpegShink(scale float64, imgtype imageType) int {
 	return 1
 	return 1
 }
 }
 
 
+func calcCropSize(orig int, crop float64) int {
+	switch {
+	case crop == 0.0:
+		return 0
+	case crop >= 1.0:
+		return int(crop)
+	default:
+		return maxInt(1, scaleInt(orig, crop))
+	}
+}
+
 func calcPosition(width, height, innerWidth, innerHeight int, gravity *gravityOptions, allowOverflow bool) (left, top int) {
 func calcPosition(width, height, innerWidth, innerHeight int, gravity *gravityOptions, allowOverflow bool) (left, top int) {
 	if gravity.Type == gravityFocusPoint {
 	if gravity.Type == gravityFocusPoint {
 		pointX := scaleInt(width, gravity.X)
 		pointX := scaleInt(width, gravity.X)
@@ -325,7 +336,9 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces
 	}
 	}
 
 
 	srcWidth, srcHeight, angle, flip := extractMeta(img, po.Rotate, po.AutoRotate)
 	srcWidth, srcHeight, angle, flip := extractMeta(img, po.Rotate, po.AutoRotate)
-	cropWidth, cropHeight := po.Crop.Width, po.Crop.Height
+
+	cropWidth := calcCropSize(srcWidth, po.Crop.Width)
+	cropHeight := calcCropSize(srcHeight, po.Crop.Height)
 
 
 	cropGravity := po.Crop.Gravity
 	cropGravity := po.Crop.Gravity
 	if cropGravity.Type == gravityUnknown {
 	if cropGravity.Type == gravityUnknown {
@@ -337,8 +350,12 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces
 
 
 	scale := calcScale(widthToScale, heightToScale, po, imgtype)
 	scale := calcScale(widthToScale, heightToScale, po, imgtype)
 
 
-	cropWidth = scaleInt(cropWidth, scale)
-	cropHeight = scaleInt(cropHeight, scale)
+	if cropWidth > 0 {
+		cropWidth = maxInt(1, scaleInt(cropWidth, scale))
+	}
+	if cropHeight > 0 {
+		cropHeight = maxInt(1, scaleInt(cropHeight, scale))
+	}
 	if cropGravity.Type != gravityFocusPoint {
 	if cropGravity.Type != gravityFocusPoint {
 		cropGravity.X *= scale
 		cropGravity.X *= scale
 		cropGravity.Y *= scale
 		cropGravity.Y *= scale
@@ -773,7 +790,7 @@ func processImage(ctx context.Context) ([]byte, context.CancelFunc, error) {
 	if po.ResizingType == resizeCrop {
 	if po.ResizingType == resizeCrop {
 		logWarning("`crop` resizing type is deprecated and will be removed in future versions. Use `crop` processing option instead")
 		logWarning("`crop` resizing type is deprecated and will be removed in future versions. Use `crop` processing option instead")
 
 
-		po.Crop.Width, po.Crop.Height = po.Width, po.Height
+		po.Crop.Width, po.Crop.Height = float64(po.Width), float64(po.Height)
 
 
 		po.ResizingType = resizeFit
 		po.ResizingType = resizeFit
 		po.Width, po.Height = 0, 0
 		po.Width, po.Height = 0, 0

+ 10 - 6
processing_options.go

@@ -95,8 +95,8 @@ type extendOptions struct {
 }
 }
 
 
 type cropOptions struct {
 type cropOptions struct {
-	Width   int
-	Height  int
+	Width   float64
+	Height  float64
 	Gravity gravityOptions
 	Gravity gravityOptions
 }
 }
 
 
@@ -571,13 +571,17 @@ func applyCropOption(po *processingOptions, args []string) error {
 		return fmt.Errorf("Invalid crop arguments: %v", args)
 		return fmt.Errorf("Invalid crop arguments: %v", args)
 	}
 	}
 
 
-	if err := parseDimension(&po.Crop.Width, "crop width", args[0]); err != nil {
-		return err
+	if w, err := strconv.ParseFloat(args[0], 64); err == nil && w >= 0 {
+		po.Crop.Width = w
+	} else {
+		return fmt.Errorf("Invalid crop width: %s", args[0])
 	}
 	}
 
 
 	if len(args) > 1 {
 	if len(args) > 1 {
-		if err := parseDimension(&po.Crop.Height, "crop height", args[1]); err != nil {
-			return err
+		if h, err := strconv.ParseFloat(args[1], 64); err == nil && h >= 0 {
+			po.Crop.Height = h
+		} else {
+			return fmt.Errorf("Invalid crop height: %s", args[1])
 		}
 		}
 	}
 	}