Bridges the 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.
Opacity, alpha and color
An opacity (opacity/0) is the value a caller supplies,
expressed relative to full opacity rather than to any image:
:transparent, :opaque, a float in 0.0..1.0, or an integer
in 0..255 as the same value in 8-bit notation.
An alpha is what an opacity becomes, the content of an image's
alpha band. alpha_for/2 scales an opacity to a given image's
band, which runs to 65535 for 16-bit and 1.0 for scRGB, and
put_alpha/3 sets it on an existing pixel. Prefer a float when
the alpha band is not 8-bit: an integer is a fraction of 255, so
it cannot reach every value of a 16-bit or scRGB band.
A color may be :none, :transparent or :opaque on top of
everything Color.new/2 accepts. All three resolve to black, the
first two fully transparent.
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}
Summary
Types
How opaque something should be, expressed relative to full opacity rather than to any particular image's alpha band.
Anything that to_pixel/3 knows how to turn into a pixel.
Functions
Returns an alpha value scaled to the alpha band of image.
Same as alpha_for/2, but raises on error.
A defguard that loosely matches things that look like a pixel input.
Returns an opacity as a fraction of full opacity.
Same as opacity_fraction/1, but raises on error.
Returns pixel with its alpha component set to opacity, scaled
to the alpha band of image.
Same as put_alpha/3, but raises on error.
Returns a resolved pixel without its alpha component, if it has one.
Converts a color to a pixel matching the interpretation and band
layout of image.
Same as to_pixel/3, but raises on error.
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.
Same as to_srgb/1, but raises on error.
Types
@type opacity() :: :transparent | :opaque | 0..255 | float()
How opaque something should be, expressed relative to full opacity rather than to any particular image's alpha band.
A float in 0.0..1.0 is the canonical form, being a fraction of
full opacity. An integer in 0..255 is that same value in 8-bit
notation, normalized as n / 255. The integer form does not
imply that the target's alpha band is 8-bit: 128 means 128/255
on a 16-bit image too, not 128/65535.
1 and 1.0 differ
1 is 8-bit notation for 1/255, which is very nearly
transparent. Fully opaque is 1.0 or :opaque.
:transparent is 0.0 and :opaque is 1.0. :none is not an
opacity, only a color meaning no color at all.
alpha_for/2 scales an opacity to the alpha band of a given image,
which is the range 0..65535 for a 16-bit image and 0.0..1.0 for
an scRGB one.
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 :none, :transparent and
:opaque.
Functions
@spec alpha_for(image :: Vix.Vips.Image.t(), opacity :: opacity()) :: {:ok, number()} | {:error, Image.Error.t()}
Returns an alpha value scaled to the alpha band of image.
The result is in the range image actually uses: 0..65535 for
16-bit images, 0.0..1.0 for scRGB, and 0..255 for everything
else, including Lab and LCH despite their float color bands.
The result is a band value, to be written into a pixel. It is
not an opacity and must not be passed back where one is expected,
such as Image.add_alpha/2 or the :opacity option of
to_pixel/3, which would scale it a second time.
Arguments
imageis anyVix.Vips.Image.t/0.opacityis anyopacity/0.
Returns
{:ok, number}or{:error, reason}.
Examples
iex> image = Image.new!(2, 2, color: :black)
iex> Image.Pixel.alpha_for(image, :opaque)
{:ok, 255}
iex> image = Image.new!(2, 2, color: :black)
iex> Image.Pixel.alpha_for(Image.to_colorspace!(image, :rgb16), 0.5)
{:ok, 32768}
iex> image = Image.new!(2, 2, color: :black)
iex> Image.Pixel.alpha_for(Image.to_colorspace!(image, :scrgb), :opaque)
{:ok, 1.0}
@spec alpha_for!(image :: Vix.Vips.Image.t(), opacity :: opacity()) :: number()
Same as alpha_for/2, but raises on error.
Examples
iex> image = Image.new!(2, 2, color: :black)
iex> Image.Pixel.alpha_for!(image, :opaque)
255
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).
@spec opacity_fraction(opacity :: opacity()) :: {:ok, float()} | {:error, Image.Error.t()}
Returns an opacity as a fraction of full opacity.
The 0.0..1.0 float is the canonical form of an opacity, so this
is the identity for a float and n / 255 for an integer. Unlike
alpha_for/2, the result belongs to no particular image.
Arguments
opacityis anyopacity/0.
Returns
{:ok, float}in0.0..1.0, or{:error, t:Image.Error.t/0}.
Examples
iex> Image.Pixel.opacity_fraction(:opaque)
{:ok, 1.0}
iex> Image.Pixel.opacity_fraction(0.5)
{:ok, 0.5}
iex> Image.Pixel.opacity_fraction(128)
{:ok, 0.5019607843137255}
Same as opacity_fraction/1, but raises on error.
Examples
iex> Image.Pixel.opacity_fraction!(:transparent)
0.0
@spec put_alpha( pixel :: [number()], image :: Vix.Vips.Image.t(), opacity :: opacity() ) :: {:ok, [number()]} | {:error, Image.Error.t()}
Returns pixel with its alpha component set to opacity, scaled
to the alpha band of image.
The inverse of strip_alpha/2, and like it a no-op on an image
with no alpha band, since there is no component to set. opacity
is validated either way.
Arguments
pixelis a list of numbers already inimage's interpretation.imageis anyVix.Vips.Image.t/0. Its interpretation determines the alpha the opacity scales to.opacityis anyopacity/0.
Returns
{:ok, pixel}with its last component replaced, or withpixelunchanged ifimagehas no alpha band, or{:error, reason}.
Examples
iex> {:ok, image} = Image.new(2, 2, color: [0, 0, 0, 255])
iex> Image.Pixel.put_alpha([255, 0, 0, 255], image, 0.5)
{:ok, [255, 0, 0, 128]}
iex> {:ok, image} = Image.new(2, 2, color: :black)
iex> Image.Pixel.put_alpha([255, 0, 0], image, 0.5)
{:ok, [255, 0, 0]}
@spec put_alpha!( pixel :: [number()], image :: Vix.Vips.Image.t(), opacity :: opacity() ) :: [number()]
Same as put_alpha/3, but raises on error.
Examples
iex> image = Image.new!(2, 2, color: [0, 0, 0, 255])
iex> Image.Pixel.put_alpha!([255, 0, 0, 255], image, 0.5)
[255, 0, 0, 128]
@spec strip_alpha(pixel :: [number()], image :: Vix.Vips.Image.t()) :: [number()]
Returns a resolved pixel without its alpha component, if it has one.
Arguments
pixelis a list of numbers inimage's band layout, such as the output ofto_pixel/3.imageis anyVix.Vips.Image.t/0.
Returns
pixelwithout its last element ifimagehas an alpha band andpixelspans all ofimage's bands, orpixelunchanged. A pixel that does not span the image's bands exactly has no identifiable alpha component, so it is left alone.
Notes
- The band count is the only check, so a pixel resolved against another image of the same band count is truncated just the same.
Examples
iex> {:ok, image} = Image.new(2, 2, color: [0, 0, 0, 255])
iex> {:ok, pixel} = Image.Pixel.to_pixel(image, :red)
iex> Image.Pixel.strip_alpha(pixel, image)
[255, 0, 0]
iex> {:ok, image} = Image.new(2, 2, color: :black)
iex> {:ok, pixel} = Image.Pixel.to_pixel(image, :red)
iex> Image.Pixel.strip_alpha(pixel, image)
[255, 0, 0]
@spec to_pixel( image :: Vix.Vips.Image.t() | Vix.Vips.MutableImage.t(), color :: t(), options :: Keyword.t() ) :: {:ok, [number()]} | {:error, Image.Error.t()}
Converts a color to a pixel matching the interpretation and band
layout of image.
Arguments
imageis the targetVix.Vips.Image.t/0. Its interpretation and band count determine the output shape and value range.coloris anythingColor.new/2accepts: aColor.*struct, a list of 3/4/5 numbers, a hex string ("#ff0000","#f80","#ff000080"), a CSS named color ("rebeccapurple",:misty_rose), or:none,:transparentor:opaque.optionsis a keyword list — see below.
Options
:opacity— if the target image has an alpha band, force this opacity. Accepts anyopacity/0. If unset, the input color's own alpha is used, or full opacity if it has none.:intent— passed through toColor.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 in0..255.For 16-bit interpretations (
:rgb16,:grey16) the output is integers in0..65535.For
:scrgb,:laband:lchthe color bands are floats in the natural range of that space, and:labsuses 16-bit integers. The alpha component is0.0..1.0for:scrgband0..255for the other three.:scrgbis linear light: mid grey"#808080"encodes as0.216, not0.502.The output band count matches
Vix.Vips.Image.bands/1exactly. Alpha is appended when the image has an alpha band, and stripped when it does not.1-band
:bwand:grey16images receive a single luminance channel computed from the perceptually-uniformColor.LabL*, as does any other interpretation that arrives with one band.1-band
:scrgbis the exception: it holds linear light, so it receives relative luminance computed fromColor.XYZY.
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, opacity: 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]}
@spec to_pixel!(image :: Vix.Vips.Image.t(), color :: t(), options :: Keyword.t()) :: [number()]
Same as to_pixel/3, but raises on error.
Examples
iex> image = Image.new!(2, 2, color: :black)
iex> Image.Pixel.to_pixel!(image, :red)
[255, 0, 0]
iex> image = Image.new!(2, 2, color: [0, 0, 0, 255])
iex> Image.Pixel.to_pixel!(image, :red, opacity: 0.5)
[255, 0, 0, 128]
@spec to_srgb(color :: t()) :: {:ok, [0..255]} | {:error, Image.Error.t()}
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
coloris anythingColor.new/2accepts, plus:none,:transparentand:opaque.
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]}
@spec to_srgb!(color :: t()) :: [0..255]
Same as to_srgb/1, but raises on error.
Examples
iex> Image.Pixel.to_srgb!(:red)
[255, 0, 0]
iex> Image.Pixel.to_srgb!("#ff000080")
[255, 0, 0, 128]