canvas

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package canvas holds the render primitives — stage three of the pipeline. A canvas knows nothing about versions, hashes or parameters; a renderer knows nothing about output formats. Everything a renderer can draw is in the Canvas interface, and every output format implements exactly that.

Index

Constants

View Source
const (
	// GlyphW and GlyphH are the cell size of one glyph, in pixels, before
	// scaling.
	GlyphW = 5
	GlyphH = 7
	// GlyphAdvance is the horizontal step from one glyph to the next,
	// including the gap.
	GlyphAdvance = GlyphW + 1
)

A 5×7 bitmap font covering printable ASCII.

It is here so that the module has no dependencies. The only text this library draws is version strings on contact sheets and the character grid of the randomart renderer, so a full font stack would be a large dependency bought for a very small need — and a dependency whose glyphs could change would move the pixels of a committed gallery for no reason anyone could see.

Glyphs are written out as pictures rather than as hex, because a wrong bit in a hex table is invisible and a wrong dot here is not. A test checks that every glyph is exactly seven rows of five.

View Source
const DefaultSupersample = 3

DefaultSupersample is the oversampling factor used by NewRaster.

Variables

This section is empty.

Functions

func AnnularSector

func AnnularSector(c Canvas, cx, cy, r0, r1, a0, a1 float64)

AnnularSector appends a closed wedge between two radii and two angles. It is the cell shape of the polar renderer, and the reason the canvas needs an arc primitive at all.

func Arc

func Arc(c Canvas, cx, cy, r, a0, a1 float64)

Arc appends a circular arc from angle a0 to a1, in radians, measured clockwise from the positive x axis in the canvas's y-down coordinate system. The caller must have positioned the path at the arc's start point.

The arc is split into segments of at most a quarter turn, because the cubic approximation of a circular arc only stays within a fraction of a pixel over spans that short.

func ClosedCurve

func ClosedCurve(c Canvas, pts []Point)

ClosedCurve emits a smooth closed contour through pts using a Catmull-Rom spline converted to cubic Béziers. The caller supplies the sample points; this turns them into a curve that stays smooth at every sample rather than showing the facets of a polyline.

func DrawGlyph added in v0.2.0

func DrawGlyph(dst *image.RGBA, x, y int, r rune, col color.Color, scale int)

DrawGlyph draws one character with its top-left corner at (x,y), scaled by an integer factor. Pixels are set opaque; there is no anti-aliasing, because a bitmap font scaled by whole pixels does not need any.

func DrawString added in v0.2.0

func DrawString(dst *image.RGBA, x, y int, s string, col color.Color, scale int)

DrawString draws text with its top-left corner at (x,y).

func ReverseInPlace

func ReverseInPlace(pts []Point)

ReverseInPlace flips a point slice. A contour drawn in reverse winds the other way, which is how a renderer punches a hole under the non-zero fill rule.

func StringWidth added in v0.2.0

func StringWidth(s string, scale int) int

StringWidth reports how wide a string will be drawn.

func StrokeOutline

func StrokeOutline(lines [][]Point, width float64) [][]Point

StrokeOutline converts polylines into fillable polygons approximating a stroke with round caps and joins.

Each segment becomes a quad and each vertex a disc, all wound in the same direction. Under the non-zero rule, overlapping same-winding polygons union, so the pieces merge into one solid stroke without any explicit join geometry. That is why the raster back end needs no real stroker.

Types

type ANSI

type ANSI struct {
	// contains filtered or unexported fields
}

ANSI renders to a block of terminal text.

Vector drawing is supported by rasterising into a buffer twice as tall as the character grid and emitting the upper half-block character ▀ per cell: the glyph's foreground paints the top pixel and its background the bottom one, so a character cell carries two square pixels rather than one oblong one. That is what makes a terminal mark look like a mark instead of like ASCII art.

Character output from Cell is kept separately and wins over the raster, so a renderer that draws glyphs — the randomart field, for instance — gets what it asked for.

func NewANSI

func NewANSI(cols, rows int, trueColor bool) *ANSI

NewANSI returns a canvas of cols×rows character cells. When trueColor is false the output falls back to a monochrome ASCII ramp, for terminals that do not accept 24-bit colour or for NO_COLOR environments.

func (*ANSI) Background

func (a *ANSI) Background(col color.Color)

Background implements Canvas.

func (*ANSI) Cell

func (a *ANSI) Cell(col, row int, r rune, fg, bg color.Color)

Cell implements Canvas by recording a glyph that overrides the raster.

func (*ANSI) Close

func (a *ANSI) Close()

Close implements Canvas.

func (*ANSI) CubicTo

func (a *ANSI) CubicTo(x1, y1, x2, y2, x, y float64)

CubicTo implements Canvas.

func (*ANSI) Fill

func (a *ANSI) Fill(col color.Color)

Fill implements Canvas.

func (*ANSI) LineTo

func (a *ANSI) LineTo(x, y float64)

LineTo implements Canvas.

func (*ANSI) MoveTo

func (a *ANSI) MoveTo(x, y float64)

MoveTo implements Canvas.

func (*ANSI) SetGrid

func (a *ANSI) SetGrid(cols, rows int)

SetGrid implements Canvas. The character grid is fixed at construction, so this only checks that a renderer is not asking for a different one.

func (*ANSI) Size

func (a *ANSI) Size() (float64, float64)

Size reports the pixel grid: one unit per column, two per row.

func (*ANSI) String

func (a *ANSI) String() string

String renders the block, one line per row, with a trailing newline.

func (*ANSI) Stroke

func (a *ANSI) Stroke(col color.Color, width float64)

Stroke implements Canvas.

type Canvas

type Canvas interface {
	// Size reports the drawing area in user units. The origin is top left.
	Size() (w, h float64)

	// Background paints the whole area. A fully transparent colour is a no-op.
	Background(col color.Color)

	MoveTo(x, y float64)
	LineTo(x, y float64)
	CubicTo(x1, y1, x2, y2, x, y float64)
	// Close joins the current subpath back to its start point.
	Close()

	// Fill paints the accumulated path and resets it.
	Fill(col color.Color)
	// Stroke outlines the accumulated path and resets it.
	Stroke(col color.Color, width float64)

	// SetGrid declares a character grid. Renderers that draw with Cell must
	// call it before their first Cell; vector back ends use it to derive cell
	// metrics, and character back ends to size their buffer.
	SetGrid(cols, rows int)
	// Cell writes a single character. Canvases that cannot draw text may
	// ignore it, but all back ends here honour it.
	Cell(col, row int, r rune, fg, bg color.Color)
}

Canvas is a drawing target. Paths are built with MoveTo/LineTo/CubicTo and consumed by Fill or Stroke, which reset the path.

Filling always uses the non-zero winding rule. A renderer that wants a hole emits the inner contour with the opposite orientation to the outer one; no even-odd mode exists, because the raster back end has no efficient way to provide one and the winding trick covers every case the renderers need.

Strokes always use round caps and round joins. Every renderer here draws organic curves or arcs where that is the right choice, so it is fixed rather than configurable.

type Path

type Path struct {
	// contains filtered or unexported fields
}

Path accumulates geometry for both back ends. It is exported so that other packages in the module can build geometry once and hand it to any canvas.

func (*Path) Close

func (p *Path) Close()

Close marks the current subpath closed.

func (*Path) CubicTo

func (p *Path) CubicTo(x1, y1, x2, y2, x, y float64)

CubicTo appends a cubic Bézier segment.

func (*Path) Empty

func (p *Path) Empty() bool

Empty reports whether the path holds no geometry.

func (*Path) Flatten

func (p *Path) Flatten() [][]Point

Flatten converts the path to polylines. Closed subpaths repeat their start point as the final point, so a consumer can treat every polyline uniformly.

func (*Path) LineTo

func (p *Path) LineTo(x, y float64)

LineTo appends a straight segment, starting a subpath at the origin if none is open.

func (*Path) MoveTo

func (p *Path) MoveTo(x, y float64)

MoveTo starts a new subpath.

func (*Path) Reset

func (p *Path) Reset()

Reset empties the path for reuse.

type Point

type Point struct{ X, Y float64 }

Point is a coordinate pair.

func Polar

func Polar(cx, cy, r, angle float64) Point

Polar converts polar coordinates to a canvas point.

func Simplify

func Simplify(pts []Point, tol float64) []Point

Simplify reduces a polyline with the Ramer–Douglas–Peucker algorithm, keeping every point that lies further than tol from the retained chord.

Renderers that sample an analytic curve need a dense sample to catch its sharp features, but emitting every sample would make the SVG an order of magnitude larger than it needs to be. Simplifying afterwards keeps the detail where the curve actually bends and throws away the rest.

type Raster

type Raster struct {
	// contains filtered or unexported fields
}

Raster is a pixel canvas backed by an anti-aliased scanline rasteriser. It shares the Path model with SVG, so a renderer draws once and both back ends agree on the geometry.

Supersampling is applied because the rasteriser anti-aliases coverage but not the thin, densely packed strokes the flow-field renderer produces; at 1:1 those alias into moiré.

func NewRaster

func NewRaster(w, h int) *Raster

NewRaster returns a canvas of w×h pixels.

func NewRasterSS

func NewRasterSS(w, h, ss int) *Raster

NewRasterSS returns a canvas of w×h pixels rendered at ss× internally and box-filtered down by Image.

func (*Raster) Background

func (r *Raster) Background(col color.Color)

Background implements Canvas.

func (*Raster) Cell

func (r *Raster) Cell(col, row int, ch rune, fg, bg color.Color)

Cell implements Canvas using the built-in bitmap font. It exists so that the character renderers still produce an image for the gallery, not because anyone should prefer a PNG of a terminal mark over the terminal itself.

func (*Raster) Close

func (r *Raster) Close()

Close implements Canvas.

func (*Raster) CubicTo

func (r *Raster) CubicTo(x1, y1, x2, y2, x, y float64)

CubicTo implements Canvas.

func (*Raster) Fill

func (r *Raster) Fill(col color.Color)

Fill implements Canvas.

func (*Raster) Image

func (r *Raster) Image() *image.RGBA

Image returns the finished image, box-filtered down from the supersampled buffer.

func (*Raster) LineTo

func (r *Raster) LineTo(x, y float64)

LineTo implements Canvas.

func (*Raster) MoveTo

func (r *Raster) MoveTo(x, y float64)

MoveTo implements Canvas.

func (*Raster) SetGrid

func (r *Raster) SetGrid(cols, rows int)

SetGrid implements Canvas.

func (*Raster) Size

func (r *Raster) Size() (float64, float64)

Size implements Canvas. The reported size is in output pixels; the supersampling factor is an internal detail a renderer never sees.

func (*Raster) Stroke

func (r *Raster) Stroke(col color.Color, width float64)

Stroke implements Canvas. The path is flattened and converted to fillable outline polygons; see StrokeOutline for why no real stroker is needed.

type SVG

type SVG struct {
	// contains filtered or unexported fields
}

SVG is a vector canvas. It builds markup directly with no dependencies, and formats every coordinate to a fixed number of decimals so that the same input always yields byte-identical output.

func NewSVG

func NewSVG(w, h float64) *SVG

NewSVG returns a canvas of the given size in user units.

func (*SVG) Background

func (s *SVG) Background(col color.Color)

Background implements Canvas.

func (*SVG) Bytes

func (s *SVG) Bytes() []byte

Bytes renders the finished document.

func (*SVG) Cell

func (s *SVG) Cell(col, row int, r rune, fg, bg color.Color)

Cell implements Canvas by drawing a monospace glyph in the declared grid.

func (*SVG) Close

func (s *SVG) Close()

Close implements Canvas.

func (*SVG) CubicTo

func (s *SVG) CubicTo(x1, y1, x2, y2, x, y float64)

CubicTo implements Canvas.

func (*SVG) Fill

func (s *SVG) Fill(col color.Color)

Fill implements Canvas.

func (*SVG) LineTo

func (s *SVG) LineTo(x, y float64)

LineTo implements Canvas.

func (*SVG) MoveTo

func (s *SVG) MoveTo(x, y float64)

MoveTo implements Canvas.

func (*SVG) SetGrid

func (s *SVG) SetGrid(cols, rows int)

SetGrid implements Canvas.

func (*SVG) Size

func (s *SVG) Size() (float64, float64)

Size implements Canvas.

func (*SVG) Stroke

func (s *SVG) Stroke(col color.Color, width float64)

Stroke implements Canvas.

Jump to

Keyboard shortcuts

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