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 (default76, 30%):paper_min— RGB colours with luma at or above this are paper (default178, 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>>}
Summary
Functions
Paper.
One byte per pixel.
Foreground and background tones for a cell; see the moduledoc table.
Reads :ink_max and :paper_min.
: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.
Dithers an RGB pixel to ink or paper against the Bayer 4×4 threshold at (x, y).
Dithers a row of RGB pixels against the Bayer row for y, walking the tile from x.
Each gray byte as an RGB8 pixel of that level.
Functions
Paper.
Examples
iex> RasterExRatatui.PixelFormat.Mono.blank(%{})
<<255>>
@spec bytes_per_pixel(term()) :: 1
One byte per pixel.
Examples
iex> RasterExRatatui.PixelFormat.Mono.bytes_per_pixel(%{})
1
@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>>}
@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}
@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
@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>>
@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>>
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>>