FrenchCurve.Raster (FrenchCurve v0.2.1)

Copy Markdown View Source

A fixed-size RGBA pixel buffer, the value every drawing and backend function operates on.

A raster has a width, a height, a background colour returned for any pixel never written, and its contents in two layers:

  • rgba — a dense row-major width * height * 4 byte base buffer, or nil when every base pixel is the background
  • pixels — a sparse map of {x, y} to colour written over the base

get_pixel/3 reads the top layer first, so the layering is not something a caller has to track. put_pixel/4 and put_pixels/3 write into pixels; fill_spans/3 writes whole horizontal runs straight into the base buffer, which is the fast way to fill large areas. finalize/1 folds the map into the base buffer.

Coordinates are zero-based with {0, 0} at the top left. Writes outside the bounds are dropped rather than raising.

FrenchCurve.Raster.new(4, 2, background: {0, 0, 0, 255})
|> FrenchCurve.Raster.put_pixel(1, 0, {255, 0, 0, 255})
|> FrenchCurve.Raster.to_rgba_binary()

Summary

Functions

Returns the {width, height} rectangle of raster whose top left is at {x, y}.

Returns the raster's {width, height} in pixels.

Returns raster with every horizontal span in spans filled with color.

Returns raster with its pixel map folded into the dense base buffer.

Builds a raster from an existing dense RGBA buffer.

Returns the color/0 at {x, y}.

Returns whether {x, y} falls inside the raster.

Builds an empty raster width by height pixels.

Returns raster with the pixel at {x, y} set to color.

Returns raster with every {x, y} in points set to color.

Returns every pixel as height lists of width colours, top row first.

Returns the raster as a dense row-major RGBA buffer.

Types

channel()

@type channel() :: 0..255

color()

@type color() :: {channel(), channel(), channel(), channel()}

color_rgb()

@type color_rgb() :: {channel(), channel(), channel()}

point()

@type point() :: {non_neg_integer(), non_neg_integer()}

span()

@type span() :: {integer(), integer(), integer()}

t()

@type t() :: %FrenchCurve.Raster{
  background: color(),
  height: pos_integer(),
  pixels: %{optional(point()) => color()},
  rgba: binary() | nil,
  width: pos_integer()
}

Functions

crop(raster, arg1, arg2)

@spec crop(t(), point(), {pos_integer(), pos_integer()}) :: t()

Returns the {width, height} rectangle of raster whose top left is at {x, y}.

The rectangle must lie inside the raster. The result carries the same background and a dense buffer.

dimensions(raster)

@spec dimensions(t()) :: {pos_integer(), pos_integer()}

Returns the raster's {width, height} in pixels.

fill_spans(raster, spans, color)

@spec fill_spans(t(), [span()], color() | term()) :: t()

Returns raster with every horizontal span in spans filled with color.

Each span is {y, x_from, x_to}, both ends inclusive and in either order. Spans are clipped to the raster and may overlap. Pending put_pixel/4 writes are folded into the base buffer first, so the spans land on top of everything drawn before them. A color that is not an {r, g, b, a} tuple is written pixel by pixel as put_pixels/3 does.

finalize(raster)

@spec finalize(t()) :: t()

Returns raster with its pixel map folded into the dense base buffer.

Pixel values are unchanged. Call this on a raster that will be sent to a backend more than once, so to_rgba_binary/1 returns the buffer without rebuilding it. Drawing after finalize/1 stays correct.

from_rgba(width, height, binary, opts \\ [])

@spec from_rgba(pos_integer(), pos_integer(), binary(), keyword()) :: t()

Builds a raster from an existing dense RGBA buffer.

binary must be exactly width * height * 4 bytes, four bytes per pixel in red, green, blue, alpha order and rows top to bottom; a mismatched size raises FunctionClauseError. The buffer becomes the raster's base without being copied.

Options

  • :background — the color/0 returned for out-of-bounds reads, default {0, 0, 0, 0}

get_pixel(raster, x, y)

@spec get_pixel(t(), integer(), integer()) :: color()

Returns the color/0 at {x, y}.

Reads a written pixel first, then the base buffer, and falls back to the raster's background. Out-of-bounds coordinates return the background rather than raising.

in_bounds?(raster, x, y)

@spec in_bounds?(t(), integer(), integer()) :: boolean()

Returns whether {x, y} falls inside the raster.

True when x and y are both non-negative and below width and height respectively.

new(width, height, opts \\ [])

@spec new(pos_integer(), pos_integer(), keyword()) :: t()

Builds an empty raster width by height pixels.

Both dimensions must be positive integers; anything else raises FunctionClauseError.

Options

  • :background — the color/0 get_pixel/3 returns for unwritten pixels, default {0, 0, 0, 0} (transparent black)

put_pixel(raster, x, y, color)

@spec put_pixel(t(), integer(), integer(), color()) :: t()

Returns raster with the pixel at {x, y} set to color.

color is an {r, g, b, a} tuple of bytes and replaces whatever was there; there is no alpha blending. Coordinates outside the bounds are ignored and the raster is returned unchanged.

put_pixels(raster, points, color)

@spec put_pixels(t(), [{integer(), integer()}], color()) :: t()

Returns raster with every {x, y} in points set to color.

Equivalent to put_pixel/4 for each point, in one map merge. Points outside the bounds are ignored.

rows(raster)

@spec rows(t()) :: [[color()]]

Returns every pixel as height lists of width colours, top row first.

Each element is a color/0; unwritten positions come back as the background.

to_rgba_binary(raster)

@spec to_rgba_binary(t()) :: binary()

Returns the raster as a dense row-major RGBA buffer.

The result is exactly width * height * 4 bytes, four bytes per pixel in red, green, blue, alpha order, rows top to bottom.