Skip to content

govips hand-written API, generated API, and idiomatic patterns

Copy page

govips v2.19.0 wraps libvips with two API layers: carefully maintained hand-written functions for I/O, export, streaming, and complex workflows, and auto-generated type-safe bindings for 193+ pixel-level operations. This page explains how they fit together, covers the major changes since v2.18.0 (pipeline package removal, minimum libvips raised to 8.14, OpenImageRefs leak detection), and covers v2.19.0’s new streaming I/O (LoadImageFromReader/SaveToWriter/TranscodeStream), and what you need to know to use govips safely in production.

These are maintained in the main package and provide lifecycle management, file/buffer/stream I/O, encoding/decoding, and convenience helpers that cannot be covered by the GObject introspection code generator:

Category Key functions
Lifecycle Startup(nil) (must check error), Shutdown(), OpenImageRefs(), AssertNoLeaks()
Input NewImageFromFile, NewImageFromBuffer, NewThumbnailFromFile, LoadImageFromReader, NewImageFromMemory
Geometry Resize, ExtractArea, Similarity, Flip, Rotate
Colour Modulate (LCH colourspace)
Alpha Flatten, HasAlpha, Bands
Export ExportWebp, ExportPng, ExportJpeg, SaveToWriter (and format-specific variants)
Streaming TranscodeStream, SetPipeReadLimit, SetStreamDiscThreshold
Cancellation SetKill

Every program must call vips.Startup(nil), check the error, and defer vips.Shutdown(). Every *vips.ImageRef must be closed when done:

if err := vips.Startup(nil); err != nil {
return fmt.Errorf("start libvips: %w", err)
}
defer vips.Shutdown()
image, err := vips.NewImageFromFile("input.jpg")
if err != nil {
return err
}
defer image.Close()

govips’ code generator reads libvips GObject introspection data at build time and generates the low-level wrapper code. The vipsGen* functions are internal implementations; user-facing ImageRef methods and selected exported helpers provide the type-safe API around them. As of v2.19.0 this covers 193+ operations across nine categories:

arithmetic, colour, conversion, convolution, create,
freqfilt, histogram, morphology, resample

Application code calls the public ImageRef methods. vipsGen* stays inside govips and is not a second API for application code to call. The generator runs only when govips itself is built.

govips v2.19.0 introduces full io.Reader / io.Writer streaming:

  • LoadImageFromReader(r io.Reader, params *ImportParams): creates an ImageRef from a Reader. Without AccessSequential, the decoded image is materialised (fully decoded and buffered into memory or a temporary file) before the call returns. Only an explicit sequential access defers pixel reads until something consumes them.
  • SaveToWriter(w io.Writer, format ImageType, params *ExportParams): writes encoded bytes to a Writer. Format-specific variants include SaveToWriterJpeg and SaveToWriterWebp.
  • TranscodeStream(r io.Reader, w io.Writer, opts *TranscodeOptions): wraps read, optional auto-rotate, and write in one call. It does not take a context.Context.
  • SetKill(true): asks the current evaluation to stop, typically from another goroutine. Set the flag back to false afterwards. On v2.19.0, setting kill before export made that export return an error.

Two decode strategies:

  • Sequential fast path: with explicit AccessSequential and no later random-access operation, pixels are read from the Reader when something consumes them. Memory still depends on the codec and the line cache. It is not a constant ceiling independent of image width.
  • Materialised path: the default import, or any later random-access operation, places the decoded image in memory or a temporary file.
out, err := os.Create("output.jpg")
if err != nil {
return err
}
defer out.Close()
image, err := vips.LoadImageFromReader(os.Stdin, nil)
if err != nil {
return err
}
defer image.Close()
if err := image.AutoRotate(); err != nil {
return err
}
return image.SaveToWriter(out, vips.ImageTypeJPEG, &vips.ExportParams{Quality: 85})
  1. Startup once per process, always check the error. It initialises libvips internal state. As of v2.18.0, Startup may return an error.
  2. Shutdown once, always deferred. It frees global libvips resources. Missing it may leave native resources unreleased.
  3. Close every ImageRef. Each load allocates libvips native memory. Defer Close() or explicitly close when done.
  4. OpenImageRefs()/AssertNoLeaks(t) available since v2.18.0. Track unclosed ImageRef instances during development and testing.
  5. Never use an image after close. libvips may crash or return corrupted data. In Go, this surfaces as a panic or silent error.
func process(inputPath string) error {
image, err := vips.NewImageFromFile(inputPath)
if err != nil {
return err
}
defer image.Close()
// Operations here
return nil
}
  • pipeline package removed. It was a thin wrapper over ~40 ImageRef methods. Use ImageRef methods directly.
  • Minimum libvips raised from 8.10 to 8.14.
  • ImageRef split into 8 focused files by concern. Zero public API changes.
  • startupIfNeeded no longer panics, returns errors instead.

libvips uses GLib for memory allocation, which can cause fragmentation under heavy concurrency. Two approaches help:

  • MALLOC_ARENA_MAX=2: Reduces GLib malloc arena count. Try this first.
  • Jemalloc: Replaces the system allocator. Build with CGO_LDFLAGS="-ljemalloc".

Neither is a silver bullet — measure your own workload before adopting either.

  • vipsgen is not a fifth interface and not its own binding. It is the code generator inside govips.
  • vipsGen* is not an API for application code. The public entry is an ImageRef method; the generated functions implement those methods.
  • Import github.com/davidbyttow/govips/v2/vips. Handwritten wrappers and generated operations share *vips.ImageRef, and application code stops at the public method.

govips v2.19.0 and libvips 8.17.3. Reviewed 2026-10-12. The SetKill failure path was executed on that version.