pixelize

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: May 28, 2026 License: MIT Imports: 20 Imported by: 0

README

pixelize

Resize images and quantize their colors to any palette. Library + CLI in Go.

Use cases

  • Fit a photo into a 64x64 pixel-art editor that rejects larger uploads.
  • Reduce a sprite to a fixed palette (NES, Game Boy, PICO-8, your own).
  • Generate a build map and piece count for a physical lego / perler / cross-stitch mosaic.

Install

go install github.com/noelruault/pixelize/cmd/pixelize@latest

Quick start

pixelize photo.jpg -size 64x64 -palette nes -o photo_nes.png
pixelize sprite.png -palette ./my-palette.csv -o sprite_reduced.png
pixelize portrait.jpg -size 48x48 -palette lego -build-map mosaic.txt -pieces parts.csv -o mosaic.png

Palettes

Palettes are CSV (or HEX / GPL / JSON) files. A few examples ship in palettes/ to demonstrate the format and bootstrap new users; anything else lives in your own files.

pixelize palettes              # list resolvable palettes (yours + shipped examples)
pixelize palettes init         # copy shipped examples to $XDG_CONFIG_HOME/pixelize/palettes/
pixelize palette nes -show nes.png   # render a palette as a swatch PNG

Authoring a palette: see palettes/README.md for the file formats.

Status

In development. v0.1 not yet released.

License

See LICENSE.

Documentation

Overview

Package pixelize resizes images and quantizes their colors to a palette.

Two layers:

  • Palette[M] models an ordered set of color entries with arbitrary typed metadata M (name, ID, hex, whatever a domain needs).
  • Apply takes an image and returns a Pattern[M]: the quantized image plus the palette index per pixel and a usage histogram.

Distance defaults to stdlib unweighted Euclidean (image/color.Palette.Index). Callers can plug in a custom DistanceFunc.

Index

Constants

This section is empty.

Variables

View Source
var Version = "dev"

Version is set at build time via -ldflags.

Functions

func EncodeAnimatedGIF

func EncodeAnimatedGIF(w io.Writer, frames []*image.RGBA, delaysCS []int, loopCount int) error

EncodeAnimatedGIF writes frames as an animated GIF preserving per- frame delays and a loop count. Use this when source frames carry their own timing (e.g. converted from another animated GIF). DelaysCS values are centiseconds, matching gif.GIF.Delay.

func EncodeGIF

func EncodeGIF(w io.Writer, frames []*image.RGBA, opts GIFOptions) error

EncodeGIF writes the frames as an animated GIF. Each frame is re-paletted against its own colors via image.NewPaletted so the caller does not need to pre-quantize. Output size matches the first frame's bounds; subsequent frames are clipped to that rectangle.

func EuclideanRGBA

func EuclideanRGBA(a, b color.Color) float64

EuclideanRGBA is the stdlib-equivalent unweighted Euclidean distance in 16-bit RGBA space.

func RunBatch

func RunBatch(ctx context.Context, jobs []BatchJob, opts BatchOptions, process func(context.Context, BatchJob) error) error

RunBatch executes process(job) for every job concurrently, with at most opts.Workers goroutines. Returns the first error if any worker fails; the rest are canceled.

func Watch

func Watch(ctx context.Context, path string, onChange func() error) error

Watch invokes onChange every time `path` is modified, until ctx is canceled. Coalesces bursts of events with a small debounce.

Watches the parent directory and filters for the basename so editors that swap files atomically (write-then-rename) still trigger.

func WriteBuildMap

func WriteBuildMap[M any](w io.Writer, p *Pattern[M]) error

WriteBuildMap writes a per-pixel build map suitable for assembling physical mosaics. One line per pixel:

[x][y] = R:r, G:g, B:b   -name

Works with any Pattern whose metadata implements Named (else the name field is left blank).

func WritePiecesCSV

func WritePiecesCSV[M any](w io.Writer, p *Pattern[M]) error

WritePiecesCSV writes a pieces summary as CSV, sorted by count desc. Columns: id, name, hex, count. Empty cells when metadata fields are absent.

Types

type ApplyOptions

type ApplyOptions struct {
	// Width and Height set the target dimensions. Zero means no resize.
	// If only one is zero, it is derived from the other to preserve the
	// aspect ratio.
	Width, Height int

	// Resize selects the resize algorithm. Default NearestNeighbor.
	// Ignored when Width and Height are both zero.
	Resize ResizeMode

	// Dither enables Floyd-Steinberg dithering when quantizing.
	Dither bool

	// Distance overrides the nearest-color metric. nil means stdlib
	// unweighted Euclidean (color.Palette.Index).
	Distance DistanceFunc
}

ApplyOptions configures palette application.

type BatchJob

type BatchJob struct {
	InputPath  string
	OutputPath string
}

BatchJob is one item in a batch run.

func CollectJobs

func CollectJobs(inDir, outDir, outExt string, opts BatchOptions) ([]BatchJob, error)

CollectJobs walks inDir and returns jobs that pair each accepted input file with an output path derived as outDir/<basename>.<outExt>.

type BatchOptions

type BatchOptions struct {
	// Workers is the number of concurrent jobs. Default runtime.NumCPU.
	// Zero or negative uses the default.
	Workers int

	// Extensions filters input files by extension (lowercase, with dot).
	// Default: .png, .jpg, .jpeg, .gif, .webp, .jxl.
	Extensions []string
}

BatchOptions controls Batch concurrency and file selection.

type Bucket

type Bucket[M any] struct {
	Index int      `json:"index"`
	Entry Entry[M] `json:"entry"`
	Count int      `json:"count"`
}

Bucket is one entry of a histogram.

type DistanceFunc

type DistanceFunc func(a, b color.Color) float64

DistanceFunc returns a distance metric between two colors. Smaller means more similar. Absolute scale does not matter, only relative ordering.

The default implementation is stdlib unweighted Euclidean (via color.Palette.Index). Callers that need perceptual accuracy can plug in CIEDE2000 over CIE Lab.

type Entry

type Entry[M any] struct {
	R, G, B uint8
	Meta    M
}

Entry is one palette color plus arbitrary typed metadata.

M is the metadata type chosen by the consumer: a string name, a rich struct (e.g. LegoColor with brick id), or struct{} when none is needed.

func (Entry[M]) Color

func (e Entry[M]) Color() color.RGBA

Color returns the entry as a stdlib color.RGBA with full opacity.

type EntryMeta

type EntryMeta struct {
	ID   string            `json:"id,omitempty"`
	Name string            `json:"name,omitempty"`
	Hex  string            `json:"hex,omitempty"`
	More map[string]string `json:"more,omitempty"`
}

EntryMeta is the metadata struct used by the CLI loaders. It is generic-friendly: any field can be empty.

func (EntryMeta) GetHex

func (m EntryMeta) GetHex() string

GetHex satisfies Hexed.

func (EntryMeta) GetID

func (m EntryMeta) GetID() string

GetID satisfies Identified.

func (EntryMeta) GetName

func (m EntryMeta) GetName() string

GetName satisfies Named.

type GIFOptions

type GIFOptions struct {
	// DelayMS is the per-frame delay in milliseconds. GIF resolution
	// is 10ms, so values are floored to the nearest centisecond.
	// Default 200ms.
	DelayMS int

	// Loop controls frame ordering.
	Loop LoopMode

	// LoopCount sets the GIF loop count (0 = infinite).
	LoopCount int
}

GIFOptions configures EncodeGIF.

type Hexed

type Hexed interface {
	GetHex() string
}

Hexed exposes the canonical hex string.

type Identified

type Identified interface {
	GetID() string
}

Identified is implemented by metadata types that carry an ID (legoid, brick-link sku, dmc thread number, ...).

type LoopMode

type LoopMode int

LoopMode controls how progressive GIF frames repeat.

const (
	// LoopNone plays the frames once in order.
	LoopNone LoopMode = iota

	// LoopReverse reverses the frame order (no forward play).
	LoopReverse

	// LoopFull plays forward then reverse (smooth back-and-forth).
	LoopFull
)

type Named

type Named interface {
	GetName() string
}

Named is implemented by metadata types that carry a human-readable name. EntryMeta satisfies this.

type Palette

type Palette[M any] []Entry[M]

Palette is an ordered set of entries. Indices into the slice are the canonical reference for results: Pattern.Indices stores them.

func LoadCSV

func LoadCSV(r io.Reader) (Palette[EntryMeta], error)

LoadCSV reads a header-driven CSV palette. Recognized columns: hex, name, id, r, g, b. Order is free. Either "hex" or all of "r","g","b" must be present.

No comment syntax: "#" is reserved for hex literals like "#FF0000". Use HEX format if you need inline comments.

func LoadFile

func LoadFile(path string) (Palette[EntryMeta], error)

LoadFile dispatches to the right loader based on file extension. Supports .csv, .hex, .gpl, .json.

func LoadGPL

func LoadGPL(r io.Reader) (Palette[EntryMeta], error)

LoadGPL reads a GIMP palette. Format:

GIMP Palette
Name: ...
Columns: N
#
R   G   B   Name
...

func LoadHEX

func LoadHEX(r io.Reader) (Palette[EntryMeta], error)

LoadHEX reads one #rrggbb (or rrggbb) per line. Comments start with # in column zero or after whitespace; a hex line may not start with # the same way, but we tell them apart by length: 6 or 7 chars => color.

func LoadJSON

func LoadJSON(r io.Reader) (Palette[EntryMeta], error)

LoadJSON reads the structured JSON palette format documented in palettes/README.md.

func (Palette[M]) Apply

func (p Palette[M]) Apply(ctx context.Context, img image.Image, opts ApplyOptions) (*Pattern[M], error)

Apply resizes the image (if requested) and quantizes it against p. Returns a Pattern indexed against p.

func (Palette[M]) ColorPalette

func (p Palette[M]) ColorPalette() color.Palette

ColorPalette converts to stdlib color.Palette for use with stdlib drawing routines (color.Palette.Index, image.NewPaletted, etc.).

func (Palette[M]) Swatch

func (p Palette[M]) Swatch(opts SwatchOptions) *image.RGBA

Swatch returns an image displaying the palette as a grid of color squares. Useful for inspecting a palette before applying it.

type Pattern

type Pattern[M any] struct {
	// Image is the quantized output. Every pixel matches an entry in Palette.
	Image *image.RGBA

	// Palette is the palette used to produce Image. Held for downstream
	// helpers (Histogram, Dominant, build-map writers).
	Palette Palette[M]

	// Indices[x][y] is the palette index assigned to pixel (x, y).
	Indices [][]int

	// Usage maps palette index to pixel count.
	Usage map[int]int
}

Pattern is the result of Apply.

func (*Pattern[M]) Dominant

func (p *Pattern[M]) Dominant(n int) []Entry[M]

Dominant returns the top n entries by usage count.

func (*Pattern[M]) Histogram

func (p *Pattern[M]) Histogram() []Bucket[M]

Histogram returns palette indices sorted by usage count (descending).

func (*Pattern[M]) Stats

func (p *Pattern[M]) Stats() StatsJSON[M]

Stats builds the JSON-friendly summary for a Pattern.

func (*Pattern[M]) UniqueColors

func (p *Pattern[M]) UniqueColors() int

UniqueColors returns the number of palette entries actually used.

func (*Pattern[M]) WriteStatsJSON

func (p *Pattern[M]) WriteStatsJSON(w io.Writer) error

WriteStatsJSON marshals the stats summary to w.

type ResizeMode

type ResizeMode int

ResizeMode selects the resize algorithm used by Apply.

const (
	// NearestNeighbor preserves blocky edges. Default for pixel art.
	NearestNeighbor ResizeMode = iota

	// BlockAverage averages every NxN source pixel block into one
	// destination pixel. Useful as a smoothing pre-pass before
	// quantizing photographic input. Only works when source dimensions
	// are integer multiples of the destination dimensions; otherwise
	// falls back to NearestNeighbor for the remainder.
	BlockAverage

	// BiLinear is golang.org/x/image/draw.BiLinear. Smooth.
	BiLinear

	// CatmullRom is golang.org/x/image/draw.CatmullRom. Sharpest of the
	// smooth resamplers.
	CatmullRom
)

type StatsJSON

type StatsJSON[M any] struct {
	Width        int         `json:"width"`
	Height       int         `json:"height"`
	TotalPixels  int         `json:"total_pixels"`
	UniqueColors int         `json:"unique_colors"`
	PaletteSize  int         `json:"palette_size"`
	Histogram    []Bucket[M] `json:"histogram"`
}

StatsJSON is the machine-readable stats summary.

type SwatchOptions

type SwatchOptions struct {
	// CellSize is the side length of one color square in pixels.
	// Default 32.
	CellSize int

	// Columns sets how many cells per row. Default 8.
	// If 0 or larger than len(palette), all entries fit on one row.
	Columns int
}

SwatchOptions configures palette swatch rendering.

Directories

Path Synopsis
cmd
pixelize command
Command pixelize resizes images and quantizes their colors to a palette.
Command pixelize resizes images and quantizes their colors to a palette.
Package decode wraps image.Decode and registers extra formats (WebP, JXL) so the caller can hand off any supported file.
Package decode wraps image.Decode and registers extra formats (WebP, JXL) so the caller can hand off any supported file.
Package palettes ships example palettes and resolves palette names against the user's config dir, then the embedded examples.
Package palettes ships example palettes and resolves palette names against the user's config dir, then the embedded examples.
Package preview renders an image to the terminal using whichever graphics protocol the terminal supports.
Package preview renders an image to the terminal using whichever graphics protocol the terminal supports.

Jump to

Keyboard shortcuts

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