One Load Path for Stills and Animation
Pass n=-1 to the libvips loader and one path handles stills and animation alike — because the default n=1 silently decodes only the first frame and drops the rest.
Pass n=-1 to the libvips loader and one unconditional path handles every input, with no format sniffing and no page-count branch.
importParams := vips.NewImportParams()
importParams.NumPages.Set(-1) // -1 = load ALL pages/frames
image, err := vips.LoadImageFromBuffer(imageBytes, importParams)
NumPages maps to the loader’s n. -1 means “read every page that exists”:
- Animated GIF (5 frames) stacks into 5 pages, so
Pages() == 5. - Single-frame image (JPEG, PNG, static GIF) is exactly 1 page, so
Pages() == 1andPageHeight() == Height(). Here-1is a genuine no-op: no “0 frames,” no error.
So single vs multi is decided after the load with Pages() and PageHeight(), never at the load call. -1 is also format-agnostic: animated WebP/AVIF and multi-page TIFF/PDF all take the same path, even if a comment says “for GIFs.”
The trap: the default n=1 silently drops animation
The loader’s n defaults to 1, so a GIF loads only page 0. The loader never decodes the other frames, so it drops the animation and you get a still of the first frame:
n = 1 decodes only page 0. The fix is to load with n = -1.No exception, no warning. The failure mode is a quietly lost animation, which is exactly why the -1 matters.
A default is not neutral. It will quietly drop the frames you did not read.
References:
Related:
- This builds on how libvips stores an animation as one tall stacked strip.
- It feeds uniform vs positional transforms on a stitched strip.
- Or go back to the crop, resize, fill overview.