Documentation
¶
Overview ¶
Package graphics puts images in a terminal that can show them.
It writes escape sequences and nothing else: it does not decode pixels, hold an image payload after transmission, decide where one 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 ¶
PNG because its dimensions can be read without decoding the pixel payload. The standard library owns format validation and configuration decoding, so this package needs no image dependency beyond Go itself. That dependency promise is worth more than the convenience of accepting every format a caller might hold.
The protocols, and what each is good for ¶
Showing an image and showing one in an interface that redraws are two different capabilities, and only one protocol has both.
Kitty's gives the program a handle: an image is sent once under a number, placed as often as needed, moved, and deleted. That is what a live region requires — what it showed last frame has to be moved or taken away this frame, and a protocol with no way to name an image has no way to be told which one.
iTerm2's protocol and sixel put pixels at the cursor and end there. No number, no z-order, no deletion. They are usable where nothing will redraw over the result — printed output, which belongs to the terminal from then on — and not in a region being drawn again sixty times a second. Protocol.Supports is that distinction, and it is why this package names more protocols than it writes.
What it writes is kitty and iTerm2. Sixel is detected and reported and not produced: producing it means decoding the image into pixels, and a decoder is the dependency this package exists without. A caller holding an encoder of its own learns from Sixel that the terminal will take what it makes.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Fit ¶
Fit is the cell box an image of pixels should occupy, keeping its aspect ratio and staying inside limit.
cell is the size of one terminal cell in pixels. Nothing sensible can be computed without positive image and cell dimensions, so an unusable argument gives one cell rather than dividing by zero. Point keeps each two-dimensional fact together, so callers cannot silently exchange a width from one coordinate space with a height from another.
func Inline ¶ added in v0.0.2
Inline writes an image at the cursor over iTerm2's protocol.
There is no counterpart to Image.Paint or Image.Erase, because the protocol has none: the image goes where the cursor is and the program never hears of it again. That is why it is only for Printed output — see the package comment — and why this takes the payload every time rather than an identifier.
The box is in cells, and the image is fitted inside it rather than stretched to it, so a caller passes what Fit worked out and gets the aspect ratio kept.
Types ¶
type Image ¶
type Image struct {
// ID is the number the terminal now knows the image by. It also makes a stable
// identity for [github.com/Tangerg/oolong/core/grid.View.Paint].
ID uint32
// Size is the image's size in pixels, for working out how many cells it should
// occupy with [Fit].
Size image.Point
}
Image is a transmitted image and the size it arrived at.
func Transmit ¶
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.
func (Image) Paint ¶ added in v0.0.3
Paint shows a transmitted image at the cursor, scaled into size 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.
The cursor is left where it was found. That is what lets an image go in a frame at all — every position in a frame is a movement from the last known one, and an inline block's whole position is relative — and it is the property the protocols that cannot be told to move an image also lack.
Paint and Image.Erase are what a frame asks of anything that writes itself onto the terminal rather than into cells. They satisfy github.com/Tangerg/oolong/core/grid.Painter without either package knowing about the other.
type Placement ¶ added in v0.0.2
type Placement uint8
Placement is where an image is going, which is what decides whether a protocol will do.
const ( // Printed is output written once that then belongs to the terminal, scrolling // away with the rest of the session. Nothing draws over it again, so a protocol // that cannot be told to remove an image is no worse off here than one that can. // // It is the zero value because it is the weaker requirement: code that has not // said where an image is going gets the answer that holds in both places. Printed Placement = iota // Live is a region the interface redraws. An image there has to be placeable // again on the next frame and removable on the frame after, which takes a // protocol that lets an image be named. Live )
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. It is // the only one usable in a region the interface redraws. Kitty // ITerm2 is iTerm2's own inline-image protocol, also spoken by WezTerm and // mintty. An image goes at the cursor and cannot be referred to again. ITerm2 // Sixel is the oldest of the three and the most widely implemented after // kitty's: xterm, foot, mlterm, contour, and recent Windows Terminal. This // package reports it and does not write it — see the package comment. Sixel )
func Detect ¶ added in v0.11.0
Detect works out the richest protocol a terminal supports.
name is what the terminal said it was when asked, or empty when nothing was asked or nothing answered. It outranks the environment for the reason above.
It takes these facts because fewer are not enough. The environment names the terminal, which is how kitty's protocol and iTerm2's are found; nothing in the environment names sixel, so a terminal that supports sixel and nothing else is indistinguishable from a terminal that supports nothing. That answer only comes from a device-attribute response, and sixel says what it said.
The lookup is passed in rather than read, which is what makes this a function of its inputs: an adapter passes its environment lookup and negotiated answers, and a test passes whatever facts it wants. 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.
func (Protocol) Supports ¶ added in v0.0.2
Supports reports whether an image can go in that place over this protocol.
Asking it is worth more than a single yes or no about the terminal. A terminal that draws images but cannot be told to move them is a different thing to tell the user about from one that draws none: the first shows a picture in printed output and nothing in a live view, and a caller holding one boolean can explain neither.