You've already forked imgproxy
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:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
565
processing/processing_test.go
Normal file
565
processing/processing_test.go
Normal 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))
|
||||
}
|
||||
@@ -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
BIN
testdata/test2.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 649 B |
Reference in New Issue
Block a user