vipsgen is a Go binding generator for libvips - a fast and efficient image processing library.
Existing Go libvips bindings rely on manually written code that is often incomplete, error-prone, and difficult to maintain as libvips evolves. vipsgen solves this by generating type-safe, robust, and fully documented Go bindings using GObject introspection.
You can use vipsgen in two ways:
- Import directly: Use the pre-generated library
github.com/cshum/vipsgen/vips
for the full installation of libvips 8.17 - Generate custom bindings: Run the vipsgen command to create bindings tailored to your specific libvips version and installation
- Comprehensive: Bindings for around 300 libvips operations
- Type-Safe: Proper Go types for all libvips C enums and structs
- Idiomatic: Clean Go APIs that feel natural to use
- Streaming:
VipsSource
andVipsTarget
integration with Goio.Reader
andio.Writer
for streaming
Use homebrew to install vips and pkg-config:
brew install vips pkg-config
On MacOS, vipsgen may not compile without first setting an environment variable:
export CGO_CFLAGS_ALLOW="-Xpreprocessor"
Use the package directly:
go get -u github.com/cshum/vipsgen/vips
vipsgen provides rich options for fine-tuning image operations. Each operation can accept a nil value for default options, or customize optional arguments with specific option structs:
package main
import (
"log"
"net/http"
"github.com/cshum/vipsgen/vips"
)
func main() {
// Fetch an image from http.Get
resp, err := http.Get("https://raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png")
if err != nil {
log.Fatalf("Failed to fetch image: %v", err)
}
defer resp.Body.Close()
// Create source from io.ReadCloser
source := vips.NewSource(resp.Body)
defer source.Close() // source needs to remain available during image lifetime
// Shrink-on-load via creating image from thumbnail source with options
image, err := vips.NewThumbnailSource(source, 800, &vips.ThumbnailSourceOptions{
Height: 1000,
FailOn: vips.FailOnError, // Fail on first error
})
if err != nil {
log.Fatalf("Failed to load image: %v", err)
}
defer image.Close() // always close images to free memory
// Add a yellow border using vips_embed
border := 10
if err := image.Embed(
border, border,
image.Width()+border*2,
image.Height()+border*2,
&vips.EmbedOptions{
Extend: vips.ExtendBackground, // extend with colour from the background property
Background: []float64{255, 255, 0, 255}, // Yellow border
},
); err != nil {
log.Fatalf("Failed to add border: %v", err)
}
log.Printf("Processed image: %dx%d\n", image.Width(), image.Height())
// Save the result as WebP file with options
err = image.Webpsave("resized-gopher.webp", &vips.WebpsaveOptions{
Q: 85, // Quality factor (0-100)
Effort: 4, // Compression effort (0-6)
SmartSubsample: true, // Better chroma subsampling
})
if err != nil {
log.Fatalf("Failed to save image as WebP: %v", err)
}
log.Println("Successfully saved processed images")
}
Code generation requires libvips to be built with GObject introspection support.
go install github.com/cshum/vipsgen/cmd/vipsgen@latest
Generate the bindings:
vipsgen -out ./vips
Use your custom-generated code:
package main
import (
"yourproject/vips"
)
Usage: vipsgen [options]
Options:
-out string Output directory (default "./out")
-templates string Template directory (uses embedded templates if not specified)
-extract Extract embedded templates and exit
-extract-dir string Directory to extract templates to (default "./templates")
-debug Enable debug json output
The generation process involves multiple layers to provide a type-safe, idiomatic Go API:
-
Introspection Analysis: vipsgen uses GObject introspection to analyze the libvips API, extracting operation metadata, argument types, and enum definitions.
-
Multi-Layer Generation: To create type-safe, idiomatic Go APIs from libvips dynamic parameter system, vipsgen creates a layered approach that handles both required and optional parameters.
-
Type-Safe Bindings: The generated code is fully type-safe with proper Go types, structs, and enums based on centralized introspection data.
┌─────────────────────────────────────────────────────────────┐
│ Go Method Layer │
│ • Methods on *Image struct │
│ • Go enums and structs │
│ • Options structs for optional parameters │
│ • Type conversions (Go <-> C) │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ Go Binding Layer │
│ • vipsgenAbc() - required parameters only │
│ • vipsgenAbcWithOptions() - with optional parameters │
│ • C array handling and memory management │
│ • String conversions and cleanup │
│ • Error handling and resource management │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ C Layer │
│ • vipsgen_abc() - required args only │
│ • vipsgen_abc_with_options() - all parameters │
│ • VipsOperation dynamic dispatch │
│ • Proper VipsArray creation and cleanup │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────┐
│ libvips │
│ • vips_abc() - original variadic functions │
│ • VipsOperation object system │
│ • GObject introspection metadata │
└─────────────────────────────────────────────────────────────┘
1. C Layer (vips.c/vips.h)
Problem: libvips dynamic parameter system with variadic functions like vips_resize(in, &out, scale, "kernel", kernel, ...)
does not translate well to type-safe, idiomatic Go APIs.
Solution: Generate two types of C wrapper functions:
// Basic function - required arguments only, calls vips_resize directly
int vipsgen_resize(VipsImage* in, VipsImage** out, double scale) {
return vips_resize(in, out, scale, NULL);
}
// With options - uses VipsOperation for optional parameters
int vipsgen_resize_with_options(VipsImage* in, VipsImage** out, double scale,
VipsKernel kernel, double gap, double vscale) {
VipsOperation *operation = vips_operation_new("resize");
if (!operation) return 1;
if (
vips_object_set(VIPS_OBJECT(operation), "in", in, NULL) ||
vips_object_set(VIPS_OBJECT(operation), "scale", scale, NULL) ||
vipsgen_set_int(operation, "kernel", kernel) ||
vipsgen_set_double(operation, "gap", gap) ||
vipsgen_set_double(operation, "vscale", vscale)
) {
g_object_unref(operation);
return 1;
}
int result = vipsgen_operation_execute(operation, "out", out, NULL);
return result;
}
This layer handles VipsArray creation/cleanup, VipsOperation lifecycle management, type-specific setters.
2. Go Binding Layer (vips.go)
Problem: C arrays, string management, and complex type conversions.
Solution: Generate Go wrapper functions that handle CGO complexity:
// vipsgenResize vips_resize resize an image
func vipsgenResize(in *C.VipsImage, scale float64) (*C.VipsImage, error) {
var out *C.VipsImage
if err := C.vipsgen_resize(in, &out, C.double(scale)); err != 0 {
return nil, handleImageError(out)
}
return out, nil
}
// vipsgenResizeWithOptions vips_resize resize an image with optional arguments
func vipsgenResizeWithOptions(in *C.VipsImage, scale float64, kernel Kernel,
gap float64, vscale float64) (*C.VipsImage, error) {
var out *C.VipsImage
if err := C.vipsgen_resize_with_options(in, &out, C.double(scale),
C.VipsKernel(kernel), C.double(gap),
C.double(vscale)); err != 0 {
return nil, handleImageError(out)
}
return out, nil
}
This layer handles C array conversion, string conversion with cleanup, memory management, error handling, and type conversion between Go and C.
3. Go Method Layer (image.go)
Problem: Provide idiomatic Go API with proper encapsulation.
Solution: Generate methods on *Image
struct that encapsulate the two-function approach with options pattern:
// ResizeOptions optional arguments for vips_resize
type ResizeOptions struct {
// Kernel Resampling kernel
Kernel Kernel
// Gap Reducing gap
Gap float64
// Vscale Vertical scale image by this factor
Vscale float64
}
// DefaultResizeOptions creates default value for vips_resize optional arguments
func DefaultResizeOptions() *ResizeOptions {
return &ResizeOptions{
Kernel: Kernel(5),
Gap: 2,
}
}
// Resize vips_resize resize an image
func (r *Image) Resize(scale float64, options *ResizeOptions) error {
if options != nil {
// Use the WithOptions variant when options are provided
out, err := vipsgenResizeWithOptions(r.image, scale,
options.Kernel, options.Gap, options.Vscale)
if err != nil {
return err
}
r.setImage(out)
return nil
}
// Use the basic variant for required parameters only
out, err := vipsgenResize(r.image, scale)
if err != nil {
return err
}
r.setImage(out)
return nil
}
This layer provides idiomatic Go methods, options structs for optional parameters, Go type system integration.
Contributions are welcome! Please feel free to submit a Pull Request.
When contributing to vipsgen, do not commit the generated code in the vips/
directory (except vips/vips_test.go
). The development workflow is designed to keep generated code separate from source code. The repository uses GitHub Actions to automatically handle code generation when PRs are created.
MIT