# `FrenchCurve.Sprite`
[🔗](https://github.com/jaman/french_curve/blob/v0.1.4/lib/french_curve/sprite.ex#L1)

A raster that is drawn many times, uploaded once where the protocol allows it.

Under `:kitty` a sprite is transmitted to the terminal once under a numeric id and then
stamped by reference, so repeated draws cost an id rather than a screenful of pixels.
The other three protocols have no stored-image concept: `upload/3` and `delete/3` are
no-ops returning `""`, and `place/3` re-emits the pixels every time. The same call
sequence therefore works everywhere.

    sprite = FrenchCurve.Sprite.new(raster)

    IO.write(FrenchCurve.Sprite.upload(sprite, :kitty))
    IO.write(FrenchCurve.Sprite.place(sprite, :kitty, fit: {4, 2}))

`FrenchCurve.Sprite.Registry` tracks which sprites have already been uploaded so a
render loop can call it unconditionally.

# `t`

```elixir
@type t() :: %FrenchCurve.Sprite{
  height: pos_integer(),
  id: pos_integer(),
  raster: FrenchCurve.Raster.t(),
  width: pos_integer()
}
```

# `delete`

```elixir
@spec delete(t(), atom(), keyword()) :: binary()
```

Returns the bytes that release the sprite's stored image from the terminal.

Under `:kitty` this deletes by the sprite's id; pass `:placement` in `opts` to target a
single placement. Every other protocol returns `""`. A sprite deleted this way must be
uploaded again before it can be placed.

# `new`

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

Wraps `raster` as a sprite, finalizing its dense pixel buffer.

`width` and `height` are taken from the raster. Drawing into `sprite.raster` afterwards
will not change the id, so build the raster fully before calling this.

## Options

  * `:id` — the positive integer the terminal stores the image under. Defaults to a hash
    of the pixels, so two sprites with identical contents share an id and upload once.
    Pass an explicit id only if you manage the terminal's id space yourself; two
    different images sharing an id will overwrite each other.

# `place`

```elixir
@spec place(t(), atom(), keyword()) :: binary()
```

Returns the bytes that draw the sprite at the cursor.

Under `:kitty` this references the uploaded image by id and takes
`FrenchCurve.Backend.Kitty.place/2`'s options (`:fit`, `:z`, `:placement`); `upload/3`
must have been sent first or the terminal has nothing to draw. Under `:sixel` and
`:iterm2` the pixels are re-encoded on every call, taking that backend's `render/2`
options. Under `:braille` the result is an ANSI string of braille characters.

`protocol` must be one of the four; any other atom raises `FunctionClauseError`.

# `upload`

```elixir
@spec upload(t(), atom(), keyword()) :: binary()
```

Returns the bytes that store the sprite's pixels in the terminal without displaying them.

Under `:kitty` this is a transmit sequence carrying the sprite's id; `opts` are
`FrenchCurve.Backend.Kitty.transmit/3`'s, of which `:compress` is the useful one. Every
other protocol returns `""`, as none has anywhere to store an image.

Send this once per sprite before the first `place/3`.

---

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