Crate palette[][src]

A library that makes linear color calculations and conversion easy and accessible for anyone. It uses the type system to enforce correctness and to avoid mistakes, such as mixing incompatible color types.

It’s Never “Just RGB”

Colors in, for example, images, are often “gamma corrected”, or converted using some non-linear transfer function into a format like sRGB before being stored or displayed. This is done as a compression method and to prevent banding, and is also a bit of a legacy from the ages of the CRT monitors, where the output from the electron gun was nonlinear. The problem is that these formats are non-linear color spaces, which means that many operations that you may want to perform on colors (addition, subtraction, multiplication, linear interpolation, etc.) will work unexpectedly when performed in such a non-linear color space. As such, the compression has to be reverted to restore linearity and make sure that many operations on the colors are accurate.

For example, this does not work:

// An alias for Rgb<Srgb>, which is what most pictures store.
use palette::Srgb;

let orangeish = Srgb::new(1.0, 0.6, 0.0);
let blueish = Srgb::new(0.0, 0.2, 1.0);
// let whateve_it_becomes = orangeish + blueish;

Instead, they have to be made linear before adding:

// An alias for Rgb<Srgb>, which is what most pictures store.
use palette::{Pixel, Srgb};

let orangeish = Srgb::new(1.0, 0.6, 0.0).into_linear();
let blueish = Srgb::new(0.0, 0.2, 1.0).into_linear();
let whateve_it_becomes = orangeish + blueish;

// Encode the result back into sRGB and create a byte array
let pixel: [u8; 3] = Srgb::from_linear(whateve_it_becomes)
    .into_format()
    .into_raw();

But, even when colors are ‘linear’, there is yet more to explore.

The most common way that colors are defined, especially for computer storage, is in terms of so-called tristimulus values, meaning that all colors are defined as a vector of three values which may represent any color. The reason colors can generally be stored as only a three dimensional vector, and not an n dimensional one, where n is some number of possible frequencies of light, is because our eyes contain only three types of cones. Each of these cones have different sensitivity curves to different wavelengths of light, giving us three “dimensions” of sensitivity to color. These cones are often called the S, M, and L (for small, medium, and large) cones, and their sensitivity curves roughly position them as most sensitive to “red”, “green”, and “blue” parts of the spectrum. As such, we can choose only three values to represent any possible color that a human is able to see. An interesting consequence of this is that humans can see two different objects which are emitting completely different actual light spectra as the exact same perceptual color so long as those wavelengths, when transformed by the sensitivity curves of our cones, end up resulting in the same S, M, and L values sent to our brains.

A color space (which simply refers to a set of standards by which we map a set of arbitrary values to real-world colors) which uses tristimulus values is often defined in terms of

  1. Its primaries
  2. Its reference white or white point

The primaries together represent the total gamut (i.e. displayable range of colors) of that color space, while the white point defines which concrete tristimulus value corresponds to a real, physical white reflecting object being lit by a known light source and observed by the ‘standard observer’ (i.e. a standardized model of human color perception).

The informal “RGB” color space is such a tristimulus color space, since it is defined by three values, but it is underspecified since we don’t know which primaries are being used (i.e. how exactly are the canonical “red”, “green”, and “blue” defined?), nor its white point. In most cases, when people talk about “RGB” or “Linear RGB” colors, what they are actually talking about is the “Linear sRGB” color space, which uses the primaries and white point defined in the sRGB standard, but which does not have the (non-linear) sRGB transfer function applied.

This library takes these things into account, and attempts to provide an interface which will let those who don’t care so much about the intricacies of color still use colors correctly, while also allowing the advanced user a high degree of flexibility in how they use it.

Transparency

There are many cases where pixel transparency is important, but there are also many cases where it becomes a dead weight, if it’s always stored together with the color, but not used. Palette has therefore adopted a structure where the transparency component (alpha) is attachable using the Alpha type, instead of having copies of each color space.

This approach comes with the extra benefit of allowing operations to selectively affect the alpha component:

use palette::{LinSrgb, LinSrgba};

let mut c1 = LinSrgba::new(1.0, 0.5, 0.5, 0.8);
let c2 = LinSrgb::new(0.5, 1.0, 1.0);

c1.color = c1.color * c2; //Leave the alpha as it is
c1.blue += 0.2; //The color components can easily be accessed
c1 = c1 * 0.5; //Scale both the color and the alpha

A Basic Workflow

The overall workflow can be divided into three steps, where the first and last may be taken care of by other parts of the application:

Decoding -> Processing -> Encoding

1. Decoding

Find out what the source format is and convert it to a linear color space. There may be a specification, such as when working with SVG or CSS.

When working with RGB or gray scale (luma):

use palette::{Srgb, Pixel};

// This works for any (even non-RGB) color type that can have the
// buffer element type as component.
let color_buffer: &mut [Srgb<u8>] = Pixel::from_raw_slice_mut(&mut image_buffer);

When working with other colors:

2. Processing

When your color has been decoded into some Palette type, it’s ready for processing. This includes things like blending, hue shifting, darkening and conversion to other formats. Just make sure that your non-linear RGB is made linear first (my_srgb.into_linear()), to make the operations available.

Different color spaced have different capabilities, pros and cons. You may have to experiment a bit (or look at the example programs) to find out what gives the desired result.

3. Encoding

When the desired processing is done, it’s time to encode the colors back into some image format. The same rules applies as for the decoding, but the process reversed.

Working with Raw Data

Oftentimes, pixel data is stored in a raw buffer such as a [u8; 3]. The Pixel trait allows for easy interoperation between Palette colors and other crates or systems. from_raw can be used to convert into a Palette color, into_format converts from Srgb<u8> to Srgb<f32>, and finally into_raw to convert from a Palette color back to a [u8;3].

use approx::assert_relative_eq;
use palette::{Srgb, Pixel};

let buffer = [255, 0, 255];
let raw = Srgb::from_raw(&buffer);
assert_eq!(raw, &Srgb::<u8>::new(255u8, 0, 255));

let raw_float: Srgb<f32> = raw.into_format();
assert_relative_eq!(raw_float, Srgb::new(1.0, 0.0, 1.0));

let raw: [u8; 3] = Srgb::into_raw(raw_float.into_format());
assert_eq!(raw, buffer);

Re-exports

pub use gradient::Gradient;
pub use luma::GammaLuma;
pub use luma::GammaLumaa;
pub use luma::LinLuma;
pub use luma::LinLumaa;
pub use luma::SrgbLuma;
pub use luma::SrgbLumaa;
pub use rgb::GammaSrgb;
pub use rgb::GammaSrgba;
pub use rgb::LinSrgb;
pub use rgb::LinSrgba;
pub use rgb::Srgb;
pub use rgb::Srgba;
pub use convert::FromColor;
pub use convert::IntoColor;
pub use encoding::pixel::Pixel;

Modules

blend

Color blending and blending equations.

chromatic_adaptation

Convert colors from one reference white point to another

convert

Traits for converting between color spaces.

encoding

Various encoding traits, types and standards.

float

Floating point trait

gradient

Types for interpolation between multiple colors.

luma

Luminance types.

named

A collection of named color constants. Can be toggled with the "named" and "named_from_str" Cargo features.

rgb

RGB types, spaces and standards.

white_point

Defines the tristimulus values of the CIE Illuminants.

Structs

Alpha

An alpha component wrapper for colors.

Hsl

HSL color space.

Hsluv

HSLuv color space.

Hsv

HSV color space.

Hwb

HWB color space.

Lab

The CIE L*a*b* (CIELAB) color space.

LabHue

A hue type for the CIE L*a*b* family of color spaces.

Lch

CIE L*C*h°, a polar version of CIE L*a*b*.

Lchuv

CIE L*C*uv h°uv, a polar version of CIE L*u*v*.

Luv

The CIE L*u*v* (CIELUV) color space.

LuvHue

A hue type for the CIE L*u*v* family of color spaces.

Packed

RGBA color packed into a 32-bit unsigned integer. Defaults to ARGB ordering for Rgb types and RGBA ordering for Rgba types.

RgbHue

A hue type for the RGB family of color spaces.

Xyz

The CIE 1931 XYZ color space.

Yxy

The CIE 1931 Yxy (xyY) color space.

Traits

Blend

A trait for colors that can be blended together.

Clamp

A trait for clamping and checking if colors are within their ranges.

ColorDifference

A trait for calculating the color difference between two colors.

Component

Common trait for color components.

ComponentWise

Perform a unary or binary operation on each component of a color.

FloatComponent

Common trait for floating point color components.

FromComponent

Converts from a color component type, while performing the appropriate scaling, rounding and clamping.

FromF64

A trait for infallible conversion from f64. The conversion may be lossy.

GetHue

A trait for colors where a hue may be calculated.

Hue

A trait for colors where the hue can be manipulated without conversion.

IntoComponent

Converts into a color component type, while performing the appropriate scaling, rounding and clamping.

Mix

A trait for linear color interpolation.

RelativeContrast

A trait for calculating relative contrast between two colors.

RgbChannels

Splits and combines RGB(A) types with some channel ordering. Channels may be ordered as Abgr, Argb, Bgra, or Rgba.

Saturate

A trait for colors where the saturation (or chroma) can be manipulated without conversion.

Shade

The Shade trait allows a color to be lightened or darkened.

WithAlpha

A trait for color types that can have or be given transparency (alpha channel).

Functions

contrast_ratio

Calculate the ratio between two luma values.

Type Definitions

Hsla

Linear HSL with an alpha component. See the Hsla implementation in Alpha.

Hsluva

HSLuv with an alpha component. See the Hsluva implementation in Alpha.

Hsva

Linear HSV with an alpha component. See the Hsva implementation in Alpha.

Hwba

Linear HWB with an alpha component. See the Hwba implementation in Alpha.

Laba

CIE L*a*b* (CIELAB) with an alpha component. See the Laba implementation in Alpha.

Lcha

CIE L*C*h° with an alpha component. See the Lcha implementation in Alpha.

Lchuva

CIE L*C*uv h°uv with an alpha component. See the Lchuva implementation in Alpha.

Luva

CIE L*u*v* (CIELUV) with an alpha component. See the Luva implementation in Alpha.

Mat3

A 9 element array representing a 3x3 matrix.

Xyza

CIE 1931 XYZ with an alpha component. See the Xyza implementation in Alpha.

Yxya

CIE 1931 Yxy (xyY) with an alpha component. See the Yxya implementation in Alpha.

Derive Macros

Pixel
WithAlpha