1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-10-30 23:08:02 +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 {
// Crop image to the result size
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)
return cropImage(img, pctx.resultCropWidth, pctx.resultCropHeight, &po.Gravity, pctx.dprScale)
}

View File

@@ -2,61 +2,47 @@ package processing
import (
"github.com/imgproxy/imgproxy/v3/imagedata"
"github.com/imgproxy/imgproxy/v3/imath"
"github.com/imgproxy/imgproxy/v3/options"
"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()
imgHeight := img.Height()
if !opts.Enabled || (resultWidth <= imgWidth && resultHeight <= imgHeight) {
if width <= imgWidth && height <= imgHeight {
return nil
}
if resultWidth <= 0 {
if extendAr {
return nil
}
resultWidth = imgWidth
if width <= 0 {
width = imgWidth
}
if resultHeight <= 0 {
if extendAr {
return nil
}
resultHeight = imgHeight
if height <= 0 {
height = imgHeight
}
if extendAr && resultWidth > imgWidth && resultHeight > imgHeight {
diffW := float64(resultWidth) / float64(imgWidth)
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)
offX, offY := calcPosition(width, height, imgWidth, imgHeight, gravity, offsetScale, false)
return img.Embed(width, height, offX, offY)
}
func extend(pctx *pipelineContext, img *vips.Image, po *options.ProcessingOptions, imgdata *imagedata.ImageData) error {
resultWidth, resultHeight := resultSize(po, pctx.dprScale)
return extendImage(img, resultWidth, resultHeight, &po.Extend, pctx.dprScale, false)
if !po.Extend.Enabled {
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 {
resultWidth, resultHeight := resultSize(po, pctx.dprScale)
return extendImage(img, resultWidth, resultHeight, &po.ExtendAspectRatio, pctx.dprScale, true)
if !po.ExtendAspectRatio.Enabled {
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
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

View File

@@ -41,7 +41,18 @@ func extractMeta(img *vips.Image, baseAngle int, useOrientation bool) (int, int,
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
srcW, srcH := float64(width), float64(height)
@@ -98,22 +109,22 @@ func calcScale(width, height int, po *options.ProcessingOptions, imgtype imagety
wshrink /= po.ZoomWidth
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)
if minShrink < 1 {
wshrink /= minShrink
hshrink /= minShrink
if !po.Extend.Enabled {
dprScale /= minShrink
pctx.dprScale /= minShrink
}
}
// The minimum of wshrink and hshrink is the maximum dprScale value
// 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 {
@@ -130,8 +141,8 @@ func calcScale(width, height int, po *options.ProcessingOptions, imgtype imagety
}
}
wshrink /= dprScale
hshrink /= dprScale
wshrink /= pctx.dprScale
hshrink /= pctx.dprScale
if wshrink > srcW {
wshrink = srcW
@@ -141,17 +152,55 @@ func calcScale(width, height int, po *options.ProcessingOptions, imgtype imagety
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 {
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) calcSizes(widthToScale, heightToScale int, po *options.ProcessingOptions) {
pctx.targetWidth = imath.Scale(po.Width, pctx.dprScale*po.ZoomWidth)
pctx.targetHeight = imath.Scale(po.Height, pctx.dprScale*po.ZoomHeight)
pctx.scaledWidth = imath.Scale(widthToScale, pctx.wscale)
pctx.scaledHeight = imath.Scale(heightToScale, pctx.hscale)
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)
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.
// 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
}

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