impasto

module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 18, 2026 License: MIT

README

impasto

impasto, a pure-Go library for Photoshop-style layered image compositing

CI Go Reference

A pure-Go library for Photoshop-style layered image compositing: blend modes, masks, vector paths, gradients, blur, and non-destructive layer effects. Intentionally built with CGO_ENABLED=0 and no dependencies outside the standard library.

This is just a library and not a full application, so there is no CLI, and no opinion about file formats beyond decoding into and encoding out of image.Image. This way, a poster renderer, a game's UI layer, or a template service should all find it equally usable.

Why

Go has pieces of this already. image/draw does Porter-Duff compositing, x/image/vector fills paths. Neither does Photoshop's blend math, layer effects, or dithered gradients, and the alternatives all mean CGO bindings to Skia or Cairo.

There are definitely better alternatives outside Go, but I'm stubborn and wanted to try filling out that missing middle piece that Go didn't seem to have.

Design

Compositing happens in linear light, premultiplied, at float32. Source pixels decode from sRGB on ingest and re-encode on output, so compositing always happens in linear space.

Rendering is deterministic. The same input gives byte-identical output every run. Parallel work is split into fixed row bands rather than scheduled dynamically.

Expensive operations are concurrent. Splits across GOMAXPROCS goroutines.

Inputs are immutable. Effects and masks work on copies, so a layer can be re-composited with different settings without its content changing.

Effects are declarative. A layer effect is a parameter struct rather than an imperative call, and the renderer decides how and when to rasterize it.

Packages

Lower layers never import higher ones, so each package works on its own.

Package Responsibility
raster The Buffer type, sRGB/linear conversion, 8/16-bit image ingest and egress with ordered dithering
blend The blend-mode formulas from ISO 32000-2, per-pixel and buffer-level
mask Raster and vector masks, coverage composition
path Bezier construction, anti-aliased fill (nonzero/even-odd), and stroking (joins, caps, dashes)
gradient Linear, radial, angle, reflected, and diamond gradients with per-stop opacity
blur 3-pass box blur as a separable Gaussian approximation
effects The declarative layer styles
canvas Layer stack, groups, and Render

Most callers import only canvas.

Install

go get github.com/odevine/impasto

Requires Go 1.24+.

Quickstart

doc := &canvas.Document{
    Width: 200, Height: 120,
    Root: canvas.Group{
        PassThrough: true, Opacity: 1,
        Layers: []canvas.Node{
            &canvas.Layer{Content: bg, Opacity: 1, Mode: blend.Normal},
            &canvas.Layer{
                Content: logo,
                Opacity: 1,
                Mask:    mask.NewVectorMask(shape, 200, 120, path.NonZero),
                Effects: []effects.Effect{
                    &effects.DropShadow{Color: color.Black, Opacity: 0.75,
                        Angle: 2.36, Distance: 12, BlurRadius: 18, Mode: blend.Multiply},
                    &effects.Stroke{Width: 4, Color: color.White,
                        Alignment: effects.StrokeOutside},
                },
            },
        },
    },
}

out := canvas.MustRender(doc)
png.Encode(w, out.ToImage(8))

Layer content is document-sized. Place a smaller image with canvas.Place.

Documentation

The API reference lives on pkg.go.dev, with runnable examples attached to the symbols that need them.

The docs/ directory covers things beyond just references. Things like how the pieces all fit together, why the model works the way it does, and when the obvious thing might be the wrong thing.

Guide Covers
raster The color model, Buffer, ingest and egress, dithering
blend The 16 modes, what each is for, the compositing formula
mask Raster, vector, procedural, and combined coverage
path Bezier construction, fill rules, anti-aliasing, stroking
gradient The five geometries, stops, opacity, spread modes
blur The three-box Gaussian approximation, sigma vs radius
effects The eight layer styles, their parameters, stacking order
canvas Documents, layers, groups, clipping, rendering

New to the library? Read canvas, then raster.

Examples

The examples/ directory has runnable programs that render PNGs showing off each part of the library. See examples/README.md for a walk-through of every mode, effect, and gradient with the image beside it.

Showcase poster

Program Renders
blendmodes All 16 blend modes as a chart
gradients The five gradient types plus a transparency fade
effects The eight layer styles on a badge
paths Stroke caps, joins, dashes, fill rules, and beziers
showcase A poster combining gradients, masks, groups, and every effect
banner The README banner at the top of this page

Run one with go run ./examples/<name>, or regenerate all of them with make examples.

Text

This library does not shape text. That's something that a package like go-text/typesetting does very well already, so the recommendation is to rasterize glyph outlines yourself, pass them into the path package, and hand the resulting buffer to canvas as an ordinary layer.

Nothing below canvas needs to know a layer's pixels came from text, so rasterized text gets shadows and strokes like anything else.

Testing

Blend modes are checked against the ISO formulas computed independently, per channel, across a grid that includes the discontinuity points. Path coverage is validated by area conservation and exact partial-pixel coverage. Golden-image regression runs over a full document, regenerated with go test ./canvas -update.

Fuzzing covers the path rasterizer, stroker, and gradient sampling, which is where untrusted or degenerate input first reaches numeric code:

go test ./path -fuzz=FuzzRasterize

Benchmarks track blend throughput, blur throughput, and full-document render:

go test ./... -bench=.

Development

A Makefile wraps the common tasks, and make ci runs exactly what CI runs:

make ci       # gofmt check, vet, build, race tests
make test     # plain test suite
make cover    # coverage summary
make fuzz     # short fuzz smoke run
make examples # regenerate the example images
make hooks    # enable the pre-commit hook (fmt + vet + test)

CI runs on every push and pull request: a test matrix on the minimum and current Go versions, govulncheck, and a fuzz smoke run. Releases are automated with release-please. Commits follow Conventional Commits, and merging the release PR tags a semver version and publishes a GitHub Release.

Status

Seven of the eight effects are built to spec-level accuracy. Bevel and emboss is close-and-configurable rather than pixel-exact, by design: it is the one effect with no clean public spec, so it derives a normal map from the layer's alpha and applies a directional lighting model. Expect it to keep iterating against real reference renders.

Non-goals (v1)

No font shaping, no CMYK or print color management, no GPU backend, no animation.

License

MIT

Directories

Path Synopsis
Package blend implements the Photoshop blend modes as pure functions.
Package blend implements the Photoshop blend modes as pure functions.
Package blur provides a fast separable blur used as a primitive by the effects package.
Package blur provides a fast separable blur used as a primitive by the effects package.
Package canvas is the orchestration layer most callers import.
Package canvas is the orchestration layer most callers import.
Package effects implements Photoshop-style layer styles as declarative data.
Package effects implements Photoshop-style layer styles as declarative data.
examples
banner command
Command banner renders the project banner shown at the top of the README.
Command banner renders the project banner shown at the top of the README.
blendmodes command
Command blendmodes renders a 4x4 chart of every blend mode, each cell blending a vertical color ramp over a horizontal rainbow so the modes' behavior is visible at a glance.
Command blendmodes renders a 4x4 chart of every blend mode, each cell blending a vertical color ramp over a horizontal rainbow so the modes' behavior is visible at a glance.
effects command
Command effects renders each of the eight layer styles on a rounded-rectangle badge, in a 4x2 grid.
Command effects renders each of the eight layer styles on a rounded-rectangle badge, in a 4x2 grid.
gradients command
Command gradients renders the five gradient types plus a per-stop opacity fade shown over a checkerboard, in a 3x2 grid.
Command gradients renders the five gradient types plus a per-stop opacity fade shown over a checkerboard, in a 3x2 grid.
internal/demo
Package demo holds helpers shared by the example programs: saving buffers to PNG, building solid and checkerboard content, and a few handy path shapes.
Package demo holds helpers shared by the example programs: saving buffers to PNG, building solid and checkerboard content, and a few handy path shapes.
paths command
Command paths renders stroke caps, stroke joins, dashes, the two fill rules, and a stroked bezier, all drawn directly onto one buffer.
Command paths renders stroke caps, stroke joins, dashes, the two fill rules, and a stroked bezier, all drawn directly onto one buffer.
showcase command
Command showcase composes a single poster that combines gradients, masks, groups, blend modes, and the full set of layer effects into one scene.
Command showcase composes a single poster that combines gradients, masks, groups, blend modes, and the full set of layer effects into one scene.
Package gradient generates linear, radial, angle, reflected, and diamond gradients with multi-stop color and independent per-stop opacity.
Package gradient generates linear, radial, angle, reflected, and diamond gradients with multi-stop color and independent per-stop opacity.
internal
parallel
Package parallel splits row-oriented pixel work across goroutines.
Package parallel splits row-oriented pixel work across goroutines.
Package mask defines coverage sources that scale a layer's alpha before it is composited.
Package mask defines coverage sources that scale a layer's alpha before it is composited.
Package path builds bezier paths and rasterizes them with anti-aliased scanline coverage.
Package path builds bezier paths and rasterizes them with anti-aliased scanline coverage.
Package raster is the substrate every other package builds on.
Package raster is the substrate every other package builds on.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL