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:
@ -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
|
||||||
}
|
}
|
||||||
|
11
vips/vips.c
11
vips/vips.c
@ -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
|
||||||
|
Reference in New Issue
Block a user