govips hand-written API, generated API, and idiomatic patterns
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.
The two API layers
Section titled “The two API layers”Hand-written functions
Section titled “Hand-written functions”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()Generated operations
Section titled “Generated operations”cmd/vipsgen/ reads libvips GObject introspection data at build time and generates generated.go, generated.c, and generated.h. 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, resampleMost users never need to run the generator — it runs during govips development. The generated functions are compiled into the vips package you import. If you are adding support for new libvips operations, regenerate with go generate ./vips/.
Streaming I/O (new in v2.19.0)
Section titled “Streaming I/O (new in v2.19.0)”govips v2.19.0 introduces full io.Reader/io.Writer streaming:
LoadImageFromReader(r io.Reader, params *ImportParams): Load from any Reader (HTTP body, S3 stream, file handle) without buffering the full compressed input in Go memory.SaveToWriter(w io.Writer, format ImageType, params *ExportParams): Encode directly to a Writer. Format-specific variants exist (SaveToWriterJpeg,SaveToWriterWebp, etc.).TranscodeStream(r io.Reader, w io.Writer, opts *TranscodeOptions): All-in-one Reader → decode → transform → encode → Writer pipeline.
Two decode strategies:
- Sequential fast path: When no random-access transform is needed, pixels flow from Reader to Writer. Peak RAM is bound by libvips line caches, independent of file and pixel count.
- Materialized path: When random access is required (e.g., EXIF rotation), the decoded frame is rendered in one streaming pass to memory or a scratch disk file.
out, _ := os.Create("output.jpg")defer out.Close()
image, _ := vips.LoadImageFromReader(os.Stdin, nil)defer image.Close()
image.AutoRotate()image.SaveToWriter(out, vips.ImageTypeJPEG, &vips.ExportParams{Quality: 85})Lifecycle rules for production
Section titled “Lifecycle rules for production”Startuponce per process, always check the error. It initialises libvips internal state. As of v2.18.0,Startupmay return an error.Shutdownonce, always deferred. It frees global libvips resources. Missing it may cause memory leaks.CloseeveryImageRef. Each load allocates libvips memory. DeferClose()or explicitly close when done.OpenImageRefs()/AssertNoLeaks(t)available since v2.18.0. Track unclosed ImageRef instances during development and testing.- Programming error: using an image after close. libvips may crash or return corrupted data. In Go, this surfaces as a panic or silent garbage output.
func process(inputPath string) error { image, err := vips.NewImageFromFile(inputPath) if err != nil { return err } defer image.Close()
// Operations here return nil}v2.18.0 breaking changes
Section titled “v2.18.0 breaking changes”pipelinepackage removed. It was a thin wrapper over ~40 ImageRef methods. UseImageRefmethods directly.- Minimum libvips raised from 8.10 to 8.14.
- ImageRef split into 8 focused files by concern. Zero public API changes.
startupIfNeededno longer panics, returns errors instead.
Memory and performance notes
Section titled “Memory and performance notes”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.
What you don’t need to worry about
Section titled “What you don’t need to worry about”vipsgenis not a separate binding or a fifth tab. It is govips’ internal code generator. External projects with similar names are not supported by this handbook unless separately evaluated.- You import
github.com/davidbyttow/govips/v2/vips. The generated code is in the same package. - Hand-written and generated operations share the same
*vips.ImageReftype and lifecycle — there is no semantic split at the call site.
Verified version
Section titled “Verified version”govips v2.19.0, libvips 8.17.3 (reference). Reviewed 2026-10-11.