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 {
|
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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
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