# `ExRatatui.CellSession.Region`
[🔗](https://github.com/mcass19/ex_ratatui/blob/v0.18.0/lib/ex_ratatui/cell_session/region.ex#L1)

A bitmap that a pixel-mode widget handed to an `ExRatatui.CellSession`
created with a `:font_size`, covering a rect of cells.

Terminal graphics protocols smuggle their bytes through one cell's
symbol, which a cell diff cannot carry. A surface with real pixels does
not need the escape at all: it wants the bitmap and the rect it covers.
So on a session that knows its cell pixel size, `ExRatatui.Widgets.Viewport3D`
and `ExRatatui.Widgets.Image` in a pixel mode rasterize into a region,
and the cells they cover arrive as plain blank cells.

## Fields

  * `:x`, `:y`, `:width`, `:height` — the covered rect, in cells
  * `:pixel_width`, `:pixel_height` — the bitmap's size in pixels.
    Usually `width * font_width` by `height * font_height`; the longest
    side is capped (1280 px), so a very large rect gets a smaller
    bitmap to scale up
  * `:format` — `:rgb8`: row-major, three bytes per pixel, no padding
  * `:data` — the pixel bytes, `pixel_width * pixel_height * 3` long

## Contract

`ExRatatui.CellSession.Snapshot` and `ExRatatui.CellSession.Diff` carry the
**complete** list of regions on screen for that frame, never a delta. A
region that is not listed is gone. Paint the cells first, then blit every
region over its rect, scaling if the bitmap is smaller than the rect.

## Examples

    iex> ExRatatui.CellSession.Region.from_native(%{
    ...>   x: 2, y: 1, width: 2, height: 1,
    ...>   pixel_width: 2, pixel_height: 1, format: :rgb8,
    ...>   data: <<255, 0, 0, 0, 0, 255>>
    ...> })
    %ExRatatui.CellSession.Region{
      x: 2, y: 1, width: 2, height: 1,
      pixel_width: 2, pixel_height: 1, format: :rgb8,
      data: <<255, 0, 0, 0, 0, 255>>
    }

# `t`

```elixir
@type t() :: %ExRatatui.CellSession.Region{
  data: binary(),
  format: :rgb8,
  height: non_neg_integer(),
  pixel_height: non_neg_integer(),
  pixel_width: non_neg_integer(),
  width: non_neg_integer(),
  x: non_neg_integer(),
  y: non_neg_integer()
}
```

# `from_native`

```elixir
@spec from_native(map()) :: t()
```

Builds a `t:t/0` from the raw map the NIF returns for one region.

# `rotate`

```elixir
@spec rotate(t(), ExRatatui.Pixels.angle()) :: t()
```

Rotates the region's bitmap clockwise by `angle` degrees.

Returns the region with `:data`, `:pixel_width` and `:pixel_height`
updated; at 90 and 270 the two dimensions are swapped. For a surface
whose panel is mounted on its side, this turns the bitmap once so the
rest of the paint stays a flat row-by-row copy.

**The cell rect is left alone.** `:x`, `:y`, `:width` and `:height` are
the app's cell coordinates, and turning them needs the size of the grid
they sit in, which a region does not carry. A consumer that rotates its
whole screen already knows that size and computes the turned rect
itself — raster_ex_ratatui does exactly that, in its physical_rect/2.

Raises `ArgumentError` on a bitmap whose byte count is not
`pixel_width * pixel_height * 3`, or on an unsupported angle. See
`ExRatatui.Pixels.rotate_rgb8/4` for the corner mapping.

## Examples

    iex> region = %ExRatatui.CellSession.Region{
    ...>   x: 4, y: 2, width: 2, height: 1,
    ...>   pixel_width: 2, pixel_height: 1,
    ...>   data: <<255, 0, 0, 0, 0, 255>>
    ...> }
    iex> ExRatatui.CellSession.Region.rotate(region, 90)
    %ExRatatui.CellSession.Region{
      x: 4, y: 2, width: 2, height: 1,
      pixel_width: 1, pixel_height: 2, format: :rgb8,
      data: <<255, 0, 0, 0, 0, 255>>
    }

# `to_png`

```elixir
@spec to_png(t()) :: binary()
```

Encodes the region's bitmap as a PNG binary.

For consumers that ship regions over a text channel (a LiveView push,
a JSON API), where raw RGB would be several times heavier. The PNG has
the region's `pixel_width` × `pixel_height`.

Raises `ArgumentError` when `data` does not hold exactly
`pixel_width * pixel_height * 3` bytes.

---

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