RasterExRatatui.PixelFormat behaviour (RasterExRatatui v0.4.0)

Copy Markdown View Source

Behaviour for packing pixels the way a panel expects them.

RasterExRatatui.Raster never builds pixels itself: it asks the format what a cell's foreground and background look like, and what an RGB pixel from a region looks like at a given position, and tiles those bytes. A format is therefore the whole colour policy of a panel, from "RGB565, little-endian" to "1-bit e-ink with tone rules and a dither".

Built in:

Callbacks

init/1 turns the raster's format_opts: into a config term, once; every other callback receives that config, so per-pixel work does not re-read keyword lists.

cell_paints/2 returns a paint/0 for each side of a cell. Most formats return solid pixel bytes. A format that simulates a tone it cannot show (a gray on a 1-bit panel) returns {:checker, even, odd}: the pixel at panel position (x, y) takes even when x + y is even, odd otherwise, so the pattern stays aligned across cells.

rgb_pixel/6 packs one region pixel. The position is there for ordered dithering; formats that do not dither ignore it.

unpack_row/2 is optional and turns packed pixels back into RGB8, so RasterExRatatui.Raster.to_png/1 can show what the panel shows.

native_format/1 is optional and names the ExRatatui.Pixels.format/0 whose bytes match the format's rgb_pixel/6, or nil. When a format has one, the raster packs its regions natively with ExRatatui.Pixels.pack_rgb8/5: resample, clip, rotate and pack in a single pass, which is how the three built-in formats run.

rgb_row/4 is optional and packs a whole row of region pixels in one call. For a format without a native format, the raster still resamples, clips and rotates natively and then packs row by row, preferring rgb_row/4 when the format exports it, because one function call per pixel is most of a region's cost. A format that does not define it gets rgb_row/5, the per-pixel fallback.

Examples

iex> alias RasterExRatatui.PixelFormat.XRGB8888
iex> config = XRGB8888.init([])
iex> XRGB8888.bytes_per_pixel(config)
4
iex> XRGB8888.rgb_pixel(255, 128, 0, 0, 0, config)
<<0, 128, 255, 255>>

Summary

Types

The config term returned by init/1.

Packed bytes for one pixel, or a two-pixel checkerboard.

t()

A module implementing this behaviour.

Callbacks

The packed pixel for areas no cell or region covers (margins, skipped cells).

Bytes per packed pixel.

{foreground, background} paints for a cell, after its colours and modifiers.

Validates opts and precomputes whatever the other callbacks need.

The ExRatatui.Pixels format these bytes match, or nil.

One packed pixel for an RGB colour at panel position (x, y).

Packs a row of region pixels: row holds n RGB8 pixels (3 * n bytes) that land at panel positions (x, y) to (x + n - 1, y). Returns n packed pixels.

Unpacks a row of packed pixels back to RGB8, 3 bytes per pixel: what the panel shows, as colours.

Functions

The format's native_format/1, or nil when it does not define one.

Resolves a paint to the pixel bytes at panel position (x, y).

Packs a row of region pixels with format, through its rgb_row/4 when it has one and pixel by pixel through rgb_pixel/6 otherwise.

Types

config()

@type config() :: term()

The config term returned by init/1.

paint()

@type paint() :: binary() | {:checker, even :: binary(), odd :: binary()}

Packed bytes for one pixel, or a two-pixel checkerboard.

t()

@type t() :: module()

A module implementing this behaviour.

Callbacks

blank(config)

@callback blank(config()) :: binary()

The packed pixel for areas no cell or region covers (margins, skipped cells).

bytes_per_pixel(config)

@callback bytes_per_pixel(config()) :: pos_integer()

Bytes per packed pixel.

cell_paints(t, config)

@callback cell_paints(ExRatatui.CellSession.Cell.t(), config()) :: {paint(), paint()}

{foreground, background} paints for a cell, after its colours and modifiers.

init(opts)

@callback init(opts :: keyword()) :: config()

Validates opts and precomputes whatever the other callbacks need.

native_format(config)

(optional)
@callback native_format(config()) :: ExRatatui.Pixels.format() | nil

The ExRatatui.Pixels format these bytes match, or nil.

Optional. A format that returns one must pack every region pixel exactly as ExRatatui.Pixels.pack_rgb8/5 does for it, at the same panel positions; its rgb_pixel/6 is then only a reference, never called for regions.

rgb_pixel(r, g, b, x, y, config)

@callback rgb_pixel(
  r :: 0..255,
  g :: 0..255,
  b :: 0..255,
  x :: non_neg_integer(),
  y :: non_neg_integer(),
  config()
) :: binary()

One packed pixel for an RGB colour at panel position (x, y).

rgb_row(row, x, y, config)

(optional)
@callback rgb_row(
  row :: binary(),
  x :: non_neg_integer(),
  y :: non_neg_integer(),
  config()
) :: binary()

Packs a row of region pixels: row holds n RGB8 pixels (3 * n bytes) that land at panel positions (x, y) to (x + n - 1, y). Returns n packed pixels.

Optional. Must produce the same bytes as n calls to rgb_pixel/6.

unpack_row(row, config)

(optional)
@callback unpack_row(row :: binary(), config()) :: binary()

Unpacks a row of packed pixels back to RGB8, 3 bytes per pixel: what the panel shows, as colours.

Optional. RasterExRatatui.Raster.to_png/1 needs it; nothing on the drawing path calls it.

Functions

native_format(format, config)

@spec native_format(t(), config()) :: ExRatatui.Pixels.format() | nil

The format's native_format/1, or nil when it does not define one.

Examples

iex> alias RasterExRatatui.PixelFormat
iex> PixelFormat.native_format(PixelFormat.RGB565, PixelFormat.RGB565.init([]))
:rgb565

resolve(paint, x, y)

@spec resolve(paint(), non_neg_integer(), non_neg_integer()) :: binary()

Resolves a paint to the pixel bytes at panel position (x, y).

Examples

iex> RasterExRatatui.PixelFormat.resolve(<<7>>, 3, 4)
<<7>>

iex> RasterExRatatui.PixelFormat.resolve({:checker, <<0>>, <<255>>}, 1, 1)
<<0>>

iex> RasterExRatatui.PixelFormat.resolve({:checker, <<0>>, <<255>>}, 1, 2)
<<255>>

rgb_row(format, row, x, y, config)

@spec rgb_row(t(), binary(), non_neg_integer(), non_neg_integer(), config()) ::
  binary()

Packs a row of region pixels with format, through its rgb_row/4 when it has one and pixel by pixel through rgb_pixel/6 otherwise.

Examples

iex> alias RasterExRatatui.PixelFormat
iex> PixelFormat.rgb_row(PixelFormat.RGB565, <<255, 0, 0, 0, 0, 255>>, 0, 0, PixelFormat.RGB565.init([]))
<<0, 248, 31, 0>>