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:
parent
1ab981ef11
commit
7a968d5fed
@ -5,6 +5,9 @@
|
|||||||
- (pro) Add `video_meta` to the `/info` response.
|
- (pro) Add `video_meta` to the `/info` response.
|
||||||
- Add 1/2/4-bit BMP support.
|
- Add 1/2/4-bit BMP support.
|
||||||
|
|
||||||
|
### Change
|
||||||
|
- Optimized `crop`.
|
||||||
|
|
||||||
### Fix
|
### Fix
|
||||||
- Fix Datadog support.
|
- Fix Datadog support.
|
||||||
|
|
||||||
|
@ -38,3 +38,11 @@ func Scale(a int, scale float64) int {
|
|||||||
|
|
||||||
return Round(float64(a) * scale)
|
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
158
options/gravity_options.go
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
@ -23,11 +23,6 @@ const maxClientHintDPR = 8
|
|||||||
|
|
||||||
var errExpiredURL = errors.New("Expired URL")
|
var errExpiredURL = errors.New("Expired URL")
|
||||||
|
|
||||||
type GravityOptions struct {
|
|
||||||
Type GravityType
|
|
||||||
X, Y float64
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExtendOptions struct {
|
type ExtendOptions struct {
|
||||||
Enabled bool
|
Enabled bool
|
||||||
Gravity GravityOptions
|
Gravity GravityOptions
|
||||||
@ -211,11 +206,7 @@ func parseBoolOption(str string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isGravityOffcetValid(gravity GravityType, offset float64) bool {
|
func isGravityOffcetValid(gravity GravityType, offset float64) bool {
|
||||||
if gravity == GravityCenter {
|
return gravity != GravityFocusPoint || (offset >= 0 && offset <= 1)
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset >= 0 && (gravity != GravityFocusPoint || offset <= 1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseGravity(g *GravityOptions, args []string) error {
|
func parseGravity(g *GravityOptions, args []string) error {
|
||||||
|
@ -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 {
|
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 {
|
width, height := pctx.cropWidth, pctx.cropHeight
|
||||||
return err
|
|
||||||
|
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
|
// Crop image to the result size
|
||||||
resultWidth := imath.Scale(po.Width, po.Dpr)
|
resultWidth := imath.Scale(po.Width, po.Dpr)
|
||||||
resultHeight := imath.Scale(po.Height, po.Dpr)
|
resultHeight := imath.Scale(po.Height, po.Dpr)
|
||||||
|
@ -161,16 +161,5 @@ func prepare(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptio
|
|||||||
|
|
||||||
pctx.wscale, pctx.hscale = calcScale(widthToScale, heightToScale, po, pctx.imgtype)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,10 @@ var mainPipeline = pipeline{
|
|||||||
prepare,
|
prepare,
|
||||||
scaleOnLoad,
|
scaleOnLoad,
|
||||||
importColorProfile,
|
importColorProfile,
|
||||||
|
crop,
|
||||||
scale,
|
scale,
|
||||||
rotateAndFlip,
|
rotateAndFlip,
|
||||||
crop,
|
cropToResult,
|
||||||
fixWebpSize,
|
fixWebpSize,
|
||||||
applyFilters,
|
applyFilters,
|
||||||
extend,
|
extend,
|
||||||
|
@ -58,15 +58,29 @@ func scaleOnLoad(pctx *pipelineContext, img *vips.Image, po *options.ProcessingO
|
|||||||
// Update scales after scale-on-load
|
// Update scales after scale-on-load
|
||||||
newWidth, newHeight, _, _ := extractMeta(img, po.Rotate, po.AutoRotate)
|
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) {
|
if newWidth == imath.Scale(newWidth, pctx.wscale) {
|
||||||
pctx.wscale = 1.0
|
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) {
|
if newHeight == imath.Scale(newHeight, pctx.hscale) {
|
||||||
pctx.hscale = 1.0
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user