From bad20a3f02cb156b711aff4c06b41523986e8210 Mon Sep 17 00:00:00 2001 From: DarthSim Date: Mon, 22 Feb 2021 19:30:55 +0600 Subject: [PATCH] Fix animations loops and delays --- process.go | 39 ++++++++++++++++++++++++++++++++++----- vips.c | 20 ++++++++++++++++++++ vips.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ vips.h | 3 +++ 4 files changed, 104 insertions(+), 5 deletions(-) diff --git a/process.go b/process.go index 1b84afbf..f4db5dad 100644 --- a/process.go +++ b/process.go @@ -595,19 +595,30 @@ func transformAnimated(ctx context.Context, img *vipsImage, data []byte, po *pro } // Vips 8.8+ supports n-pages and doesn't load the whole animated image on header access - if nPages, _ := img.GetInt("n-pages"); nPages > framesCount { + if nPages, _ := img.GetIntDefault("n-pages", 0); nPages > framesCount { // Load only the needed frames if err = img.Load(data, imgtype, 1, 1.0, framesCount); err != nil { return err } } - delay, err := img.GetInt("gif-delay") + delay, err := img.GetIntSliceDefault("delay", nil) if err != nil { return err } - loop, err := img.GetInt("gif-loop") + loop, err := img.GetIntDefault("loop", 0) + if err != nil { + return err + } + + // Legacy fields + // TODO: remove this in major update + gifLoop, err := img.GetIntDefault("gif-loop", -1) + if err != nil { + return err + } + gifDelay, err := img.GetIntDefault("gif-delay", -1) if err != nil { return err } @@ -661,11 +672,29 @@ func transformAnimated(ctx context.Context, img *vipsImage, data []byte, po *pro return err } + if len(delay) == 0 { + delay = make([]int, framesCount) + for i := range delay { + delay[i] = 40 + } + } else if len(delay) > framesCount { + delay = delay[:framesCount] + } + img.SetInt("page-height", frames[0].Height()) - img.SetInt("gif-delay", delay) - img.SetInt("gif-loop", loop) + img.SetIntSlice("delay", delay) + img.SetInt("loop", loop) img.SetInt("n-pages", framesCount) + // Legacy fields + // TODO: remove this in major update + if gifLoop >= 0 { + img.SetInt("gif-loop", gifLoop) + } + if gifDelay >= 0 { + img.SetInt("gif-delay", gifDelay) + } + return nil } diff --git a/vips.c b/vips.c index e3e22cda..5f27cac6 100644 --- a/vips.c +++ b/vips.c @@ -28,6 +28,9 @@ #define VIPS_SUPPORT_WEBP_ANIMATION \ (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 8)) +#define VIPS_SUPPORT_ARRAY_HEADERS \ + (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 9)) + #define VIPS_SUPPORT_HEIF \ (VIPS_MAJOR_VERSION > 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION >= 8)) @@ -245,6 +248,23 @@ vips_is_animated(VipsImage * in) { vips_image_get_typeof(in, "gif-loop") != G_TYPE_INVALID ); } +int +vips_image_get_array_int_go(VipsImage *image, const char *name, int **out, int *n) { +#if VIPS_SUPPORT_ARRAY_HEADERS + return vips_image_get_array_int(image, name, out, n); +#else + vips_error("vips_image_get_array_int_go", "Array headers are not supported (libvips 8.9+ reuired)"); + return 1; +#endif +} + +void +vips_image_set_array_int_go(VipsImage *image, const char *name, const int *array, int n) { +#if VIPS_SUPPORT_ARRAY_HEADERS + vips_image_set_array_int(image, name, array, n); +#endif +} + gboolean vips_image_hasalpha_go(VipsImage * in) { #if VIPS_SUPPORT_HASALPHA diff --git a/vips.go b/vips.go index 67b21ef1..5ab1cd74 100644 --- a/vips.go +++ b/vips.go @@ -13,6 +13,7 @@ import ( "encoding/binary" "errors" "fmt" + "math" "os" "runtime" "unsafe" @@ -330,10 +331,56 @@ func (img *vipsImage) GetInt(name string) (int, error) { return int(i), nil } +func (img *vipsImage) GetIntDefault(name string, def int) (int, error) { + if C.vips_image_get_typeof(img.VipsImage, cachedCString(name)) == 0 { + return def, nil + } + + return img.GetInt(name) +} + +func (img *vipsImage) GetIntSlice(name string) ([]int, error) { + var ptr unsafe.Pointer + size := C.int(0) + + if C.vips_image_get_array_int_go(img.VipsImage, cachedCString(name), (**C.int)(unsafe.Pointer(&ptr)), &size) != 0 { + return nil, vipsError() + } + + if size == 0 { + return []int{}, nil + } + + cOut := (*[math.MaxInt32]C.int)(ptr)[:int(size):int(size)] + out := make([]int, int(size)) + + for i, el := range cOut { + out[i] = int(el) + } + + return out, nil +} + +func (img *vipsImage) GetIntSliceDefault(name string, def []int) ([]int, error) { + if C.vips_image_get_typeof(img.VipsImage, cachedCString(name)) == 0 { + return def, nil + } + + return img.GetIntSlice(name) +} + func (img *vipsImage) SetInt(name string, value int) { C.vips_image_set_int(img.VipsImage, cachedCString(name), C.int(value)) } +func (img *vipsImage) SetIntSlice(name string, value []int) { + in := make([]C.int, len(value)) + for i, el := range value { + in[i] = C.int(el) + } + C.vips_image_set_array_int_go(img.VipsImage, cachedCString(name), &in[0], C.int(len(value))) +} + func (img *vipsImage) CastUchar() error { var tmp *C.VipsImage diff --git a/vips.h b/vips.h index 8e4fb9a1..2cd601ed 100644 --- a/vips.h +++ b/vips.h @@ -47,6 +47,9 @@ VipsBandFormat vips_band_format(VipsImage *in); gboolean vips_support_webp_animation(); gboolean vips_is_animated(VipsImage * in); +int vips_image_get_array_int_go(VipsImage *image, const char *name, int **out, int *n); +void vips_image_set_array_int_go(VipsImage *image, const char *name, const int *array, int n); + gboolean vips_image_hasalpha_go(VipsImage * in); int vips_addalpha_go(VipsImage *in, VipsImage **out);