Imaging image provider
Pure-Go image transformer for development and tests, backed by disintegration/imaging and HugoSmits86/nativewebp. No CGO, no system libraries.
Overview
The imaging provider implements media.ImageTransformerPort using only Go libraries. It uses disintegration/imaging for decode, resize, and colour adjustments, and HugoSmits86/nativewebp for WebP encoding. It encodes three output formats: JPEG, PNG, and WebP. WebP output is lossless (VP8L) only. For lossy WebP or AVIF, use vips. The provider decodes GIF input but cannot encode GIF.
The modifier set covers greyscale, blur, sharpen, rotate, flip, brightness, contrast, and saturation. Advanced modifiers such as hue, tint, gravity, and radius live in vips.
The provider plugs in as a single value that implements one port, with no glue. NewProvider returns *Provider directly with no error path, and a compile-time assertion guarantees it satisfies the image port. It reuses the shared piko image machinery. It runs the same media.TransformationSpec, the same image_domain checks for spec, format, dimensions, pixel count, and decompression-bomb peek, and the same media.ImageServiceConfig security limits as every other provider.
This provider targets development and testing, and emits a runtime warning to that effect on first construction. The warning fires once per process through a sync.Once, not once per provider. The pure-Go WebP encoder uses 100 to 500 MB of transient buffer memory per request. To prevent memory exhaustion under load, the provider caps concurrent transforms at min(2, runtime.NumCPU()). Transforms past the cap block on a semaphore, and the provider cancels a queued request when its context ends. With the default 30-second TransformTimeout, a queued request can time out before it runs. Use vips for production. It offers lower memory, lossy WebP, AVIF, and demand-driven streaming.
The pure-Go versus cgo choice is the takeaway here. Imaging carries no build tag and compiles wherever go build works, so a single static binary ships everywhere. vips links against libvips at build and run time, which rules out static-binary distribution but unlocks the wider performance and format envelope. Reach for imaging when you build locally or run in a container that cannot assume libvips.
Configuration
import (
"piko.sh/piko/wdk/media"
"piko.sh/piko/wdk/media/image_provider_imaging"
)
provider := image_provider_imaging.NewProvider(image_provider_imaging.Config{
ImageServiceConfig: media.DefaultImageServiceConfig(), // optional; falls back to defaults if zero
})
Config embeds media.ImageServiceConfig, which holds the max file size, dimension limits, and allowed formats. The constructor returns *Provider directly with no error path. Per-request quality comes from the media.TransformationSpec, not from the constructor.
The default allowed-format list includes avif and gif. That list gates accepted input. This provider cannot encode either format, so a request that targets avif or gif output passes format validation and then fails at the encode step.
Bootstrap
ssr := piko.New(
piko.WithImageProvider("imaging", provider),
piko.WithDefaultImageProvider("imaging"),
)
The imaging provider needs no build tag, no CGO, and no system libraries. It defines no Close step, so registration is the whole wiring. The provider runs in interpreted dev mode (dev-i) and in compiled builds unchanged. vips, by contrast, needs the vips build tag, libvips, and a Close call at shutdown.
For a fuller image-service setup with named variants and security limits, use piko.WithImage(media.Image()...) instead.
See also
Other media providers:
- vips, production-grade libvips-backed image transformer, lower memory and more formats.
- astiav, FFmpeg-based video transcoding.
Framework docs:
- How to do image processing, wiring an image provider end-to-end.
- Media API reference, every type and method on the image and video services.
External:
- disintegration/imaging, the underlying image-manipulation library.
- HugoSmits86/nativewebp, the pure-Go WebP encoder used here.