1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-11-23 22:34:44 +02:00

Calculate all sizes at the start of processing pipeline

This commit is contained in:
DarthSim
2025-06-05 20:19:20 +03:00
parent dccfe80349
commit 810cad5d26
7 changed files with 686 additions and 87 deletions

View File

@@ -48,23 +48,5 @@ func crop(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions,
} }
func cropToResult(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error { func cropToResult(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error {
// Crop image to the result size return cropImage(img, pctx.resultCropWidth, pctx.resultCropHeight, &po.Gravity, pctx.dprScale)
resultWidth, resultHeight := resultSize(po, pctx.dprScale)
if po.ResizingType == options.ResizeFillDown {
diffW := float64(resultWidth) / float64(img.Width())
diffH := float64(resultHeight) / float64(img.Height())
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()
}
}
return cropImage(img, resultWidth, resultHeight, &po.Gravity, pctx.dprScale)
} }

View File

@@ -2,61 +2,47 @@ package processing
import ( import (
"github.com/imgproxy/imgproxy/v3/imagedata" "github.com/imgproxy/imgproxy/v3/imagedata"
"github.com/imgproxy/imgproxy/v3/imath"
"github.com/imgproxy/imgproxy/v3/options" "github.com/imgproxy/imgproxy/v3/options"
"github.com/imgproxy/imgproxy/v3/vips" "github.com/imgproxy/imgproxy/v3/vips"
) )
func extendImage(img *vips.Image, resultWidth, resultHeight int, opts *options.ExtendOptions, offsetScale float64, extendAr bool) error { func extendImage(img *vips.Image, width, height int, gravity *options.GravityOptions, offsetScale float64) error {
imgWidth := img.Width() imgWidth := img.Width()
imgHeight := img.Height() imgHeight := img.Height()
if !opts.Enabled || (resultWidth <= imgWidth && resultHeight <= imgHeight) { if width <= imgWidth && height <= imgHeight {
return nil return nil
} }
if resultWidth <= 0 { if width <= 0 {
if extendAr { width = imgWidth
return nil
}
resultWidth = imgWidth
} }
if resultHeight <= 0 { if height <= 0 {
if extendAr { height = imgHeight
return nil
}
resultHeight = imgHeight
} }
if extendAr && resultWidth > imgWidth && resultHeight > imgHeight { offX, offY := calcPosition(width, height, imgWidth, imgHeight, gravity, offsetScale, false)
diffW := float64(resultWidth) / float64(imgWidth) return img.Embed(width, height, offX, offY)
diffH := float64(resultHeight) / float64(imgHeight)
switch {
case diffH > diffW:
resultHeight = imath.Scale(imgWidth, float64(resultHeight)/float64(resultWidth))
resultWidth = imgWidth
case diffW > diffH:
resultWidth = imath.Scale(imgHeight, float64(resultWidth)/float64(resultHeight))
resultHeight = imgHeight
default:
// The image has the requested arpect ratio
return nil
}
}
offX, offY := calcPosition(resultWidth, resultHeight, imgWidth, imgHeight, &opts.Gravity, offsetScale, false)
return img.Embed(resultWidth, resultHeight, offX, offY)
} }
func extend(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error { func extend(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error {
resultWidth, resultHeight := resultSize(po, pctx.dprScale) if !po.Extend.Enabled {
return extendImage(img, resultWidth, resultHeight, &po.Extend, pctx.dprScale, false) return nil
}
width, height := pctx.targetWidth, pctx.targetHeight
return extendImage(img, width, height, &po.Extend.Gravity, pctx.dprScale)
} }
func extendAspectRatio(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error { func extendAspectRatio(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error {
resultWidth, resultHeight := resultSize(po, pctx.dprScale) if !po.ExtendAspectRatio.Enabled {
return extendImage(img, resultWidth, resultHeight, &po.ExtendAspectRatio, pctx.dprScale, true) return nil
}
width, height := pctx.extendAspectRatioWidth, pctx.extendAspectRatioHeight
if width == 0 || height == 0 {
return nil
}
return extendImage(img, width, height, &po.ExtendAspectRatio.Gravity, pctx.dprScale)
} }

View File

@@ -30,6 +30,34 @@ type pipelineContext struct {
hscale float64 hscale float64
dprScale float64 dprScale float64
// The width we aim to get.
// Based on the requested width scaled according to processing options.
// Can be 0 if width is not specified in the processing options.
targetWidth int
// The height we aim to get.
// Based on the requested height scaled according to processing options.
// Can be 0 if height is not specified in the processing options.
targetHeight int
// The width of the image after cropping, scaling and rotating
scaledWidth int
// The height of the image after cropping, scaling and rotating
scaledHeight int
// The width of the result crop according to the resizing type
resultCropWidth int
// The height of the result crop according to the resizing type
resultCropHeight int
// The width of the image extended to the requested aspect ratio.
// Can be 0 if any of the dimensions is not specified in the processing options
// or if the image already has the requested aspect ratio.
extendAspectRatioWidth int
// The width of the image extended to the requested aspect ratio.
// Can be 0 if any of the dimensions is not specified in the processing options
// or if the image already has the requested aspect ratio.
extendAspectRatioHeight int
} }
type pipelineStep func(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error type pipelineStep func(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error

View File

@@ -41,7 +41,18 @@ func extractMeta(img *vips.Image, baseAngle int, useOrientation bool) (int, int,
return width, height, angle, flip return width, height, angle, flip
} }
func calcScale(width, height int, po *options.ProcessingOptions, imgtype imagetype.Type) (float64, float64, float64) { func calcCropSize(orig int, crop float64) int {
switch {
case crop == 0.0:
return 0
case crop >= 1.0:
return int(crop)
default:
return imath.Max(1, imath.Scale(orig, crop))
}
}
func (pctx *pipelineContext) calcScale(width, height int, po *options.ProcessingOptions) {
var wshrink, hshrink float64 var wshrink, hshrink float64
srcW, srcH := float64(width), float64(height) srcW, srcH := float64(width), float64(height)
@@ -98,22 +109,22 @@ func calcScale(width, height int, po *options.ProcessingOptions, imgtype imagety
wshrink /= po.ZoomWidth wshrink /= po.ZoomWidth
hshrink /= po.ZoomHeight hshrink /= po.ZoomHeight
dprScale := po.Dpr pctx.dprScale = po.Dpr
if !po.Enlarge && imgtype != imagetype.SVG { if !po.Enlarge && !pctx.imgtype.IsVector() {
minShrink := math.Min(wshrink, hshrink) minShrink := math.Min(wshrink, hshrink)
if minShrink < 1 { if minShrink < 1 {
wshrink /= minShrink wshrink /= minShrink
hshrink /= minShrink hshrink /= minShrink
if !po.Extend.Enabled { if !po.Extend.Enabled {
dprScale /= minShrink pctx.dprScale /= minShrink
} }
} }
// The minimum of wshrink and hshrink is the maximum dprScale value // The minimum of wshrink and hshrink is the maximum dprScale value
// that can be used without enlarging the image. // that can be used without enlarging the image.
dprScale = math.Min(dprScale, math.Min(wshrink, hshrink)) pctx.dprScale = math.Min(pctx.dprScale, math.Min(wshrink, hshrink))
} }
if po.MinWidth > 0 { if po.MinWidth > 0 {
@@ -130,8 +141,8 @@ func calcScale(width, height int, po *options.ProcessingOptions, imgtype imagety
} }
} }
wshrink /= dprScale wshrink /= pctx.dprScale
hshrink /= dprScale hshrink /= pctx.dprScale
if wshrink > srcW { if wshrink > srcW {
wshrink = srcW wshrink = srcW
@@ -141,17 +152,55 @@ func calcScale(width, height int, po *options.ProcessingOptions, imgtype imagety
hshrink = srcH hshrink = srcH
} }
return 1.0 / wshrink, 1.0 / hshrink, dprScale pctx.wscale = 1.0 / wshrink
pctx.hscale = 1.0 / hshrink
} }
func calcCropSize(orig int, crop float64) int { func (pctx *pipelineContext) calcSizes(widthToScale, heightToScale int, po *options.ProcessingOptions) {
switch { pctx.targetWidth = imath.Scale(po.Width, pctx.dprScale*po.ZoomWidth)
case crop == 0.0: pctx.targetHeight = imath.Scale(po.Height, pctx.dprScale*po.ZoomHeight)
return 0
case crop >= 1.0: pctx.scaledWidth = imath.Scale(widthToScale, pctx.wscale)
return int(crop) pctx.scaledHeight = imath.Scale(heightToScale, pctx.hscale)
default:
return imath.Max(1, imath.Scale(orig, crop)) if po.ResizingType == options.ResizeFillDown && !po.Enlarge {
diffW := float64(pctx.targetWidth) / float64(pctx.scaledWidth)
diffH := float64(pctx.targetHeight) / float64(pctx.scaledHeight)
switch {
case diffW > diffH && diffW > 1.0:
pctx.resultCropHeight = imath.Scale(pctx.scaledWidth, float64(pctx.targetHeight)/float64(pctx.targetWidth))
pctx.resultCropWidth = pctx.scaledWidth
case diffH > diffW && diffH > 1.0:
pctx.resultCropWidth = imath.Scale(pctx.scaledHeight, float64(pctx.targetWidth)/float64(pctx.targetHeight))
pctx.resultCropHeight = pctx.scaledHeight
default:
pctx.resultCropWidth = pctx.targetWidth
pctx.resultCropHeight = pctx.targetHeight
}
} else {
pctx.resultCropWidth = pctx.targetWidth
pctx.resultCropHeight = pctx.targetHeight
}
if po.ExtendAspectRatio.Enabled && pctx.targetWidth > 0 && pctx.targetHeight > 0 {
outWidth := imath.MinNonZero(pctx.scaledWidth, pctx.resultCropWidth)
outHeight := imath.MinNonZero(pctx.scaledHeight, pctx.resultCropHeight)
diffW := float64(pctx.targetWidth) / float64(outWidth)
diffH := float64(pctx.targetHeight) / float64(outHeight)
switch {
case diffH > diffW:
pctx.extendAspectRatioHeight = imath.Scale(outWidth, float64(pctx.targetHeight)/float64(pctx.targetWidth))
pctx.extendAspectRatioWidth = outWidth
case diffW > diffH:
pctx.extendAspectRatioWidth = imath.Scale(outHeight, float64(pctx.targetWidth)/float64(pctx.targetHeight))
pctx.extendAspectRatioHeight = outHeight
}
} }
} }
@@ -169,7 +218,7 @@ func prepare(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptio
widthToScale := imath.MinNonZero(pctx.cropWidth, pctx.srcWidth) widthToScale := imath.MinNonZero(pctx.cropWidth, pctx.srcWidth)
heightToScale := imath.MinNonZero(pctx.cropHeight, pctx.srcHeight) heightToScale := imath.MinNonZero(pctx.cropHeight, pctx.srcHeight)
pctx.wscale, pctx.hscale, pctx.dprScale = calcScale(widthToScale, heightToScale, po, pctx.imgtype) pctx.calcScale(widthToScale, heightToScale, po)
// The size of a vector image is not checked during download, yet it can be very large. // The size of a vector image is not checked during download, yet it can be very large.
// So we should scale it down to the maximum allowed resolution // So we should scale it down to the maximum allowed resolution
@@ -182,5 +231,7 @@ func prepare(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptio
} }
} }
pctx.calcSizes(widthToScale, heightToScale, po)
return nil return nil
} }

View File

@@ -0,0 +1,565 @@
package processing
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/suite"
"github.com/imgproxy/imgproxy/v3/config"
"github.com/imgproxy/imgproxy/v3/imagedata"
"github.com/imgproxy/imgproxy/v3/options"
"github.com/imgproxy/imgproxy/v3/security"
"github.com/imgproxy/imgproxy/v3/vips"
)
type ProcessingTestSuite struct {
suite.Suite
}
func (s *ProcessingTestSuite) SetupSuite() {
config.Reset()
s.Require().NoError(imagedata.Init())
s.Require().NoError(vips.Init())
logrus.SetOutput(io.Discard)
}
func (s *ProcessingTestSuite) openFile(name string) *imagedata.ImageData {
secopts := security.Options{
MaxSrcResolution: 10 * 1024 * 1024,
MaxSrcFileSize: 10 * 1024 * 1024,
MaxAnimationFrames: 100,
MaxAnimationFrameResolution: 10 * 1024 * 1024,
}
wd, err := os.Getwd()
s.Require().NoError(err)
path := filepath.Join(wd, "..", "testdata", name)
imagedata, err := imagedata.FromFile(path, "test image", secopts)
s.Require().NoError(err)
return imagedata
}
func (s *ProcessingTestSuite) checkSize(imgdata *imagedata.ImageData, width, height int) {
img := new(vips.Image)
err := img.Load(imgdata, 1, 1, 1)
s.Require().NoError(err)
defer img.Clear()
s.Require().Equal(width, img.Width(), "Width mismatch")
s.Require().Equal(height, img.Height(), "Height mismatch")
}
func (s *ProcessingTestSuite) TestResizeToFit() {
imgdata := s.openFile("test2.jpg")
po := options.NewProcessingOptions()
po.ResizingType = options.ResizeFit
testCases := []struct {
width int
height int
outWidth int
outHeight int
}{
{width: 50, height: 50, outWidth: 50, outHeight: 25},
{width: 50, height: 20, outWidth: 40, outHeight: 20},
{width: 20, height: 50, outWidth: 20, outHeight: 10},
{width: 300, height: 300, outWidth: 200, outHeight: 100},
{width: 300, height: 50, outWidth: 100, outHeight: 50},
{width: 100, height: 300, outWidth: 100, outHeight: 50},
{width: 0, height: 50, outWidth: 100, outHeight: 50},
{width: 50, height: 0, outWidth: 50, outHeight: 25},
{width: 0, height: 200, outWidth: 200, outHeight: 100},
{width: 300, height: 0, outWidth: 200, outHeight: 100},
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("%dx%d", tc.width, tc.height), func() {
po.Width = tc.width
po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err)
s.Require().NotNil(outImgdata)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight)
})
}
}
func (s *ProcessingTestSuite) TestResizeToFitEnlarge() {
imgdata := s.openFile("test2.jpg")
po := options.NewProcessingOptions()
po.ResizingType = options.ResizeFit
po.Enlarge = true
testCases := []struct {
width int
height int
outWidth int
outHeight int
}{
{width: 50, height: 50, outWidth: 50, outHeight: 25},
{width: 50, height: 20, outWidth: 40, outHeight: 20},
{width: 20, height: 50, outWidth: 20, outHeight: 10},
{width: 300, height: 300, outWidth: 300, outHeight: 150},
{width: 300, height: 125, outWidth: 250, outHeight: 125},
{width: 250, height: 300, outWidth: 250, outHeight: 125},
{width: 0, height: 50, outWidth: 100, outHeight: 50},
{width: 50, height: 0, outWidth: 50, outHeight: 25},
{width: 0, height: 200, outWidth: 400, outHeight: 200},
{width: 300, height: 0, outWidth: 300, outHeight: 150},
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("%dx%d", tc.width, tc.height), func() {
po.Width = tc.width
po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err)
s.Require().NotNil(outImgdata)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight)
})
}
}
func (s *ProcessingTestSuite) TestResizeToFitExtend() {
imgdata := s.openFile("test2.jpg")
po := options.NewProcessingOptions()
po.ResizingType = options.ResizeFit
po.Extend = options.ExtendOptions{
Enabled: true,
Gravity: options.GravityOptions{
Type: options.GravityCenter,
},
}
testCases := []struct {
width int
height int
outWidth int
outHeight int
}{
{width: 50, height: 50, outWidth: 50, outHeight: 50},
{width: 50, height: 20, outWidth: 50, outHeight: 20},
{width: 20, height: 50, outWidth: 20, outHeight: 50},
{width: 300, height: 300, outWidth: 300, outHeight: 300},
{width: 300, height: 125, outWidth: 300, outHeight: 125},
{width: 250, height: 300, outWidth: 250, outHeight: 300},
{width: 0, height: 50, outWidth: 100, outHeight: 50},
{width: 50, height: 0, outWidth: 50, outHeight: 25},
{width: 0, height: 200, outWidth: 200, outHeight: 200},
{width: 300, height: 0, outWidth: 300, outHeight: 100},
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("%dx%d", tc.width, tc.height), func() {
po.Width = tc.width
po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err)
s.Require().NotNil(outImgdata)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight)
})
}
}
func (s *ProcessingTestSuite) TestResizeToFitExtendAR() {
imgdata := s.openFile("test2.jpg")
po := options.NewProcessingOptions()
po.ResizingType = options.ResizeFit
po.ExtendAspectRatio = options.ExtendOptions{
Enabled: true,
Gravity: options.GravityOptions{
Type: options.GravityCenter,
},
}
testCases := []struct {
width int
height int
outWidth int
outHeight int
}{
{width: 50, height: 50, outWidth: 50, outHeight: 50},
{width: 50, height: 20, outWidth: 50, outHeight: 20},
{width: 20, height: 50, outWidth: 20, outHeight: 50},
{width: 300, height: 300, outWidth: 200, outHeight: 200},
{width: 300, height: 125, outWidth: 240, outHeight: 100},
{width: 250, height: 500, outWidth: 200, outHeight: 400},
{width: 0, height: 50, outWidth: 100, outHeight: 50},
{width: 50, height: 0, outWidth: 50, outHeight: 25},
{width: 0, height: 200, outWidth: 200, outHeight: 100},
{width: 300, height: 0, outWidth: 200, outHeight: 100},
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("%dx%d", tc.width, tc.height), func() {
po.Width = tc.width
po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err)
s.Require().NotNil(outImgdata)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight)
})
}
}
func (s *ProcessingTestSuite) TestResizeToFill() {
imgdata := s.openFile("test2.jpg")
po := options.NewProcessingOptions()
po.ResizingType = options.ResizeFill
testCases := []struct {
width int
height int
outWidth int
outHeight int
}{
{width: 50, height: 50, outWidth: 50, outHeight: 50},
{width: 50, height: 20, outWidth: 50, outHeight: 20},
{width: 20, height: 50, outWidth: 20, outHeight: 50},
{width: 300, height: 300, outWidth: 200, outHeight: 100},
{width: 300, height: 50, outWidth: 200, outHeight: 50},
{width: 100, height: 300, outWidth: 100, outHeight: 100},
{width: 0, height: 50, outWidth: 100, outHeight: 50},
{width: 50, height: 0, outWidth: 50, outHeight: 25},
{width: 0, height: 200, outWidth: 200, outHeight: 100},
{width: 300, height: 0, outWidth: 200, outHeight: 100},
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("%dx%d", tc.width, tc.height), func() {
po.Width = tc.width
po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err)
s.Require().NotNil(outImgdata)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight)
})
}
}
func (s *ProcessingTestSuite) TestResizeToFillEnlarge() {
imgdata := s.openFile("test2.jpg")
po := options.NewProcessingOptions()
po.ResizingType = options.ResizeFill
po.Enlarge = true
testCases := []struct {
width int
height int
outWidth int
outHeight int
}{
{width: 50, height: 50, outWidth: 50, outHeight: 50},
{width: 50, height: 20, outWidth: 50, outHeight: 20},
{width: 20, height: 50, outWidth: 20, outHeight: 50},
{width: 300, height: 300, outWidth: 300, outHeight: 300},
{width: 300, height: 125, outWidth: 300, outHeight: 125},
{width: 250, height: 300, outWidth: 250, outHeight: 300},
{width: 0, height: 50, outWidth: 100, outHeight: 50},
{width: 50, height: 0, outWidth: 50, outHeight: 25},
{width: 0, height: 200, outWidth: 400, outHeight: 200},
{width: 300, height: 0, outWidth: 300, outHeight: 150},
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("%dx%d", tc.width, tc.height), func() {
po.Width = tc.width
po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err)
s.Require().NotNil(outImgdata)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight)
})
}
}
func (s *ProcessingTestSuite) TestResizeToFillExtend() {
imgdata := s.openFile("test2.jpg")
po := options.NewProcessingOptions()
po.ResizingType = options.ResizeFill
po.Extend = options.ExtendOptions{
Enabled: true,
Gravity: options.GravityOptions{
Type: options.GravityCenter,
},
}
testCases := []struct {
width int
height int
outWidth int
outHeight int
}{
{width: 50, height: 50, outWidth: 50, outHeight: 50},
{width: 50, height: 20, outWidth: 50, outHeight: 20},
{width: 20, height: 50, outWidth: 20, outHeight: 50},
{width: 300, height: 300, outWidth: 300, outHeight: 300},
{width: 300, height: 125, outWidth: 300, outHeight: 125},
{width: 250, height: 300, outWidth: 250, outHeight: 300},
{width: 300, height: 50, outWidth: 300, outHeight: 50},
{width: 100, height: 300, outWidth: 100, outHeight: 300},
{width: 0, height: 50, outWidth: 100, outHeight: 50},
{width: 50, height: 0, outWidth: 50, outHeight: 25},
{width: 0, height: 200, outWidth: 200, outHeight: 200},
{width: 300, height: 0, outWidth: 300, outHeight: 100},
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("%dx%d", tc.width, tc.height), func() {
po.Width = tc.width
po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err)
s.Require().NotNil(outImgdata)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight)
})
}
}
func (s *ProcessingTestSuite) TestResizeToFillExtendAR() {
imgdata := s.openFile("test2.jpg")
po := options.NewProcessingOptions()
po.ResizingType = options.ResizeFill
po.ExtendAspectRatio = options.ExtendOptions{
Enabled: true,
Gravity: options.GravityOptions{
Type: options.GravityCenter,
},
}
testCases := []struct {
width int
height int
outWidth int
outHeight int
}{
{width: 50, height: 50, outWidth: 50, outHeight: 50},
{width: 50, height: 20, outWidth: 50, outHeight: 20},
{width: 20, height: 50, outWidth: 20, outHeight: 50},
{width: 300, height: 300, outWidth: 200, outHeight: 200},
{width: 300, height: 125, outWidth: 240, outHeight: 100},
{width: 250, height: 500, outWidth: 200, outHeight: 400},
{width: 300, height: 50, outWidth: 300, outHeight: 50},
{width: 100, height: 300, outWidth: 100, outHeight: 300},
{width: 0, height: 50, outWidth: 100, outHeight: 50},
{width: 50, height: 0, outWidth: 50, outHeight: 25},
{width: 0, height: 200, outWidth: 200, outHeight: 100},
{width: 300, height: 0, outWidth: 200, outHeight: 100},
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("%dx%d", tc.width, tc.height), func() {
po.Width = tc.width
po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err)
s.Require().NotNil(outImgdata)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight)
})
}
}
func (s *ProcessingTestSuite) TestResizeToFillDown() {
imgdata := s.openFile("test2.jpg")
po := options.NewProcessingOptions()
po.ResizingType = options.ResizeFillDown
testCases := []struct {
width int
height int
outWidth int
outHeight int
}{
{width: 50, height: 50, outWidth: 50, outHeight: 50},
{width: 50, height: 20, outWidth: 50, outHeight: 20},
{width: 20, height: 50, outWidth: 20, outHeight: 50},
{width: 300, height: 300, outWidth: 100, outHeight: 100},
{width: 300, height: 125, outWidth: 200, outHeight: 83},
{width: 250, height: 300, outWidth: 83, outHeight: 100},
{width: 0, height: 50, outWidth: 100, outHeight: 50},
{width: 50, height: 0, outWidth: 50, outHeight: 25},
{width: 0, height: 200, outWidth: 200, outHeight: 100},
{width: 300, height: 0, outWidth: 200, outHeight: 100},
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("%dx%d", tc.width, tc.height), func() {
po.Width = tc.width
po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err)
s.Require().NotNil(outImgdata)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight)
})
}
}
func (s *ProcessingTestSuite) TestResizeToFillDownEnlarge() {
imgdata := s.openFile("test2.jpg")
po := options.NewProcessingOptions()
po.ResizingType = options.ResizeFillDown
po.Enlarge = true
testCases := []struct {
width int
height int
outWidth int
outHeight int
}{
{width: 50, height: 50, outWidth: 50, outHeight: 50},
{width: 50, height: 20, outWidth: 50, outHeight: 20},
{width: 20, height: 50, outWidth: 20, outHeight: 50},
{width: 300, height: 300, outWidth: 300, outHeight: 300},
{width: 300, height: 125, outWidth: 300, outHeight: 125},
{width: 250, height: 300, outWidth: 250, outHeight: 300},
{width: 0, height: 50, outWidth: 100, outHeight: 50},
{width: 50, height: 0, outWidth: 50, outHeight: 25},
{width: 0, height: 200, outWidth: 400, outHeight: 200},
{width: 300, height: 0, outWidth: 300, outHeight: 150},
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("%dx%d", tc.width, tc.height), func() {
po.Width = tc.width
po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err)
s.Require().NotNil(outImgdata)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight)
})
}
}
func (s *ProcessingTestSuite) TestResizeToFillDownExtend() {
imgdata := s.openFile("test2.jpg")
po := options.NewProcessingOptions()
po.ResizingType = options.ResizeFillDown
po.Extend = options.ExtendOptions{
Enabled: true,
Gravity: options.GravityOptions{
Type: options.GravityCenter,
},
}
testCases := []struct {
width int
height int
outWidth int
outHeight int
}{
{width: 50, height: 50, outWidth: 50, outHeight: 50},
{width: 50, height: 20, outWidth: 50, outHeight: 20},
{width: 20, height: 50, outWidth: 20, outHeight: 50},
{width: 300, height: 300, outWidth: 300, outHeight: 300},
{width: 300, height: 125, outWidth: 300, outHeight: 125},
{width: 250, height: 300, outWidth: 250, outHeight: 300},
{width: 300, height: 50, outWidth: 300, outHeight: 50},
{width: 100, height: 300, outWidth: 100, outHeight: 300},
{width: 0, height: 50, outWidth: 100, outHeight: 50},
{width: 50, height: 0, outWidth: 50, outHeight: 25},
{width: 0, height: 200, outWidth: 200, outHeight: 200},
{width: 300, height: 0, outWidth: 300, outHeight: 100},
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("%dx%d", tc.width, tc.height), func() {
po.Width = tc.width
po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err)
s.Require().NotNil(outImgdata)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight)
})
}
}
func (s *ProcessingTestSuite) TestResizeToFillDownExtendAR() {
imgdata := s.openFile("test2.jpg")
po := options.NewProcessingOptions()
po.ResizingType = options.ResizeFillDown
po.ExtendAspectRatio = options.ExtendOptions{
Enabled: true,
Gravity: options.GravityOptions{
Type: options.GravityCenter,
},
}
testCases := []struct {
width int
height int
outWidth int
outHeight int
}{
{width: 50, height: 50, outWidth: 50, outHeight: 50},
{width: 50, height: 20, outWidth: 50, outHeight: 20},
{width: 20, height: 50, outWidth: 20, outHeight: 50},
{width: 300, height: 300, outWidth: 100, outHeight: 100},
{width: 300, height: 125, outWidth: 200, outHeight: 83},
{width: 250, height: 300, outWidth: 83, outHeight: 100},
{width: 0, height: 50, outWidth: 100, outHeight: 50},
{width: 50, height: 0, outWidth: 50, outHeight: 25},
{width: 0, height: 200, outWidth: 200, outHeight: 100},
{width: 300, height: 0, outWidth: 200, outHeight: 100},
}
for _, tc := range testCases {
s.Run(fmt.Sprintf("%dx%d", tc.width, tc.height), func() {
po.Width = tc.width
po.Height = tc.height
outImgdata, err := ProcessImage(context.Background(), imgdata, po)
s.Require().NoError(err)
s.Require().NotNil(outImgdata)
s.checkSize(outImgdata, tc.outWidth, tc.outHeight)
})
}
}
func TestProcessing(t *testing.T) {
suite.Run(t, new(ProcessingTestSuite))
}

View File

@@ -1,13 +0,0 @@
package processing
import (
"github.com/imgproxy/imgproxy/v3/imath"
"github.com/imgproxy/imgproxy/v3/options"
)
func resultSize(po *options.ProcessingOptions, dprScale float64) (int, int) {
resultWidth := imath.Scale(po.Width, dprScale*po.ZoomWidth)
resultHeight := imath.Scale(po.Height, dprScale*po.ZoomHeight)
return resultWidth, resultHeight
}

BIN
testdata/test2.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B