Renders a raster as block characters, two colours per cell, for terminals with no graphics protocol.
A cell stands in for a small block of pixels, sized by the glyph :set:
:half— 1×2,▀and▄; every terminal and font:quad— 2×2, the sixteen quadrant blocks:sextant— 2×3, the sixty Unicode 13 sextants with▌,▐and█:braille— 2×4 dots; dots do not fill the cell, so the background shows through around them:octant— 2×4, the 230 Unicode 16 octants with the 24 patterns that are older block characters; solid, and the finest of the sets
The pixels of a cell are split into two groups, those nearer the brightest pixel and those nearer the darkest. The first group is drawn in the foreground colour, the mean of its pixels, as the glyph whose pattern they form; the rest is the background, the mean of the other pixels. A cell of one colour is a space on that background.
A raster width by height comes out at ceil(width / sx) columns by
ceil(height / sy) rows for a set of sx by sy pixels. Pixels past the raster's
edge, and pixels below the alpha threshold, take the :background colour.
render/2 returns cell rows; to_ansi/1 turns them into a string.
raster
|> FrenchCurve.Backend.Cells.render(set: :sextant)
|> FrenchCurve.Backend.Cells.to_ansi()
|> IO.puts()
Summary
Functions
Whether the quantisation is done by the NIF rather than in Elixir.
The set a terminal is known to draw well: :sextant for iTerm2, :octant for
terminals speaking the kitty graphics protocol (kitty, Ghostty, WezTerm, which draw the
block glyphs themselves), :half for anything else or nil.
The glyph of set whose lit pixels are pattern, a bitmask in row-major order with
bit 0 the top-left pixel.
Returns the cell rows for raster.
The glyph sets, coarsest first.
How many pixels across and down one cell of set covers.
Flattens cell rows to one newline-separated string with 24-bit colours, every glyph wrapped in its foreground and background sequence and a reset after each row.
Types
@type cell() :: {binary(), FrenchCurve.Raster.color_rgb(), FrenchCurve.Raster.color_rgb()}
A glyph with its foreground and background colours.
@type set() :: :half | :quad | :sextant | :braille | :octant
A glyph set.
Functions
@spec available?() :: boolean()
Whether the quantisation is done by the NIF rather than in Elixir.
The set a terminal is known to draw well: :sextant for iTerm2, :octant for
terminals speaking the kitty graphics protocol (kitty, Ghostty, WezTerm, which draw the
block glyphs themselves), :half for anything else or nil.
Takes a graphics protocol atom, or an environment map as FrenchCurve.Capability.detect/1
reads it, which is the surer guide: a terminal can be driven through a protocol that is
not its own (iTerm2 through sixel) and still draw the glyphs its name says it does.
iex> FrenchCurve.Backend.Cells.default_set(:kitty)
:octant
iex> FrenchCurve.Backend.Cells.default_set(nil)
:half
iex> FrenchCurve.Backend.Cells.default_set(%{"TERM_PROGRAM" => "iTerm.app"})
:sextant
iex> FrenchCurve.Backend.Cells.default_set(%{"TERM" => "xterm-ghostty"})
:octant
@spec glyph(set(), non_neg_integer()) :: binary()
The glyph of set whose lit pixels are pattern, a bitmask in row-major order with
bit 0 the top-left pixel.
iex> FrenchCurve.Backend.Cells.glyph(:quad, 0b0011)
"▀"
iex> FrenchCurve.Backend.Cells.glyph(:sextant, 0b111111)
"█"
iex> FrenchCurve.Backend.Cells.glyph(:octant, 0b00000100)
""
iex> FrenchCurve.Backend.Cells.glyph(:braille, 0b00000001)
"⠁"
@spec render( FrenchCurve.Raster.t(), keyword() ) :: [[cell()]]
Returns the cell rows for raster.
Options
:set— the glyph set, default:half:background— an{r, g, b}colour for transparent pixels and for pixels past the raster's edge, default{0, 0, 0}:alpha_threshold— the smallest alpha treated as opaque, default128
@spec sets() :: [set()]
The glyph sets, coarsest first.
@spec subpixels(set()) :: {pos_integer(), pos_integer()}
How many pixels across and down one cell of set covers.
iex> FrenchCurve.Backend.Cells.subpixels(:sextant)
{2, 3}
Flattens cell rows to one newline-separated string with 24-bit colours, every glyph wrapped in its foreground and background sequence and a reset after each row.