graphics

package
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Aug 5, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package graphics puts images in a terminal that can show them.

It writes escape sequences and nothing else: it does not decode an image, hold one, decide where it goes, or know what a cell contains. What it needs is a PNG that somebody else already has and a place the caller has already worked out.

Why only PNG, and only kitty

PNG because its dimensions are in the first twenty-four bytes, which means the size can be known without a decoder and therefore without a dependency. The promise this library makes about its dependency list is worth more than the convenience of accepting a JPEG.

Kitty because it is the protocol the terminals worth targeting converged on — kitty, Ghostty, WezTerm and Warp all speak it. iTerm2's own protocol and sixel are not here, and a terminal that speaks neither this nor nothing at all gets None, which is the caller's cue to draw a placeholder.

Index

Constants

This section is empty.

Variables

View Source
var ErrNotPNG = errors.New("graphics: not a PNG")

ErrNotPNG is reported for data that is not a PNG this package can size.

Functions

func Delete

func Delete(w io.Writer, id uint32) error

Delete removes every placement of an image and forgets it.

func Fit

func Fit(pxW, pxH, cellW, cellH, maxCols, maxRows int) (cols, rows int)

Fit is the cell box an image of pxW by pxH should occupy, keeping its aspect ratio and staying inside maxCols by maxRows.

cellW and cellH are the size of one cell in pixels, which a terminal reports and a caller has to have asked for. Nothing sensible can be computed without them, so an unusable argument gives a single cell rather than a division by zero.

func PNGSize

func PNGSize(png []byte) (width, height int, err error)

PNGSize is the pixel size of a PNG, read from its header.

It reads the signature and the IHDR chunk and nothing else. A decoder would be a dependency, and every byte it would decode is a byte this package has no use for: the pixels are the terminal's problem, and only the dimensions are ours.

func Place

func Place(w io.Writer, id uint32, cols, rows int) error

Place shows a transmitted image at the cursor, scaled into cols by rows cells.

The cursor is positioned by the caller, and the escape belongs after the cell diff of the frame it appears in: the diff would otherwise write over the image with the blanks it thinks are underneath it.

Types

type Image

type Image struct {
	// ID is the number the terminal now knows the image by, which is what
	// [Place] and [Delete] refer to.
	ID uint32
	// Width and Height are the image's size in pixels, for working out how many
	// cells it should occupy with [Fit].
	Width, Height int
}

Image is a transmitted image and the size it arrived at.

func Transmit

func Transmit(w io.Writer, id uint32, png []byte) (Image, error)

Transmit sends a PNG to the terminal under an ID, without placing it.

Transmission and placement are separate because they happen at different times: an image is sent once and placed on every frame that shows it, and re-sending the payload each frame would put a megabyte on the wire to move a picture by one row.

type Protocol

type Protocol uint8

Protocol is the inline-image capability of a terminal.

The zero value is None, so anything that has not been told what it is talking to draws no images rather than corrupting a screen with escape sequences the terminal will print instead of obey.

const (
	// None is a terminal that cannot show an image. A caller draws a placeholder.
	None Protocol = iota
	// Kitty is the kitty graphics protocol: kitty, Ghostty, WezTerm and Warp.
	Kitty
)

func DetectIn

func DetectIn(getenv func(string) string) Protocol

DetectIn works out the protocol from an environment.

It takes its own lookup rather than reading the process environment, which is what makes it a function of its inputs: [term.DetectGraphics] passes os.Getenv, and a test passes whatever it wants to say the terminal is. There is no cached global and no override hook, for the same reason there is no global palette — a program with two terminals could not have two answers, and a test could not pin either.

Jump to

Keyboard shortcuts

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