Drafter.Render.Picture (drafter v0.5.0)

Copy Markdown View Source

Encodes a widget's picture into the bytes and region Drafter.Compositor.put_image/4 takes.

A widget exporting image_raster/2 returns a t/0: a FrenchCurve.Raster covering a box of cells, the protocol to draw it with, and where the box sits in the widget. The widget server encodes it with encode/3.

On a kitty terminal that keeps images under an id, the whole raster is stored once and the region carries :crop. The compositor then moves the image and shows any part of it with placements alone, so neither a move nor a scroll re-encodes it.

Every other protocol sends the pixels themselves. Only the part of the box inside the visible window is encoded, and the region is reduced to that part, so a change of window needs a new encoding. window_dependent?/1 says which case a picture is in.

picture = %{raster: raster, protocol: :kitty, dx: 0, dy: 0, cols: 40, rows: 6}
{paint, clear, region} = Drafter.Render.Picture.encode(picture, :my_chart, nil)

Two optional keys change how a stored kitty image is sent, each on its own:

  • format: :png sends it as a PNG — a palette one when it has at most 256 colours — instead of deflated raw pixels (FrenchCurve.Backend.Kitty.encode/2), usually far smaller for flat-coloured pictures
  • swap: true keeps it under two image ids, a new frame stored under the one not on screen, placed, and only then the old one deleted, the whole sent as one synchronized update, so the terminal never shows the region empty between frames

Summary

Types

The stored kitty image a region shows: its kitty image id and its size in pixels. A picture sent with swap: true also has a spare id and its encoded pixels, which the compositor stores under whichever of the two is not on screen, after the region's own bytes.

t()

A widget's picture: raster drawn with protocol into the cols by rows cell box whose top left is dx, dy cells from the widget's top left.

A rectangle of cells, relative to the widget's top left.

Functions

Encodes picture for the image region id, showing the part inside window.

The pixel rectangle {x, y, width, height} of a width by height pixel image drawn into cols by rows cells that covers the cells {col, row, visible_cols, visible_rows}, counted from the image's top left cell.

The cells of picture's box inside window, as {col, row, cols, rows} counted from the box's top left, or nil when none are. A nil window is the whole box.

Whether the encoding of picture depends on the visible window.

Types

crop()

@type crop() :: %{
  :image => pos_integer(),
  :width => pos_integer(),
  :height => pos_integer(),
  optional(:spare) => pos_integer(),
  optional(:encoded) => FrenchCurve.Backend.Kitty.encoded()
}

The stored kitty image a region shows: its kitty image id and its size in pixels. A picture sent with swap: true also has a spare id and its encoded pixels, which the compositor stores under whichever of the two is not on screen, after the region's own bytes.

t()

@type t() :: %{
  :raster => FrenchCurve.Raster.t(),
  :protocol => atom(),
  :dx => non_neg_integer(),
  :dy => non_neg_integer(),
  :cols => pos_integer(),
  :rows => pos_integer(),
  optional(:format) => :rgba | :png,
  optional(:swap) => boolean()
}

A widget's picture: raster drawn with protocol into the cols by rows cell box whose top left is dx, dy cells from the widget's top left.

window()

@type window() :: %{
  x: integer(),
  y: integer(),
  width: non_neg_integer(),
  height: non_neg_integer()
}

A rectangle of cells, relative to the widget's top left.

Functions

encode(picture, id, window)

@spec encode(t(), term(), window() | nil) :: {iodata(), iodata(), map()} | nil

Encodes picture for the image region id, showing the part inside window.

window is the widget's visible cells relative to its top left, or nil when all of it is visible. It is ignored when the terminal keeps kitty images under an id.

Returns {paint, clear, region}, or nil when no part of the picture is inside window or the protocol cannot draw it. With :crop in region, paint stores the image without drawing it, and the compositor places it.

source(arg1, arg2, arg3)

The pixel rectangle {x, y, width, height} of a width by height pixel image drawn into cols by rows cells that covers the cells {col, row, visible_cols, visible_rows}, counted from the image's top left cell.

Cell edges are mapped to pixels by rounding down, so adjacent cell ranges share their boundary pixel row and column exactly.

iex> Drafter.Render.Picture.source({160, 48}, {40, 6}, {0, 2, 40, 3})
{0, 16, 160, 24}

visible_cells(picture, window)

@spec visible_cells(map(), window() | nil) ::
  {non_neg_integer(), non_neg_integer(), pos_integer(), pos_integer()} | nil

The cells of picture's box inside window, as {col, row, cols, rows} counted from the box's top left, or nil when none are. A nil window is the whole box.

iex> picture = %{dx: 0, dy: 1, cols: 10, rows: 6}
iex> Drafter.Render.Picture.visible_cells(picture, %{x: 0, y: 3, width: 10, height: 20})
{0, 2, 10, 4}

window_dependent?(picture)

@spec window_dependent?(t()) :: boolean()

Whether the encoding of picture depends on the visible window.

False when the terminal keeps kitty images under an id, where the compositor crops the stored image; true for every other protocol.