# `FrenchCurve.Backend.Cells`
[🔗](https://github.com/jaman/french_curve/blob/v0.2.0/lib/french_curve/backend/cells.ex#L1)

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()

# `cell`

```elixir
@type cell() ::
  {binary(), FrenchCurve.Raster.color_rgb(), FrenchCurve.Raster.color_rgb()}
```

A glyph with its foreground and background colours.

# `set`

```elixir
@type set() :: :half | :quad | :sextant | :braille | :octant
```

A glyph set.

# `available?`

```elixir
@spec available?() :: boolean()
```

Whether the quantisation is done by the NIF rather than in Elixir.

# `default_set`

```elixir
@spec default_set(atom() | nil | map()) :: set()
```

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

# `glyph`

```elixir
@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)
    "⠁"

# `render`

```elixir
@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, default `128`

# `sets`

```elixir
@spec sets() :: [set()]
```

The glyph sets, coarsest first.

# `subpixels`

```elixir
@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}

# `to_ansi`

```elixir
@spec to_ansi([[cell()]]) :: binary()
```

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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
