1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-06-17 22:37:33 +02:00

Better animation detection

This commit is contained in:
DarthSim
2022-06-10 15:27:15 +06:00
parent 1de96252ed
commit 8a536d69a3
2 changed files with 48 additions and 44 deletions

View File

@ -42,20 +42,6 @@ func imageTypeGoodForWeb(imgtype imagetype.Type) bool {
imgtype != imagetype.BMP imgtype != imagetype.BMP
} }
// src - the source image format
// dst - what the user specified
// want - what we want switch to
func canSwitchFormat(src, dst, want imagetype.Type) bool {
// If the format we want is not supported, we can't switch to it anyway
return vips.SupportsSave(want) &&
// if src format does't support animation, we can switch to whatever we want
(!src.SupportsAnimation() ||
// if user specified the format and it doesn't support animation, we can switch to whatever we want
(dst != imagetype.Unknown && !dst.SupportsAnimation()) ||
// if the format we want supports animation, we can switch in any case
want.SupportsAnimation())
}
func canFitToBytes(imgtype imagetype.Type) bool { func canFitToBytes(imgtype imagetype.Type) bool {
switch imgtype { switch imgtype {
case imagetype.JPEG, imagetype.WEBP, imagetype.AVIF, imagetype.TIFF: case imagetype.JPEG, imagetype.WEBP, imagetype.AVIF, imagetype.TIFF:
@ -96,7 +82,7 @@ func transformAnimated(ctx context.Context, img *vips.Image, po *options.Process
} }
// Vips 8.8+ supports n-pages and doesn't load the whole animated image on header access // Vips 8.8+ supports n-pages and doesn't load the whole animated image on header access
if nPages, _ := img.GetIntDefault("n-pages", 0); nPages > framesCount { if nPages, _ := img.GetIntDefault("n-pages", 1); nPages > framesCount {
// Load only the needed frames // Load only the needed frames
if err = img.Load(imgdata, 1, 1.0, framesCount); err != nil { if err = img.Load(imgdata, 1, 1.0, framesCount); err != nil {
return err return err
@ -117,7 +103,7 @@ func transformAnimated(ctx context.Context, img *vips.Image, po *options.Process
po.Watermark.Enabled = false po.Watermark.Enabled = false
defer func() { po.Watermark.Enabled = watermarkEnabled }() defer func() { po.Watermark.Enabled = watermarkEnabled }()
frames := make([]*vips.Image, framesCount) frames := make([]*vips.Image, 0, framesCount)
defer func() { defer func() {
for _, frame := range frames { for _, frame := range frames {
if frame != nil { if frame != nil {
@ -133,7 +119,7 @@ func transformAnimated(ctx context.Context, img *vips.Image, po *options.Process
return err return err
} }
frames[i] = frame frames = append(frames, frame)
if err = mainPipeline.Run(ctx, frame, po, nil); err != nil { if err = mainPipeline.Run(ctx, frame, po, nil); err != nil {
return err return err
@ -207,29 +193,10 @@ func ProcessImage(ctx context.Context, imgdata *imagedata.ImageData, po *options
defer vips.Cleanup() defer vips.Cleanup()
switch { animationSupport :=
case po.Format == imagetype.Unknown: config.MaxAnimationFrames > 1 &&
switch { imgdata.Type.SupportsAnimation() &&
case po.PreferAvif && canSwitchFormat(imgdata.Type, imagetype.Unknown, imagetype.AVIF): (po.Format == imagetype.Unknown || po.Format.SupportsAnimation())
po.Format = imagetype.AVIF
case po.PreferWebP && canSwitchFormat(imgdata.Type, imagetype.Unknown, imagetype.WEBP):
po.Format = imagetype.WEBP
case vips.SupportsSave(imgdata.Type) && imageTypeGoodForWeb(imgdata.Type):
po.Format = imgdata.Type
default:
po.Format = imagetype.JPEG
}
case po.EnforceAvif && canSwitchFormat(imgdata.Type, po.Format, imagetype.AVIF):
po.Format = imagetype.AVIF
case po.EnforceWebP && canSwitchFormat(imgdata.Type, po.Format, imagetype.WEBP):
po.Format = imagetype.WEBP
}
if !vips.SupportsSave(po.Format) {
return nil, fmt.Errorf("Can't save %s, probably not supported by your libvips", po.Format)
}
animationSupport := config.MaxAnimationFrames > 1 && imgdata.Type.SupportsAnimation() && po.Format.SupportsAnimation()
pages := 1 pages := 1
if animationSupport { if animationSupport {
@ -251,11 +218,43 @@ func ProcessImage(ctx context.Context, imgdata *imagedata.ImageData, po *options
originWidth, originHeight := getImageSize(img) originWidth, originHeight := getImageSize(img)
if animationSupport && img.IsAnimated() { animated := img.IsAnimated()
switch {
case po.Format == imagetype.Unknown:
switch {
case po.PreferAvif && !animated:
po.Format = imagetype.AVIF
case po.PreferWebP:
po.Format = imagetype.WEBP
case vips.SupportsSave(imgdata.Type) && imageTypeGoodForWeb(imgdata.Type):
po.Format = imgdata.Type
default:
po.Format = imagetype.JPEG
}
case po.EnforceAvif && !animated:
po.Format = imagetype.AVIF
case po.EnforceWebP:
po.Format = imagetype.WEBP
}
if !vips.SupportsSave(po.Format) {
return nil, fmt.Errorf("Can't save %s, probably not supported by your libvips", po.Format)
}
if po.Format.SupportsAnimation() && animated {
if err := transformAnimated(ctx, img, po, imgdata); err != nil { if err := transformAnimated(ctx, img, po, imgdata); err != nil {
return nil, err return nil, err
} }
} else { } else {
if animated {
// We loaded animated image but the resulting format doesn't support
// animations, so we need to reload image as not animated
if err := img.Load(imgdata, 1, 1.0, 1); err != nil {
return nil, err
}
}
if err := mainPipeline.Run(ctx, img, po, imgdata); err != nil { if err := mainPipeline.Run(ctx, img, po, imgdata); err != nil {
return nil, err return nil, err
} }

View File

@ -139,9 +139,14 @@ vips_band_format(VipsImage *in) {
gboolean gboolean
vips_is_animated(VipsImage * in) { vips_is_animated(VipsImage * in) {
return( vips_image_get_typeof(in, "page-height") != G_TYPE_INVALID && int n_pages;
vips_image_get_typeof(in, "gif-delay") != G_TYPE_INVALID &&
vips_image_get_typeof(in, "gif-loop") != G_TYPE_INVALID ); return( vips_image_get_typeof(in, "delay") != G_TYPE_INVALID &&
vips_image_get_typeof(in, "loop") != G_TYPE_INVALID &&
vips_image_get_typeof(in, "page-height") == G_TYPE_INT &&
vips_image_get_typeof(in, "n-pages") == G_TYPE_INT &&
vips_image_get_int(in, "n-pages", &n_pages) == 0 &&
n_pages > 1 );
} }
int int