Skip to content
All writing ‹ Part 06 of 07 · Crop, Resize, Fill: Image Processing
Engineering · 2 min read

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() == 1 and PageHeight() == Height(). Here -1 is 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:

GIF with 5 frames 5 stacked frames load n = 1 frame 0 — kept frames 1–4 discarded no exception · no warning Pages() = 1 → treated as a plain still · animation LOST
The default 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:

↑↓ navigate ↵ open esc close