1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-01-03 10:43:58 +02:00

Crop offsets

This commit is contained in:
DarthSim 2019-06-20 18:49:25 +06:00
parent 4167ade55a
commit e894afec13
3 changed files with 88 additions and 45 deletions

View File

@ -120,25 +120,30 @@ Default: `0`
##### Gravity
```
gravity:%gravity
g:%gravity
gravity:%gravity_type:%x_offset:%y_offset
g:%gravity_type:%x_offset:%y_offset
```
When imgproxy needs to cut some parts of the image, it is guided by the gravity. The following values are supported:
When imgproxy needs to cut some parts of the image, it is guided by the gravity.
* `no`: north (top edge);
* `so`: south (bottom edge);
* `ea`: east (right edge);
* `we`: west (left edge);
* `noea`: north-east (top-right corner);
* `nowe`: north-west (top-left corner);
* `soea`: south-east (bottom-right corner);
* `sowe`: south-west (bottom-left corner);
* `ce`: center;
* `sm`: smart. `libvips` detects the most "interesting" section of the image and considers it as the center of the resulting image;
* `fp:%x:%y`: focus point. `x` and `y` are floating point numbers between 0 and 1 that define the coordinates of the center of the resulting image. Treat 0 and 1 as right/left for `x` and top/bottom for `y`.
* `gravity_type` - specifies the gravity type. Available values:
* `no`: north (top edge);
* `so`: south (bottom edge);
* `ea`: east (right edge);
* `we`: west (left edge);
* `noea`: north-east (top-right corner);
* `nowe`: north-west (top-left corner);
* `soea`: south-east (bottom-right corner);
* `sowe`: south-west (bottom-left corner);
* `ce`: center.
* `x_offset`, `y_offset` - (optional) specify gravity offset by X and Y axes.
Default: `ce`
Default: `ce:0:0`
###### Special gravities:
* `gravity:sm` - smart gravity. `libvips` detects the most "interesting" section of the image and considers it as the center of the resulting image. Offsets are not applicable here;
* `gravity:fp:%x:%y` - focus point gravity. `x` and `y` are floating point numbers between 0 and 1 that define the coordinates of the center of the resulting image. Treat 0 and 1 as right/left for `x` and top/bottom for `y`.
##### Crop

View File

@ -115,25 +115,30 @@ func calcCrop(width, height, cropWidth, cropHeight int, gravity *gravityOptions)
return
}
left = (width - cropWidth + 1) / 2
top = (height - cropHeight + 1) / 2
offX, offY := int(gravity.X), int(gravity.Y)
left = (width-cropWidth+1)/2 + offX
top = (height-cropHeight+1)/2 + offY
if gravity.Type == gravityNorth || gravity.Type == gravityNorthEast || gravity.Type == gravityNorthWest {
top = 0
top = 0 + offY
}
if gravity.Type == gravityEast || gravity.Type == gravityNorthEast || gravity.Type == gravitySouthEast {
left = width - cropWidth
left = width - cropWidth - offX
}
if gravity.Type == gravitySouth || gravity.Type == gravitySouthEast || gravity.Type == gravitySouthWest {
top = height - cropHeight
top = height - cropHeight - offY
}
if gravity.Type == gravityWest || gravity.Type == gravityNorthWest || gravity.Type == gravitySouthWest {
left = 0
left = 0 + offX
}
left = maxInt(0, minInt(left, width-cropWidth))
top = maxInt(0, minInt(top, height-cropHeight))
return
}
@ -176,14 +181,28 @@ func cropImage(img *vipsImage, cropWidth, cropHeight int, gravity *gravityOption
return img.Crop(left, top, cropWidth, cropHeight)
}
func scaleSize(size int, scale float64) int {
if size == 0 {
return 0
}
return roundToInt(float64(size) * scale)
}
func transformImage(ctx context.Context, img *vipsImage, data []byte, po *processingOptions, imgtype imageType) error {
var err error
srcWidth, srcHeight, angle, flip := extractMeta(img)
widthToScale, heightToScale := srcWidth, srcHeight
cropWidth, cropHeight := po.Crop.Width, po.Crop.Height
cropGravity := po.Crop.Gravity
if cropGravity.Type == gravityUnknown {
cropGravity = po.Gravity
}
widthToScale, heightToScale := srcWidth, srcHeight
if cropWidth > 0 {
widthToScale = minInt(cropWidth, srcWidth)
}
@ -193,8 +212,10 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces
scale := calcScale(widthToScale, heightToScale, po, imgtype)
cropWidth = roundToInt(float64(cropWidth) * scale)
cropHeight = roundToInt(float64(cropHeight) * scale)
cropWidth = scaleSize(cropWidth, scale)
cropHeight = scaleSize(cropHeight, scale)
cropGravity.X = cropGravity.X * scale
cropGravity.Y = cropGravity.Y * scale
if scale != 1 && data != nil && canScaleOnLoad(imgtype, scale) {
if imgtype == imageTypeWEBP || imgtype == imageTypeSVG {
@ -214,8 +235,8 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces
// Update scale after scale-on-load
newWidth, newHeight, _, _ := extractMeta(img)
widthToScale = roundToInt(float64(widthToScale) * float64(newWidth) / float64(srcWidth))
heightToScale = roundToInt(float64(heightToScale) * float64(newHeight) / float64(srcHeight))
widthToScale = scaleSize(widthToScale, float64(newWidth)/float64(srcWidth))
heightToScale = scaleSize(heightToScale, float64(newHeight)/float64(srcHeight))
scale = calcScale(widthToScale, heightToScale, po, imgtype)
}
@ -269,11 +290,6 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces
dprWidth := roundToInt(float64(po.Width) * po.Dpr)
dprHeight := roundToInt(float64(po.Height) * po.Dpr)
cropGravity := po.Crop.Gravity
if cropGravity.Type == gravityUnknown {
cropGravity = po.Gravity
}
if cropGravity.Type == po.Gravity.Type && cropGravity.Type != gravityFocusPoint {
if cropWidth == 0 {
cropWidth = dprWidth
@ -287,7 +303,13 @@ func transformImage(ctx context.Context, img *vipsImage, data []byte, po *proces
cropHeight = minInt(cropHeight, dprHeight)
}
if err = cropImage(img, cropWidth, cropHeight, &cropGravity); err != nil {
sumGravity := gravityOptions{
Type: cropGravity.Type,
X: cropGravity.X + po.Gravity.X,
Y: cropGravity.Y + po.Gravity.Y,
}
if err = cropImage(img, cropWidth, cropHeight, &sumGravity); err != nil {
return err
}
} else {

View File

@ -52,11 +52,6 @@ var gravityTypes = map[string]gravityType{
"fp": gravityFocusPoint,
}
type gravityOptions struct {
Type gravityType
X, Y float64
}
type resizeType int
const (
@ -80,6 +75,11 @@ const (
hexColorShortFormat = "%1x%1x%1x"
)
type gravityOptions struct {
Type gravityType
X, Y float64
}
type cropOptions struct {
Width int
Height int
@ -258,31 +258,47 @@ func parseDimension(d *int, name, arg string) error {
return nil
}
func isGravityOffcetValid(gravity gravityType, offset float64) bool {
if gravity == gravityCenter {
return true
}
return offset >= 0 && (gravity != gravityFocusPoint || offset <= 1)
}
func parseGravity(g *gravityOptions, args []string) error {
nArgs := len(args)
if nArgs > 3 {
return fmt.Errorf("Invalid gravity arguments: %v", args)
}
if t, ok := gravityTypes[args[0]]; ok {
g.Type = t
} else {
return fmt.Errorf("Invalid gravity: %s", args[0])
}
if g.Type == gravityFocusPoint {
if len(args) != 3 {
return fmt.Errorf("Invalid gravity arguments: %v", args)
}
if g.Type == gravitySmart && nArgs > 1 {
return fmt.Errorf("Invalid gravity arguments: %v", args)
} else if g.Type == gravityFocusPoint && nArgs != 3 {
return fmt.Errorf("Invalid gravity arguments: %v", args)
}
if x, err := strconv.ParseFloat(args[1], 64); err == nil && x >= 0 && x <= 1 {
if nArgs > 1 {
if x, err := strconv.ParseFloat(args[1], 64); err == nil && isGravityOffcetValid(g.Type, x) {
g.X = x
} else {
return fmt.Errorf("Invalid gravity X: %s", args[1])
}
}
if y, err := strconv.ParseFloat(args[2], 64); err == nil && y >= 0 && y <= 1 {
if nArgs > 2 {
if y, err := strconv.ParseFloat(args[2], 64); err == nil && isGravityOffcetValid(g.Type, y) {
g.Y = y
} else {
return fmt.Errorf("Invalid gravity Y: %s", args[2])
}
} else if len(args) > 1 {
return fmt.Errorf("Invalid gravity arguments: %v", args)
}
return nil