Drafter.Color (drafter v0.3.1)

Copy Markdown View Source

Color representation and parsing for terminal styling.

Accepts hex ("#RGB", "#RRGGBB"), RGB ("rgb(r,g,b)", "rgba(r,g,b,a)"), and HSL ("hsl(h,s%,l%)", "hsla(h,s%,l%,a)") strings, as well as {r, g, b} and {r, g, b, a} tuples.

parse/1 returns {:ok, t()} or {:error, reason} and handles strings and tuples only. normalize/1 covers the same inputs plus the named-colour atoms listed below, converting any of them to a plain {r, g, b} tuple for segment styles and falling back to white on anything it does not recognise.

Named colours

:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white, and a :bright_-prefixed variant of each. These are the palette values, not the terminal's own ANSI colours.

Examples

iex> Drafter.Color.normalize("#f00")
{255, 0, 0}

iex> Drafter.Color.normalize("rgb(10, 20, 30)")
{10, 20, 30}

iex> Drafter.Color.normalize(:black)
{0, 0, 0}

iex> Drafter.Color.normalize("not a color")
{255, 255, 255}

iex> Drafter.Color.parse("nope")
{:error, :invalid_format}

Summary

Types

Anything normalize/1 accepts: a colour string, a tuple, a struct, or a named atom.

A plain RGB triple, as carried on segment styles.

t()

Functions

Build a colour from hue in degrees, saturation and lightness in percent.

Build a colour from channel values.

Convert any supported colour input to a plain {r, g, b} triple.

Normalize a colour to {{r, g, b}, alpha}, keeping the alpha channel.

Parse a colour string or tuple into a t/0.

Hue in degrees, saturation and lightness in percent, for a colour.

The colour's {r, g, b} triple, dropping alpha.

The colour's {r, g, b, a} tuple, keeping alpha.

Types

input()

@type input() :: String.t() | rgb() | {0..255, 0..255, 0..255, number()} | atom()

Anything normalize/1 accepts: a colour string, a tuple, a struct, or a named atom.

rgb()

@type rgb() :: {0..255, 0..255, 0..255}

A plain RGB triple, as carried on segment styles.

t()

@type t() :: %Drafter.Color{a: float(), b: 0..255, g: 0..255, r: 0..255}

Functions

from_hsl(h, s, l, a \\ 1.0)

@spec from_hsl(number(), number(), number(), number()) :: t()

Build a colour from hue in degrees, saturation and lightness in percent.

Hue wraps, so -60 and 300 are the same. Saturation and lightness are clamped to 0..100. a defaults to 1.0 and is clamped into 0.0..1.0.

Examples

iex> Drafter.Color.from_hsl(0, 100, 50)
%Drafter.Color{r: 255, g: 0, b: 0, a: 1.0}

iex> Drafter.Color.from_hsl(240, 100, 50)
%Drafter.Color{r: 0, g: 0, b: 255, a: 1.0}

iex> Drafter.Color.from_hsl(-240, 100, 50) == Drafter.Color.from_hsl(120, 100, 50)
true

new(r, g, b, a \\ 1.0)

@spec new(0..255, 0..255, 0..255, number()) :: t()

Build a colour from channel values.

Each of r, g and b must be in 0..255; anything else raises FunctionClauseError. a is clamped into 0.0..1.0 rather than rejected. Default: 1.0.

Examples

iex> Drafter.Color.new(10, 20, 30)
%Drafter.Color{r: 10, g: 20, b: 30, a: 1.0}

iex> Drafter.Color.new(10, 20, 30, 2.5)
%Drafter.Color{r: 10, g: 20, b: 30, a: 1.0}

normalize(color)

@spec normalize(input() | term()) :: rgb()

Convert any supported colour input to a plain {r, g, b} triple.

Alpha is dropped. Unrecognised strings, unknown atoms and anything else fall back to white, {255, 255, 255} — this function never raises and never returns an error tuple. Use parse/1 when a bad colour should be reported rather than substituted.

Examples

iex> Drafter.Color.normalize("#00ff00")
{0, 255, 0}

iex> Drafter.Color.normalize({1, 2, 3, 0.5})
{1, 2, 3}

iex> Drafter.Color.normalize(:bright_white)
{255, 255, 255}

iex> Drafter.Color.normalize(:no_such_colour)
{255, 255, 255}

normalize_with_alpha(color)

@spec normalize_with_alpha(term()) :: {rgb(), float()}

Normalize a colour to {{r, g, b}, alpha}, keeping the alpha channel.

Accepts the same inputs as normalize/1. alpha is a float in 0.0..1.0; a colour with no alpha channel comes back as 1.0. Use this where the alpha is still needed — the compositor blends against the cell beneath — and normalize/1 where a plain RGB triple is wanted.

Examples

iex> Drafter.Color.normalize_with_alpha("rgba(1,2,3,0.5)")
{{1, 2, 3}, 0.5}

iex> Drafter.Color.normalize_with_alpha("#f00")
{{255, 0, 0}, 1.0}

iex> Drafter.Color.normalize_with_alpha(:black)
{{0, 0, 0}, 1.0}

iex> Drafter.Color.normalize_with_alpha("not a color")
{{255, 255, 255}, 1.0}

parse(color)

@spec parse(term()) ::
  {:ok, t()}
  | {:error,
     :invalid_hex
     | :invalid_hex_length
     | :invalid_rgb
     | :invalid_hsl
     | :invalid_tuple
     | :invalid_format}

Parse a colour string or tuple into a t/0.

Named-colour atoms are not accepted here — use normalize/1 for those.

Error reasons are :invalid_hex, :invalid_hex_length, :invalid_rgb, :invalid_hsl, :invalid_tuple (a tuple that is not a valid RGB/RGBA tuple), and :invalid_format for everything else.

Examples

iex> Drafter.Color.parse("#abc")
{:ok, %Drafter.Color{r: 170, g: 187, b: 204, a: 1.0}}

iex> Drafter.Color.parse("rgba(1,2,3,0.5)")
{:ok, %Drafter.Color{r: 1, g: 2, b: 3, a: 0.5}}

iex> Drafter.Color.parse({1, 2, 3})
{:ok, %Drafter.Color{r: 1, g: 2, b: 3, a: 1.0}}

iex> Drafter.Color.parse("#gg00zz")
{:error, :invalid_hex}

iex> Drafter.Color.parse("#12345")
{:error, :invalid_hex_length}

iex> Drafter.Color.parse({1, 2})
{:error, :invalid_tuple}

iex> Drafter.Color.parse(:black)
{:error, :invalid_format}

to_hsl(color)

@spec to_hsl(t()) :: {number(), number(), number()}

Hue in degrees, saturation and lightness in percent, for a colour.

Alpha is dropped. For a fully desaturated colour hue and saturation come back as the integer 0, so compare numerically rather than by pattern match.

Examples

iex> Drafter.Color.to_hsl(Drafter.Color.new(255, 0, 0))
{0.0, 100.0, 50.0}

iex> Drafter.Color.to_hsl(Drafter.Color.new(0, 0, 0))
{0, 0, 0.0}

to_tuple(color)

@spec to_tuple(t()) :: rgb()

The colour's {r, g, b} triple, dropping alpha.

Examples

iex> Drafter.Color.to_tuple(Drafter.Color.new(1, 2, 3, 0.5))
{1, 2, 3}

to_tuple_with_alpha(color)

@spec to_tuple_with_alpha(t()) :: {0..255, 0..255, 0..255, float()}

The colour's {r, g, b, a} tuple, keeping alpha.

Examples

iex> Drafter.Color.to_tuple_with_alpha(Drafter.Color.new(1, 2, 3, 0.5))
{1, 2, 3, 0.5}