# `FrenchCurve.Backend.Kitty`
[🔗](https://github.com/jaman/french_curve/blob/v0.1.4/lib/french_curve/backend/kitty.ex#L1)

Renders a raster with the kitty graphics protocol, as raw RGBA (`f=32`) in APC sequences.

True RGBA, so transparency is kept. Alone among the four backends this one has a stored
image: `transmit/3` puts pixels in the terminal under a numeric id and `place/2` stamps
that id anywhere, so a repeated image costs one small sequence per draw. `render/2` is
the one-shot form that transmits and displays in a single call.

Pixels are base64-encoded and split into 4096-byte chunks, each carrying its own escape
sequence.

`transmit/3`, `place/2` and `delete/1` suppress the terminal's acknowledgement (`q=2`)
on every chunk they emit, so none of them provokes a reply on stdin, where it would
reach the application as a run of keystrokes. `render/2` does not set it, so a terminal
that acknowledges by default will answer a `render/2` call; read and discard that reply,
or use `transmit/3` with `place/2` on an input-reading application.

    IO.write(FrenchCurve.Backend.Kitty.transmit(raster, 42, compress: true))
    IO.write(FrenchCurve.Backend.Kitty.place(42, fit: {20, 10}, z: 1))

## Compression

`compress: true` deflates the pixels before base64 and tells the terminal so with `o=z`,
which shrinks both the payload and the number of chunks. It is off by default: the
protocol makes compression optional and a partial implementation may not support it, so
turn it on only for terminals you know accept it.

# `delete`

```elixir
@spec delete(keyword()) :: binary()
```

Returns the sequence that removes placements (`a=d`).

## Options

  * `:id` — the image id given to `transmit/3`; removes the image and every placement
    of it. Required unless `:z` is given.
  * `:placement` — with `:id`, delete only this placement, leaving the image stored and
    its other placements alone.
  * `:z` — instead of `:id`, delete every placement at this z-index, of any image,
    keeping the images stored.

# `place`

```elixir
@spec place(
  pos_integer(),
  keyword()
) :: binary()
```

Returns the sequence that draws the stored image `id` at the cursor (`a=p`).

`id` must already have been sent by `transmit/3`, or the terminal has nothing to draw.
The cursor is left where it was (`C=1`), so a placement does not move the caret.

## Options

  * `:fit` — `{cols, rows}`, the cell box the terminal scales this placement into
  * `:z` — integer stacking order; higher placements draw over lower ones, and negative
    values sit behind the text
  * `:placement` — integer id for this placement, needed to delete or replace it
    individually. Omitted by default, which lets the terminal assign one.
  * `:x_offset`, `:y_offset` — pixels to shift the image right and down inside the
    cursor's cell, each less than the cell's size in that direction
  * `:source` — `{x, y, width, height}`, the rectangle of the stored image to show, in
    pixels from its top left. The whole image by default.

Every option is omitted from the sequence when not given, leaving the terminal's default.

# `render`

```elixir
@spec render(
  FrenchCurve.Raster.t(),
  keyword()
) :: binary()
```

Returns the sequence that transmits `raster` and displays it at the cursor (`a=T`).

The image is not stored under an id and cannot be placed again; use `transmit/3` and
`place/2` for that.

## Options

  * `:fit` — `{cols, rows}`, the cell box the terminal scales the image into. Omitted by
    default, which sizes the image from its pixel dimensions.
  * `:compress` — deflate the pixels before base64 and mark the payload `o=z`, default
    `false`
  * `:cursor` — `:keep` leaves the caret where it was (`C=1`) rather than letting the
    terminal move it past the image, which near the bottom of the screen would scroll.
    Omitted by default, which is the terminal's own behaviour
  * `:quiet` — suppress the terminal's replies (`q=2`), default `false`. Worth setting for
    anything drawn repeatedly, since the replies otherwise arrive as input
  * `:id` — also name the image, so `delete/1` can take it off again. **Set this for anything
    drawn more than once.** A kitty image is an overlay rather than cells: text written over
    it does not rub it out and leaving the alternate screen does not discard it, so an
    unnamed one cannot be removed at all and is still on the screen after the program exits

# `transmit`

```elixir
@spec transmit(FrenchCurve.Raster.t(), pos_integer(), keyword()) :: binary()
```

Returns the sequence that stores `raster` under `id` without displaying it (`a=t`).

`id` is a positive integer naming the image in the terminal until `delete/1`; storing a
second image under an id already in use replaces the first. Pass the same `id` to
`place/2` to draw it.

## Options

  * `:compress` — deflate the pixels before base64 and mark the payload `o=z`, default
    `false`

Sizing is chosen per placement rather than per image, so `:fit` has no effect here and
belongs on `place/2`.

---

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