# `Image.Pixel`
[🔗](https://github.com/elixir-image/image/blob/v0.65.0/lib/image/pixel.ex#L1)

Bridges the [`Color`](https://hexdocs.pm/color) library and libvips
pixel arguments.

Every libvips operation that takes a color (background, fill, draw,
flatten, embed, …) ultimately wants a flat list of numbers in the
interpretation, value range, and band layout of the image it is
operating on. This module owns that conversion so that callers can
pass user-friendly inputs (atoms, hex strings, `Color.*` structs,
numeric lists) without worrying about whether the target image is
sRGB, Lab, scRGB, CMYK, or 16-bit.

## Example

    iex> {:ok, image} = Image.new(2, 2, color: :black)
    iex> Image.Pixel.to_pixel(image, :red)
    {:ok, [255, 0, 0]}

    iex> {:ok, image} = Image.new(2, 2, color: :black)
    iex> {:ok, lab_image} = Image.to_colorspace(image, :lab)
    iex> {:ok, [l, a, b]} = Image.Pixel.to_pixel(lab_image, :red)
    iex> {Float.round(l, 2), Float.round(a, 2), Float.round(b, 2)}
    {53.24, 80.09, 67.2}

# `t`

```elixir
@type t() :: struct() | [number()] | String.t() | atom()
```

Anything that `to_pixel/3` knows how to turn into a pixel.

This includes any input accepted by `Color.new/2` (a `Color.*`
struct, a numeric list of length 3..5, a hex string, a CSS named
color string or atom), plus the Image-specific transparency aliases
`:none`, `:transparent`, and `:opaque`.

# `transparency`

```elixir
@type transparency() :: :none | :transparent | :opaque | 0..255 | float()
```

A transparency value.

* `:none` and `:transparent` are equivalent to `0` (fully transparent).
* `:opaque` is equivalent to `255` (fully opaque).
* An integer in `0..255` is used as-is.
* A float in `0.0..1.0` is scaled to `0..255`.

# `is_pixel`
*macro* 

A defguard that loosely matches things that look like a pixel input.

This is intentionally permissive — it accepts anything that *might*
be a color (struct, numeric list, atom that isn't a boolean,
binary). Actual validation happens in `to_pixel/3`.

Use this in function-head guards where you need to dispatch a color
argument away from an image argument (the `Image.if_then_else/4`
pattern).

# `max_opacity`

```elixir
@spec max_opacity() :: 255
```

The maximum opacity value (255).

# `min_opacity`

```elixir
@spec min_opacity() :: 0
```

The minimum opacity value (0).

# `to_pixel`

```elixir
@spec to_pixel(
  image :: Vix.Vips.Image.t() | Vix.Vips.MutableImage.t(),
  color :: t(),
  options :: Keyword.t()
) :: {:ok, [number()]} | {:error, String.t()}
```

Converts a color to a pixel matching the interpretation and band
layout of `image`.

### Arguments

* `image` is the target `t:Vix.Vips.Image.t/0`. Its interpretation
  and band count determine the output shape and value range.

* `color` is anything `Color.new/2` accepts: a `Color.*` struct, a
  list of 3/4/5 numbers, a hex string (`"#ff0000"`, `"#f80"`,
  `"#ff000080"`), a CSS named color (`"rebeccapurple"`,
  `:misty_rose`), or one of Image's transparency aliases (`:none`,
  `:transparent`, `:opaque`).

* `options` is a keyword list — see below.

### Options

* `:alpha` — if the target image has an alpha band, force this
  transparency. Accepts any value `transparency/1` accepts. If
  unset, the input color's own alpha is used (or full opacity if
  none).

* `:intent` — passed through to `Color.convert/3`. One of
  `:relative_colorimetric` (default), `:absolute_colorimetric`,
  `:perceptual`, or `:saturation`.

### Returns

* `{:ok, [number(), ...]}` — a flat list of numbers in the band
  order and pixel range that the image's interpretation expects.

* `{:error, reason}`.

### Notes

* For 8-bit interpretations (`:srgb`, `:rgb`, `:cmyk`, `:hsv`,
  `:bw`) the output is integers in `0..255`.

* For 16-bit interpretations (`:rgb16`, `:grey16`) the output is
  integers in `0..65535`.

* For float interpretations (`:scrgb`, `:lab`, `:lch`, etc.) the
  output is floats in the natural range of that space.

* The output band count matches `Vix.Vips.Image.bands/1` exactly.
  Alpha is appended when the image has an alpha band, and stripped
  when it does not.

* 1-band (`:bw`, `:grey16`) images receive a single luminance
  channel computed from the perceptually-uniform `Color.Lab` `L*`.

### Examples

    iex> {:ok, image} = Image.new(2, 2, color: :black)
    iex> Image.Pixel.to_pixel(image, :red)
    {:ok, [255, 0, 0]}

    iex> {:ok, image} = Image.new(2, 2, color: [0, 0, 0, 255])
    iex> Image.Pixel.to_pixel(image, :red)
    {:ok, [255, 0, 0, 255]}

    iex> {:ok, image} = Image.new(2, 2, color: [0, 0, 0, 255])
    iex> Image.Pixel.to_pixel(image, :red, alpha: 0.5)
    {:ok, [255, 0, 0, 128]}

    iex> {:ok, image} = Image.new(2, 2, color: :black)
    iex> Image.Pixel.to_pixel(image, "#ff000080")
    {:ok, [255, 0, 0]}

# `to_pixel!`

```elixir
@spec to_pixel!(image :: Vix.Vips.Image.t(), color :: t(), options :: Keyword.t()) ::
  [number()]
```

Same as `to_pixel/3`, but raises on error.

# `to_srgb`

```elixir
@spec to_srgb(color :: t()) :: {:ok, [0..255]} | {:error, Image.Error.t() | term()}
```

Resolves a color input to an sRGB pixel `[r, g, b]` (or
`[r, g, b, a]`) with channels in `0..255`, regardless of any
image context.

Use this for callers that need sRGB output specifically — for
example SVG renderers — rather than the interpretation of an
image. For image-aware encoding use `to_pixel/3`.

### Arguments

* `color` is anything `Color.new/2` accepts, plus the
  transparency aliases.

### Returns

* `{:ok, [0..255, 0..255, 0..255]}` or
  `{:ok, [0..255, 0..255, 0..255, 0..255]}` if the source had an
  alpha channel.

* `{:error, reason}`.

### Examples

    iex> Image.Pixel.to_srgb(:red)
    {:ok, [255, 0, 0]}

    iex> Image.Pixel.to_srgb("#ff000080")
    {:ok, [255, 0, 0, 128]}

    iex> Image.Pixel.to_srgb(%Color.Lab{l: 53.24, a: 80.09, b: 67.20})
    {:ok, [255, 0, 0]}

# `to_srgb!`

```elixir
@spec to_srgb!(color :: t()) :: [0..255]
```

Same as `to_srgb/1`, but raises on error.

# `transparency`

```elixir
@spec transparency(value :: transparency()) ::
  {:ok, 0..255} | {:error, Image.Error.t()}
```

Returns a transparency value in `0..255` where `0` is transparent
and `255` is opaque.

### Arguments

* `transparency` is one of:

  * The atoms `:none`, `:transparent`, or `:opaque`.

  * An integer in `0..255`.

  * A float in `0.0..1.0`.

### Returns

* `{:ok, 0..255}` or

* `{:error, reason}`.

### Examples

    iex> Image.Pixel.transparency(:opaque)
    {:ok, 255}

    iex> Image.Pixel.transparency(:transparent)
    {:ok, 0}

    iex> Image.Pixel.transparency(0.5)
    {:ok, 128}

    iex> Image.Pixel.transparency(200)
    {:ok, 200}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
