1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-01-18 11:12:10 +02:00

Crop between scale-on-load and scale

This commit is contained in:
DarthSim 2022-01-17 18:39:59 +06:00
parent 1ab981ef11
commit 7a968d5fed
9 changed files with 200 additions and 78 deletions

View File

@ -5,6 +5,9 @@
- (pro) Add `video_meta` to the `/info` response.
- Add 1/2/4-bit BMP support.
### Change
- Optimized `crop`.
### Fix
- Fix Datadog support.

View File

@ -38,3 +38,11 @@ func Scale(a int, scale float64) int {
return Round(float64(a) * scale)
}
func Shrink(a int, shrink float64) int {
if a == 0 {
return 0
}
return Round(float64(a) / shrink)
}

158
options/gravity_options.go Normal file
View File

@ -0,0 +1,158 @@
package options
import (
"fmt"
)
type GravityType int
const (
GravityUnknown GravityType = iota
GravityCenter
GravityNorth
GravityEast
GravitySouth
GravityWest
GravityNorthWest
GravityNorthEast
GravitySouthWest
GravitySouthEast
GravitySmart
GravityFocusPoint
)
var gravityTypes = map[string]GravityType{
"ce": GravityCenter,
"no": GravityNorth,
"ea": GravityEast,
"so": GravitySouth,
"we": GravityWest,
"nowe": GravityNorthWest,
"noea": GravityNorthEast,
"sowe": GravitySouthWest,
"soea": GravitySouthEast,
"sm": GravitySmart,
"fp": GravityFocusPoint,
}
var gravityTypesRotationMap = map[int]map[GravityType]GravityType{
90: {
GravityNorth: GravityWest,
GravityEast: GravityNorth,
GravitySouth: GravityEast,
GravityWest: GravitySouth,
GravityNorthWest: GravitySouthWest,
GravityNorthEast: GravityNorthWest,
GravitySouthWest: GravitySouthEast,
GravitySouthEast: GravityNorthEast,
},
180: {
GravityNorth: GravitySouth,
GravityEast: GravityWest,
GravitySouth: GravityNorth,
GravityWest: GravityEast,
GravityNorthWest: GravitySouthEast,
GravityNorthEast: GravitySouthWest,
GravitySouthWest: GravityNorthEast,
GravitySouthEast: GravityNorthWest,
},
270: {
GravityNorth: GravityEast,
GravityEast: GravitySouth,
GravitySouth: GravityWest,
GravityWest: GravityNorth,
GravityNorthWest: GravityNorthEast,
GravityNorthEast: GravitySouthEast,
GravitySouthWest: GravityNorthWest,
GravitySouthEast: GravitySouthWest,
},
}
var gravityTypesFlipMap = map[GravityType]GravityType{
GravityEast: GravityWest,
GravityWest: GravityEast,
GravityNorthWest: GravityNorthEast,
GravityNorthEast: GravityNorthWest,
GravitySouthWest: GravitySouthEast,
GravitySouthEast: GravitySouthWest,
}
func (gt GravityType) String() string {
for k, v := range gravityTypes {
if v == gt {
return k
}
}
return ""
}
func (gt GravityType) MarshalJSON() ([]byte, error) {
for k, v := range gravityTypes {
if v == gt {
return []byte(fmt.Sprintf("%q", k)), nil
}
}
return []byte("null"), nil
}
type GravityOptions struct {
Type GravityType
X, Y float64
}
func (g *GravityOptions) RotateAndFlip(angle int, flip bool) {
angle %= 360
if flip {
if gt, ok := gravityTypesFlipMap[g.Type]; ok {
g.Type = gt
}
switch g.Type {
case GravityCenter, GravityNorth, GravitySouth:
g.X = -g.X
case GravityFocusPoint:
g.X = 1.0 - g.X
}
}
if angle > 0 {
if rotMap := gravityTypesRotationMap[angle]; rotMap != nil {
if gt, ok := rotMap[g.Type]; ok {
g.Type = gt
}
switch angle {
case 90:
switch g.Type {
case GravityCenter, GravityEast, GravityWest:
g.X, g.Y = g.Y, -g.X
case GravityFocusPoint:
g.X, g.Y = g.Y, 1.0-g.X
default:
g.X, g.Y = g.Y, g.X
}
case 180:
switch g.Type {
case GravityCenter:
g.X, g.Y = -g.X, -g.Y
case GravityNorth, GravitySouth:
g.X = -g.X
case GravityEast, GravityWest:
g.Y = -g.Y
case GravityFocusPoint:
g.X, g.Y = 1.0-g.X, 1.0-g.Y
}
case 270:
switch g.Type {
case GravityCenter, GravityNorth, GravitySouth:
g.X, g.Y = -g.Y, g.X
case GravityFocusPoint:
g.X, g.Y = 1.0-g.Y, g.X
default:
g.X, g.Y = g.Y, g.X
}
}
}
}
}

View File

@ -1,52 +0,0 @@
package options
import "fmt"
type GravityType int
const (
GravityUnknown GravityType = iota
GravityCenter
GravityNorth
GravityEast
GravitySouth
GravityWest
GravityNorthWest
GravityNorthEast
GravitySouthWest
GravitySouthEast
GravitySmart
GravityFocusPoint
)
var gravityTypes = map[string]GravityType{
"ce": GravityCenter,
"no": GravityNorth,
"ea": GravityEast,
"so": GravitySouth,
"we": GravityWest,
"nowe": GravityNorthWest,
"noea": GravityNorthEast,
"sowe": GravitySouthWest,
"soea": GravitySouthEast,
"sm": GravitySmart,
"fp": GravityFocusPoint,
}
func (gt GravityType) String() string {
for k, v := range gravityTypes {
if v == gt {
return k
}
}
return ""
}
func (gt GravityType) MarshalJSON() ([]byte, error) {
for k, v := range gravityTypes {
if v == gt {
return []byte(fmt.Sprintf("%q", k)), nil
}
}
return []byte("null"), nil
}

View File

@ -23,11 +23,6 @@ const maxClientHintDPR = 8
var errExpiredURL = errors.New("Expired URL")
type GravityOptions struct {
Type GravityType
X, Y float64
}
type ExtendOptions struct {
Enabled bool
Gravity GravityOptions
@ -211,11 +206,7 @@ func parseBoolOption(str string) bool {
}
func isGravityOffcetValid(gravity GravityType, offset float64) bool {
if gravity == GravityCenter {
return true
}
return offset >= 0 && (gravity != GravityFocusPoint || offset <= 1)
return gravity != GravityFocusPoint || (offset >= 0 && offset <= 1)
}
func parseGravity(g *GravityOptions, args []string) error {

View File

@ -38,10 +38,20 @@ func cropImage(img *vips.Image, cropWidth, cropHeight int, gravity *options.Grav
}
func crop(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error {
if err := cropImage(img, pctx.cropWidth, pctx.cropHeight, &pctx.cropGravity); err != nil {
return err
width, height := pctx.cropWidth, pctx.cropHeight
opts := pctx.cropGravity
opts.RotateAndFlip(pctx.angle, pctx.flip)
opts.RotateAndFlip(po.Rotate, false)
if (pctx.angle+po.Rotate)%180 == 90 {
width, height = height, width
}
return cropImage(img, width, height, &opts)
}
func cropToResult(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error {
// Crop image to the result size
resultWidth := imath.Scale(po.Width, po.Dpr)
resultHeight := imath.Scale(po.Height, po.Dpr)

View File

@ -161,16 +161,5 @@ func prepare(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptio
pctx.wscale, pctx.hscale = calcScale(widthToScale, heightToScale, po, pctx.imgtype)
if pctx.cropWidth > 0 {
pctx.cropWidth = imath.Max(1, imath.Scale(pctx.cropWidth, pctx.wscale))
}
if pctx.cropHeight > 0 {
pctx.cropHeight = imath.Max(1, imath.Scale(pctx.cropHeight, pctx.hscale))
}
if pctx.cropGravity.Type != options.GravityFocusPoint {
pctx.cropGravity.X *= pctx.wscale
pctx.cropGravity.Y *= pctx.hscale
}
return nil
}

View File

@ -23,9 +23,10 @@ var mainPipeline = pipeline{
prepare,
scaleOnLoad,
importColorProfile,
crop,
scale,
rotateAndFlip,
crop,
cropToResult,
fixWebpSize,
applyFilters,
extend,

View File

@ -58,15 +58,29 @@ func scaleOnLoad(pctx *pipelineContext, img *vips.Image, po *options.ProcessingO
// Update scales after scale-on-load
newWidth, newHeight, _, _ := extractMeta(img, po.Rotate, po.AutoRotate)
pctx.wscale = float64(pctx.srcWidth) * pctx.wscale / float64(newWidth)
wpreshrink := float64(pctx.srcWidth) / float64(newWidth)
hpreshrink := float64(pctx.srcHeight) / float64(newHeight)
pctx.wscale = wpreshrink * pctx.wscale
if newWidth == imath.Scale(newWidth, pctx.wscale) {
pctx.wscale = 1.0
}
pctx.hscale = float64(pctx.srcHeight) * pctx.hscale / float64(newHeight)
pctx.hscale = hpreshrink * pctx.hscale
if newHeight == imath.Scale(newHeight, pctx.hscale) {
pctx.hscale = 1.0
}
if pctx.cropWidth > 0 {
pctx.cropWidth = imath.Max(1, imath.Shrink(pctx.cropWidth, wpreshrink))
}
if pctx.cropHeight > 0 {
pctx.cropHeight = imath.Max(1, imath.Shrink(pctx.cropHeight, hpreshrink))
}
if pctx.cropGravity.Type != options.GravityFocusPoint {
pctx.cropGravity.X /= wpreshrink
pctx.cropGravity.Y /= hpreshrink
}
return nil
}