# `Cauldron2D.Surface`
[🔗](https://github.com/jaman/cauldron/blob/v0.1.3/cauldron_2d/lib/cauldron_2d/surface.ex#L1)

Composites a grid of cells into one `FrenchCurve.Raster`.

## Cells

    :void                       nothing here
    {base, overlays, tint}      a tile, what is on it in paint order, and how it is shaded

`overlays` is ordered bottom to top and may be empty. `tint` is `nil` or the name of a tint
registered on the atlas.

Overlays and movers are composited source-over: a half-transparent sprite on a
half-transparent base leaves the result more opaque than either, not as opaque as the more
opaque of the two. Over an opaque base — which most tiles are — this is the same arithmetic
as a straight weighted mix.

## Movers

`compose/3` also takes sprites placed at arbitrary pixel offsets rather than on the grid,
as `[{art, {x, y}}]` where `{x, y}` is the sprite's top-left corner in the raster's own
pixel coordinates. Movers are drawn over the grid, in list order, and clipped to the
raster's edges. `blit/4` draws one onto an existing raster.

## Labels

`compose/4` also takes `:labels`, text drawn in `Linocut.Font` over everything else:
`[{text, {x, y}, colour}]` or `[{text, {x, y}, colour, opts}]`, positioned by the
text's top-left corner in the raster's pixel coordinates, with `opts` of `:scale`
(default `1`) and `:background`, a colour filled behind the text (default none).

## Caching

Each distinct cell is blended once and the result kept in the atlas's ETS table, keyed by
the sprite ids of its base and overlays together with the tint name. Two art names with
byte-identical rasters share one entry.

The cache outlives `Cauldron2D.Atlas.install/1` when the atlas installed is the same one.
Installing a changed atlas — new art, a redefined tint, a different void colour — empties it,
so a tint re-registered under a name it already had takes effect rather than serving tiles as
they used to look.

# `cell`

```elixir
@type cell() ::
  :void
  | {Cauldron2D.Atlas.art(), [Cauldron2D.Atlas.art()],
     Cauldron2D.Atlas.tint() | nil}
```

# `blit`

```elixir
@spec blit(
  Cauldron2D.Atlas.name() | Cauldron2D.Atlas.t(),
  FrenchCurve.Raster.t(),
  Cauldron2D.Atlas.art(),
  {number(), number()}
) :: FrenchCurve.Raster.t()
```

Draw the sprite `art` onto `raster` with its top-left corner at `{x, y}` in pixels.

Coordinates may be fractional and are rounded; parts falling outside the raster are clipped.
Returns a new raster; `raster` is unchanged.

# `compose`

```elixir
@spec compose(
  Cauldron2D.Atlas.name() | Cauldron2D.Atlas.t(),
  [[cell()]],
  [{Cauldron2D.Atlas.art(), {number(), number()}}],
  keyword()
) :: FrenchCurve.Raster.t()
```

Compose `rows` — a list of equal-length rows of cells — into a raster.

The raster is `columns * tile` pixels wide by `length(rows) * tile` tall, where `columns` is
the length of the first row. Every row must be that same length.

`movers` are `[{art, {x, y}}]` drawn over the grid in list order, positioned by their
top-left corner in the raster's pixel coordinates rather than snapped to the grid.
Coordinates may be fractional and are rounded; parts falling outside the raster are clipped.
Default `[]`.

An empty `rows` yields a `tile` by `tile` raster rather than a zero-sized one.

## Options

  * `:scale` — a positive integer dividing the tile size; the raster is composed at
    `1 / scale` of the atlas's resolution, every tile and sprite sampled once and kept in
    the atlas's cache table. Mover coordinates stay in full-resolution pixels. Default `1`
  * `:at` — the time in milliseconds an animated art's frame is chosen for. Default `0`,
    the first frame of everything
  * `:labels` — text drawn over the grid and the movers, as the module documentation
    describes. Default `[]`

# `magnify`

```elixir
@spec magnify(FrenchCurve.Raster.t(), pos_integer()) :: FrenchCurve.Raster.t()
```

The raster `times` as wide and tall, every pixel repeated that many times each way; the raster itself at 1.

# `shade_color`

```elixir
@spec shade_color(
  Cauldron2D.Atlas.name() | Cauldron2D.Atlas.t(),
  Cauldron2D.Atlas.rgb(),
  Cauldron2D.Atlas.tint() | nil
) :: Cauldron2D.Atlas.rgb()
```

Apply the tint named `tint` to a plain RGB colour, for renderers that draw characters.

The colour is passed to the tint function at full alpha and the alpha of the result is
dropped. A `nil` tint returns the colour unchanged.

# `tile_binary`

```elixir
@spec tile_binary(
  Cauldron2D.Atlas.name() | Cauldron2D.Atlas.t(),
  cell(),
  pos_integer(),
  integer()
) ::
  binary()
```

The flat RGBA binary for one cell, `tile * tile * 4` bytes long, with animated arts at
the frame showing at `at` milliseconds.

Blends the cell on first sight and keeps the result in the atlas's cache table, which
`Cauldron2D.Atlas.install/1` must have created.

---

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