Documentation
¶
Overview ¶
Package blazon turns a version number into a visual seal.
The pipeline has three stages. Entropy is derived from the version in separated streams (Seed, Params) so that family resemblance between neighbouring versions is a policy choice rather than an accident. A Renderer maps those streams onto its own parameters — this is the stage that decides whether the result is useful. A canvas.Canvas turns drawing primitives into SVG, pixels or terminal cells — this stage decides only how it looks.
Distinguishability is not a promise here but a test: see the blazontest package, which measures perceptual distance across a version corpus and fails when neighbouring versions render too much alike.
Index ¶
- Constants
- Variables
- func EncodePNG(img image.Image) ([]byte, error)
- func Image(version string, o Options) (*image.RGBA, error)
- func ImageWith(r Renderer, version string, o Options) (*image.RGBA, error)
- func MarkPrerelease(ctx *Context)
- func MarkPrereleaseCells(ctx *Context, cols, row int)
- func PNG(version string, o Options) ([]byte, error)
- func Register(r Renderer)
- func Renderers() []string
- func SVG(version string, o Options) ([]byte, error)
- func SVGWith(r Renderer, version string, o Options) ([]byte, error)
- func Text(version string, o Options) (string, error)
- func TextWith(r Renderer, version string, o Options) (string, error)
- type Background
- type BitReader
- func (b *BitReader) Bit() uint64
- func (b *BitReader) Bool() bool
- func (b *BitReader) Choice(n int) int
- func (b *BitReader) Derive(tag string) *BitReader
- func (b *BitReader) Float01() float64
- func (b *BitReader) IntRange(lo, hi int) int
- func (b *BitReader) LogRange(lo, hi float64) float64
- func (b *BitReader) Range(lo, hi float64) float64
- func (b *BitReader) Sign() float64
- func (b *BitReader) Uint(n uint) uint64
- type Box
- type Caps
- type Context
- type Options
- type Palette
- type PaletteMode
- type Params
- func (p *Params) Amplified() *BitReader
- func (p *Params) Archetype() *BitReader
- func (p *Params) Detail() *BitReader
- func (p *Params) PaletteBase() *BitReader
- func (p *Params) PaletteShift() *BitReader
- func (p *Params) Pre() *BitReader
- func (p *Params) Reset()
- func (p *Params) Variant() *BitReader
- func (p *Params) Version() Version
- type Policy
- type Renderer
- type Seed
- type Version
Constants ¶
const DefaultRenderer = "superformula"
DefaultRenderer is used when Options.Renderer is empty.
const LogoRenderer = "flowfield"
LogoRenderer draws the mark blazon uses for itself.
const Release = "0.2.0"
Release is this library's own version.
It is here because blazon signs its own releases with its own marks: the logo in the README is the flowfield mark for this exact version, and the release workflow refuses a tag that does not match. Bump it in the same commit as the tag.
Variables ¶
var ( // ErrEmptyVersion is returned when the input contains no version at all. ErrEmptyVersion = errors.New("blazon: empty version string") // ErrSyntax is returned when the input is not a valid semantic version. ErrSyntax = errors.New("blazon: invalid semantic version") )
Functions ¶
func EncodePNG ¶
EncodePNG encodes an image as PNG.
The encoder is the library's own, for the same reason the rasteriser and the font are: it is the last stage that decides what bytes a version turns into, and importing image/png would hand that decision — and about a fifth of a megabyte of binary — to something outside this repository.
The error is retained in the signature because callers already handle one and encoding is where a future format could fail; today it is always nil.
func MarkPrerelease ¶
func MarkPrerelease(ctx *Context)
MarkPrerelease draws the prerelease marker: a short row of dots along the bottom edge of the drawing box, their count and spacing taken from the prerelease entropy stream.
The marker is deliberately small. A prerelease must still read as the release it precedes — 1.0.0-rc.1 is a 1.0.0 — so it may not disturb the main shape. But it must exist: without it, 1.0.0 and 1.0.0-rc.1 would render to identical bytes, and two different versions sharing one mark is the one thing an identicon may never do.
It lives here rather than in each renderer so that the rule is the same everywhere and a new renderer gets it by calling one function.
func MarkPrereleaseCells ¶
MarkPrereleaseCells is the character-grid form of the marker: a run of dots in a row the renderer has reserved for it. Text renderers call this instead of MarkPrerelease, which draws vector geometry a character canvas would have to rasterise.
The row must be reserved rather than borrowed. Overwriting cells the renderer has already drawn would hide part of the mark itself, and a marker that destroys information is worse than no marker.
func Register ¶
func Register(r Renderer)
Register adds a renderer under its own name. It panics on a duplicate name, because two renderers sharing a name would make golden files and gallery output ambiguous. Renderers in this package register themselves; Register is exported so that callers can add their own and run them through blazontest.
func Renderers ¶
func Renderers() []string
Renderers lists the registered renderer names in sorted order.
func SVGWith ¶
SVGWith renders with an explicit renderer, bypassing the registry. It is how blazontest exercises a renderer that a caller has written but not registered.
func Text ¶
Text renders a version as a block of terminal output.
Half-block characters give each cell two square pixels, so vector renderers arrive in the terminal as pictures rather than as ASCII art. Renderers that declare their own character grid — the randomart field — write glyphs directly instead.
Types ¶
type Background ¶
type Background int
Background selects the ground the mark is drawn on.
const ( // BackgroundTransparent leaves the ground unpainted. Ink lightness is // held mid-band so the mark reads on light and dark surfaces alike. BackgroundTransparent Background = iota BackgroundLight BackgroundDark )
type BitReader ¶
type BitReader struct {
// contains filtered or unexported fields
}
BitReader is a deterministic, unlimited bit stream derived from a 32-byte hash. Bits are consumed most-significant-first from successive bytes; when the current block is exhausted it is extended by hashing the root together with a counter, so a renderer never has to budget its bits.
A BitReader is not safe for concurrent use.
func NewBitReader ¶
NewBitReader starts a stream at the given root hash.
func (*BitReader) Derive ¶
Derive returns an independent stream from the same root, separated by tag. Use it when a renderer needs a fresh draw that must stay deterministic, such as resampling a degenerate parameter set.
func (*BitReader) IntRange ¶
IntRange returns an integer in [lo,hi] inclusive. It uses rejection sampling so the result is unbiased regardless of range width.
func (*BitReader) LogRange ¶
LogRange returns a value in [lo,hi) distributed uniformly on a logarithmic scale. Both bounds must be positive. Shape parameters whose visual effect is multiplicative — the superformula exponents, for instance — must be drawn this way, otherwise nearly every sample lands in the large-value regime and the shapes collapse onto one another.
type Box ¶
type Box struct{ X, Y, W, H float64 }
Box is the drawing area available to a renderer, in canvas units, after padding has been applied.
type Caps ¶
type Caps struct {
// TextOnly means the renderer draws exclusively through Canvas.Cell.
// Vector back ends still render it, via a monospace font, but the
// terminal is its native target.
TextOnly bool
// GridCols and GridRows are the character grid a TextOnly renderer needs.
GridCols, GridRows int
// ByteExact means the renderer's geometry is derived from integer
// arithmetic only, so its output can be compared against golden files
// byte for byte. Renderers using transcendental functions cannot promise
// this: math.Sin and math.Pow may differ by an ulp between architectures,
// so those are compared by perceptual hash with a tolerance instead.
ByteExact bool
// Ordered means the mark encodes the version legibly rather than
// hashing it. Such renderers are exempt from the distinctness thresholds,
// because being systematic is the point; they are checked for injectivity
// instead.
Ordered bool
}
Caps describes what a renderer can and cannot do. The registry and the test suite consult it instead of special-casing renderers by name.
type Context ¶
Context is everything a renderer is given. Palette and Box are resolved centrally so that colour policy and padding stay in one place instead of being reimplemented by every renderer.
type Options ¶
type Options struct {
// Renderer names a registered renderer; empty means DefaultRenderer.
Renderer string
// Size is the edge length in pixels or user units; zero means 512.
Size int
// Padding is the fraction of Size kept clear at each edge. Zero means the
// default of 0.06; use a negative value for no padding at all.
Padding float64
// Policy controls family resemblance between neighbouring versions.
Policy Policy
// Palette selects colour or monochrome ink.
Palette PaletteMode
// Background selects the ground.
Background Background
// Cols is the terminal width in character cells for Text output; zero
// means 32. It is ignored by renderers that declare their own grid.
Cols int
// NoColor suppresses ANSI colour in Text output, falling back to a
// density ramp.
NoColor bool
}
Options configures a render. The zero value is valid and yields a 512-pixel mark with the default renderer, the default policy and a transparent ground.
type Palette ¶
type Palette struct {
Ground color.RGBA // ignored when the background is transparent
Inks []color.RGBA
}
Palette is the resolved set of colours for one mark.
func DerivePalette ¶
func DerivePalette(p *Params, mode PaletteMode, bg Background) Palette
DerivePalette resolves the palette for a render. The base hue comes from the archetype stream, so a major version keeps its colour identity; the hue rotation is an amplified parameter, which is what makes neighbouring patch versions separate at a glance even when their geometry barely moves.
type PaletteMode ¶
type PaletteMode int
PaletteMode selects how colour is derived.
const ( // PaletteColor derives hues from the version's entropy. PaletteColor PaletteMode = iota // PaletteMono renders in a single neutral ink. Useful for print, for // embedding in coloured UI, and for perceptual-hash comparisons that // should judge form rather than hue. PaletteMono )
type Params ¶
Params is what a renderer draws its parameters from. Each named stream is a separate, lazily created BitReader: draws from one never shift the bits seen by another, so a renderer can add a parameter to one stream without disturbing the rest of the image.
func (*Params) Amplified ¶
Amplified is the stream a renderer uses for its two or three highest-contrast parameters. Under the default policy it follows the patch; under PolicyFamily it follows the minor, so patch bumps stay subtle. Drawing from it rather than branching on Policy keeps renderers policy-agnostic.
func (*Params) Archetype ¶
Archetype carries the coarsest identity: major only. Parameters that define the basic gestalt belong here.
func (*Params) PaletteBase ¶
PaletteBase and PaletteShift are the streams the palette draws from. They are separate from the renderer streams on purpose: if colour shared the archetype stream, switching to monochrome would shift every subsequent draw and change the shape of the mark. Form must not depend on whether the mark is being rendered in colour.
func (*Params) PaletteShift ¶
PaletteShift follows the amplified stream, so hue moves with the patch under the default policy.
func (*Params) Pre ¶
Pre carries prerelease and build metadata. Renderers use it for a marker rather than for the main shape, so 1.0.0-rc1 stays visibly a 1.0.0.
func (*Params) Reset ¶
func (p *Params) Reset()
Reset discards all drawn bits, so the same Params can render again identically.
type Policy ¶
type Policy int
Policy decides how much of a version's identity is inherited from its neighbours. It is an API-level choice, not a design detail: it determines whether adjacent versions read as a family with a mutation, or as unrelated marks.
const ( // PolicyFamilyAmplified is the default. Major fixes the archetype, minor // the variant, patch the detail — but a renderer additionally routes two // or three of its highest-contrast parameters (rotation, hue, one shape // parameter) through the patch stream. Neighbouring patches stay // recognisably related while remaining easy to tell apart at a glance. PolicyFamilyAmplified Policy = iota // PolicyFamily is the plain cascade. Patch changes only fine detail; // high-contrast parameters follow the minor stream. PolicyFamily // PolicyIndependent derives every stream from the full version. Adjacent // versions look unrelated: maximum distinguishability, no family // information. PolicyIndependent )
type Seed ¶
type Seed struct {
V Version
H0 [32]byte // archetype: major
H1 [32]byte // variant: major, minor
H2 [32]byte // detail: major, minor, patch
H3 [32]byte // prerelease: full version including build metadata
}
Seed holds the four separated entropy streams for a version. Splitting the hash by version component is what makes family resemblance controllable; hashing the whole string at once would not.
type Version ¶
type Version struct {
Major uint64
Minor uint64
Patch uint64
Prerelease string // without the leading '-'
Build string // without the leading '+'
}
Version is a parsed semantic version. Build metadata is retained because it feeds the prerelease entropy stream, even though SemVer ignores it for ordering.
func ParseVersion ¶
ParseVersion parses a semantic version string. A single leading "v" is tolerated because Go module tags carry one. Missing minor and patch components default to zero, so "1" and "1.2" are accepted as "1.0.0" and "1.2.0" respectively.
func (Version) IsPrerelease ¶
IsPrerelease reports whether the version carries a prerelease identifier.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package blazontest measures whether a renderer actually distinguishes versions, and fails the build when it does not.
|
Package blazontest measures whether a renderer actually distinguishes versions, and fails the build when it does not. |
|
Package canvas holds the render primitives — stage three of the pipeline.
|
Package canvas holds the render primitives — stage three of the pipeline. |
|
cmd
|
|
|
blazon
command
Command blazon renders version numbers as visual seals, and provides the tools used to judge whether a renderer actually distinguishes them.
|
Command blazon renders version numbers as visual seals, and provides the tools used to judge whether a renderer actually distinguishes them. |
|
internal
|
|
|
noise
Package noise provides deterministic 2D simplex noise.
|
Package noise provides deterministic 2D simplex noise. |
|
phash
Package phash computes perceptual hashes of rendered marks.
|
Package phash computes perceptual hashes of rendered marks. |
|
pngenc
Package pngenc encodes images as PNG without image/png.
|
Package pngenc encodes images as PNG without image/png. |
|
sha256
Package sha256 implements SHA-256 (FIPS 180-4).
|
Package sha256 implements SHA-256 (FIPS 180-4). |
|
sizeprobe
command
Command sizeprobe renders one mark and writes it to standard output.
|
Command sizeprobe renders one mark and writes it to standard output. |















