# `RasterExRatatui.PixelFormat.Mono`
[🔗](https://github.com/mcass19/raster_ex_ratatui/blob/v0.4.0/lib/raster_ex_ratatui/pixel_format/mono.ex#L1)

One gray byte per pixel for 1-bit panels: `0` is ink, `255` is paper.

A 1-bit panel cannot show colour, so every colour collapses to a *tone*: ink, paper, or gray (a pixel checkerboard). The bytes are gray8 so they can be thresholded, PNG-encoded, or bit-packed by the driver; every pixel is exactly `0` or `255`.

## Cells

The glyph's set bits take the foreground tone and the rest of the cell the background tone, which keeps half-block cells honest: `▀` with a dark `fg` and a light `bg` inks only its top half, which is what `Canvas`'s `:half_block` marker emits.

| Colour | as `fg` | as `bg` |
| ------ | ------- | ------- |
| `:black` | ink | ink |
| `:reset`, other named, indexed | ink on a paper `bg`, paper on an ink `bg` | paper |
| `{:rgb, r, g, b}` | by luma: ink / gray / paper | same |

`:reversed` short-circuits all of that and always paints a paper glyph on an ink cell: screens stack it with a `bg: :black` fill to invert a whole frame. Named colours keep terminal legibility: a red or white label is ink on paper and paper on an ink fill, and `bg: :white` (which `Canvas` emits for shape cells) is plain paper. RGB colours are treated as intent: dark is ink, light is paper, and the band between `:ink_max` and `:paper_min` (Rec. 601 luma) is gray, so lit and shadowed faces read as three tones. Other modifiers are ignored.

## Regions

Region pixels go through a Bayer 4×4 ordered dither on luma. The tile repeats across the panel, so the texture is stable between frames and under partial refresh, without the crawling noise error diffusion shows.

## Options

  * `:ink_max` — RGB colours with luma at or below this are ink (default `76`, 30%)
  * `:paper_min` — RGB colours with luma at or above this are paper (default `178`, 70%)

## Examples

    iex> alias ExRatatui.CellSession.Cell
    iex> alias RasterExRatatui.PixelFormat.Mono
    iex> config = Mono.init([])
    iex> Mono.cell_paints(%Cell{fg: :red, bg: :reset}, config)
    {<<0>>, <<255>>}
    iex> Mono.cell_paints(%Cell{fg: :red, bg: :black}, config)
    {<<255>>, <<0>>}
    iex> Mono.cell_paints(%Cell{fg: {:rgb, 128, 128, 128}, bg: :reset}, config)
    {{:checker, <<0>>, <<255>>}, <<255>>}

# `blank`

```elixir
@spec blank(term()) :: binary()
```

Paper.

## Examples

    iex> RasterExRatatui.PixelFormat.Mono.blank(%{})
    <<255>>

# `bytes_per_pixel`

```elixir
@spec bytes_per_pixel(term()) :: 1
```

One byte per pixel.

## Examples

    iex> RasterExRatatui.PixelFormat.Mono.bytes_per_pixel(%{})
    1

# `cell_paints`

```elixir
@spec cell_paints(ExRatatui.CellSession.Cell.t(), map()) ::
  {RasterExRatatui.PixelFormat.paint(), RasterExRatatui.PixelFormat.paint()}
```

Foreground and background tones for a cell; see the moduledoc table.

## Examples

    iex> alias ExRatatui.CellSession.Cell
    iex> RasterExRatatui.PixelFormat.Mono.cell_paints(%Cell{bg: :blue, modifiers: [:reversed]}, RasterExRatatui.PixelFormat.Mono.init([]))
    {<<255>>, <<0>>}

# `init`

```elixir
@spec init(keyword()) :: %{ink_max: 0..255, paper_min: 0..255}
```

Reads `:ink_max` and `:paper_min`.

## Examples

    iex> RasterExRatatui.PixelFormat.Mono.init(ink_max: 50)
    %{ink_max: 50, paper_min: 178}

# `native_format`

```elixir
@spec native_format(term()) :: :mono_bayer4
```

`:mono_bayer4`: regions are dithered natively by `ExRatatui.Pixels.pack_rgb8/5`, with the same luma, matrix and panel anchoring as `rgb_pixel/6`. Cells keep the tone rules.

## Examples

    iex> RasterExRatatui.PixelFormat.Mono.native_format(%{})
    :mono_bayer4

# `rgb_pixel`

```elixir
@spec rgb_pixel(0..255, 0..255, 0..255, non_neg_integer(), non_neg_integer(), term()) ::
  binary()
```

Dithers an RGB pixel to ink or paper against the Bayer 4×4 threshold at `(x, y)`.

## Examples

    iex> alias RasterExRatatui.PixelFormat.Mono
    iex> Mono.rgb_pixel(255, 255, 255, 0, 0, %{})
    <<255>>
    iex> Mono.rgb_pixel(0, 0, 0, 3, 3, %{})
    <<0>>
    iex> for x <- 0..3, y <- 0..3, into: <<>>, do: Mono.rgb_pixel(128, 128, 128, x, y, %{})
    <<255, 0, 255, 0, 0, 255, 0, 255, 255, 0, 255, 0, 0, 255, 0, 255>>

# `rgb_row`

```elixir
@spec rgb_row(binary(), non_neg_integer(), non_neg_integer(), term()) :: binary()
```

Dithers a row of RGB pixels against the Bayer row for `y`, walking the tile from `x`.

## Examples

    iex> alias RasterExRatatui.PixelFormat.Mono
    iex> Mono.rgb_row(:binary.copy(<<128, 128, 128>>, 4), 0, 1, %{})
    <<0, 255, 0, 255>>
    iex> Mono.rgb_row(:binary.copy(<<128, 128, 128>>, 4), 1, 1, %{})
    <<255, 0, 255, 0>>

# `unpack_row`

```elixir
@spec unpack_row(binary(), term()) :: binary()
```

Each gray byte as an RGB8 pixel of that level.

## Examples

    iex> RasterExRatatui.PixelFormat.Mono.unpack_row(<<0, 255>>, %{})
    <<0, 0, 0, 255, 255, 255>>

---

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