Expand description
§colr
A general purpose, extensible color type unifying storage, channel layouts, and color spaces at the type level.
Color spaces, transfer functions, chromatic adaptation, and channel layouts are encoded in the type system. Mixing an sRGB color with a Display P3 color is a compile error unless you convert explicitly. All colorimetric matrices are derived at compile time. Colors are generic over their storage representation, which is itself generic over its channel layout.
§Usage
use colr::{Color, LinearSrgb, Oklab, Oklch, Srgb, Xyz};
// Construct an sRGB color. Defaults to RGBA layout.
let srgb: Color<[f32; 4], Srgb> = Color::new([0.8, 0.4, 0.2, 1.0]);
// Decode to linear light before converting. The transfer function must be
// removed before the RGB-to-XYZ matrix is applied.
let linear: Color<[f32; 4], LinearSrgb> = srgb.decode();
// Convert to Oklab for perceptual work. Each step is an explicit From.
// All matrices are compile-time constants. Xyz defaults to D65.
// Alpha is carried through each conversion unchanged.
let xyz: Color<[f32; 4], Xyz> = linear.into();
let lab: Color<[f32; 4], Oklab> = xyz.into();
// Pull out chroma and hue, reduce chroma, put it back.
let lch: Color<[f32; 4], Oklch> = lab.into();
let [l, c, h, a] = lch.inner();
let muted: Color<[f32; 4], Oklch> = Color::new([l, c * 0.5, h, a]);
// Convert back. XYZ-to-RGB may produce out-of-range values for wide-gamut
// colors; clamp if targeting a display, or pass through for tone mapping.
let oklab_out: Color<[f32; 4], Oklab> = muted.into();
let xyz_out: Color<[f32; 4], Xyz> = oklab_out.into();
let linear_out: Color<[f32; 4], LinearSrgb> = xyz_out.into();
let final_color: Color<[f32; 4], Srgb> = linear_out.encode();§Color models and spaces
§Perceptual spaces
| Type | Description |
|---|---|
Lab | CIE 1976 L*a*b* parameterized by reference white W |
LCh | Polar form of L*a*b* |
Oklab | Oklab (Ottosson 2020); improved hue linearity over Lab |
Oklch | Polar form of Oklab |
Lab and LCh are parameterized by illuminant: Lab<D65>, Lab<D50>, and so on, with D65 as the default. Oklab and Oklch have D65 baked into the specification.
All four types accept a const OFFSET parameter that controls where the color channels begin within a four-channel array. The default OFFSET = 0 places color channels at indices 0, 1, 2 and alpha at 3. OFFSET = 1 places alpha at 0 and color channels at 1, 2, 3. This allows in-place processing of ARGB-layout buffers without swizzling: reinterpret the buffer as Oklab<1>, operate, reinterpret back.
§RGB spaces
| Alias | Primaries | Transfer function | White |
|---|---|---|---|
Srgb | sRGB/Rec709 | sRGB | D65 |
LinearSrgb | sRGB/Rec709 | Linear | D65 |
Rec709 | sRGB/Rec709 | Rec. 709 | D65 |
DisplayP3 | P3 | sRGB | D65 |
LinearP3 | P3 | Linear | D65 |
Hdr10 | Rec. 2020 | PQ (ST 2084) | D65 |
Hlg | Rec. 2020 | HLG (BT.2100) | D65 |
LinearRec2020 | Rec. 2020 | Linear | D65 |
AcesCg | AP1 | Linear | ACES |
Aces2065 | AP0 | Linear | ACES |
AcesCc | AP1 | ACEScc log | ACES |
AcesCct | AP1 | ACEScct log+toe | ACES |
ProPhoto | ProPhoto | gamma 1.8 | D50 |
LinearProPhoto | ProPhoto | Linear | D50 |
DciP3 | DCI-P3 | gamma 2.6 | DCI |
Each alias defaults to RGBA layout. Alternate layouts are selected with the type parameter: Srgb<Rgb>, Srgb<Bgra>, and so on.
§XYZ
Xyz<W> is the CIE XYZ tristimulus space parameterized by illuminant W, defaulting to Xyz<D65>. It serves as the connection space for all RGB and perceptual conversions.
Conversions between Xyz<W1> and Xyz<W2> apply Bradford chromatic adaptation by default, computed at compile time. The adaptation method can be overridden with .adapt::<W2, CAT>().
§Luma
Luma<P, TF> is a single-channel luminance model derived from primary set P with transfer function TF. LumaAlpha<P, TF, A> adds an alpha channel with alpha state A (defaults to Straight). The luma weights are the Y row of the primaries’ RGB-to-XYZ matrix, so they vary by primary set.
§YCbCr
YCbCr<P, TF, L> is Y’CbCr derived from RGB primaries P and transfer function TF. The conversion matrix is derived from the primaries’ luma weights. Used in JPEG, H.264, H.265, and broadcast video. Layout defaults to Ycbcr.
§Spectral
Spectral<N, G, K> carries N spectral samples over wavelength grid G with physical kind K. Three kinds are defined:
| Kind marker | Physical quantity | Conversion to XYZ |
|---|---|---|
IsRadiance | Spectral radiance | SPD dot CMF |
IsReflectance | Spectral reflectance [0,1] | (illuminant * reflectance) dot CMF |
IsTransmittance | Spectral transmittance [0,1] | same as reflectance |
IsBispectral is defined as a kind marker to close the taxonomy but does not implement BackingStore; bispectral (fluorescent) storage is reserved for a future explicit design.
Standard grids include Grid380_780_10nm (41 bands), Grid400_700_10nm (31 band ICC standard), and Grid380_780_5nm (81 bands), all evaluated against the CIE 1931 observer.
Blanket alias traits Radiance, Reflectance, Transmittance, and Bispectral are provided for use in bounds.
§Device-dependent
| Type | Description |
|---|---|
Cmyk | Device CMYK. Profile required for XYZ conversion |
DeviceRgb | Device RGB. Profile required for XYZ conversion |
Conversions for these types use FromDevice rather than From. See the Device-dependent colors section.
If a color space or model from an open standard is not listed above, please open an issue.
§Conversions
All conversions use From/Into. XYZ-to-RGB is infallible and may return out-of-range channel values for wide-gamut colors. Clamp with .clamp(0.0, 1.0) for display output, or pass the values directly to a tone mapper.
let linear: Color<[f32; 4], LinearSrgb> = src.into();
let display_ready: Color<[f32; 4], LinearSrgb> = linear.clamp(0.0, 1.0);Encode and decode transfer functions with .encode() and .decode(). Adapt a color between XYZ white points with .adapt::<W2, CAT>() on a Color<_, Xyz<W1>>.
§Device-dependent colors
Cmyk and DeviceRgb<L> are device-dependent color models whose relationship to XYZ is defined by a runtime ICC profile. Because no compile-time matrix describes them, they do not implement From. Conversions use FromDevice from colr::device:
pub trait FromDevice<Src>: Sized {
type Profile;
fn from_device(src: Src, profile: &Self::Profile) -> Self;
}The Profile associated type is set by each impl. A profile type defined externally can add FromDevice impls for Cmyk or DeviceRgb without changes to this crate.
§Channel layouts
Layout is a type parameter, not a runtime tag. The compiler distinguishes RGBA from BGRA and will not accept one where the other is expected.
| Type | Order |
|---|---|
Rgba | R G B A |
Bgra | B G R A |
Argb | A R G B |
Abgr | A B G R |
Rgb | R G B |
Bgr | B G R |
Layout reorders are infallible and compile to a shuffle with no arithmetic:
let bgra: Color<[f32; 4], Srgb<Bgra>> = rgba.swizzle();§Chromatic adaptation
Four methods are provided. Bradford is used automatically by all built-in conversions and is the correct default for standard RGB work.
| Type | Standard |
|---|---|
Bradford | ICC, ACES, CSS Color Level 4 |
Cat02 | CIECAM02, ICC v4 appearance models |
Cat16 | CAM16 |
VonKries | Legacy and reference |
To adapt a color explicitly:
let xyz_d50: Color<[f32; 3], Xyz<D50>> = xyz_d65.adapt::<D50, Bradford>();Custom adaptation matrices can be computed at compile time with operations::adapt::adapt.
§Color difference
| Method | Space | Formula |
|---|---|---|
delta_e76 | Lab | CIE 1976 (Euclidean) |
delta_e2000 | Lab | CIEDE2000 (CIE 142-2001) |
delta_e_ok | Oklab | Euclidean in Oklab |
let delta: f32 = a.delta_e_ok(b);§Tone mapping
Tone mapping compresses unbounded scene-linear light [0, inf) down to bounded display-linear light [0, 1].
| Type | Description |
|---|---|
AcesNarkowicz | ACES filmic curve approximation (per-channel) |
KhronosPbrNeutral | glTF 3D Commerce standard, hue-preserving (RGB-coupled) |
Reinhard | Classic x / (x + 1) curve (per-channel) |
let display_linear = scene_linear.tonemap::<KhronosPbrNeutral>();§Quantization and dithering
Converting continuous f32 signals to 8-bit u8 integers without dithering causes visible banding. colr provides a way to convert with a dithering offset as well as straight. The to_u8 method performs naive quantization while to_u8_dithered method dithers the color channels leaving the alpha channel untouched.
// Generate two independent uniform random floats in [0, 1)
let dither = (u1 - u2) / 255.0;
let pixel: Color<[u8; 4], Srgb> = color.to_u8_dithered(dither);§Alpha compositing
Blend operations are defined on Color<[f32; 4], Rgb<P, Linear, L>> and require premultiplied alpha. Compositing in non-linear space is incorrect.
let result = src.premultiply().over(dst.premultiply()).unpremultiply();| Method | Description |
|---|---|
premultiply | Multiply color channels by alpha |
unpremultiply | Divide color channels by alpha |
over | Porter-Duff over (both premultiplied) |
§no_std support
no_std is available with the libm feature. Either std or libm must be enabled.
[dependencies]
colr = { version = "0.2", default-features = false, features = ["libm"] }To support both std and no_std builds in a library crate:
[features]
default = ["std"]
std = ["colr/std"]
libm = ["colr/libm"]
[dependencies]
colr = { version = "0.2", default-features = false }§Optional features
std is enabled by default and selects standard library math functions. The remaining features are opt-in.
| Feature | Description |
|---|---|
glam | Adds VecN types as valid color storage |
libm | Uses libm math functions for no_std targets |
§Adding a custom primary set
Implement Primaries for your type. All matrices are derived at compile time from the chromaticity coordinates and white point you supply.
use colr::primaries::{Primaries, derive_rgb_to_xyz};
use colr::illuminant::{D65, Illuminant};
use colr::math::Mat3;
pub struct CustomPrimaries;
const CUSTOM_TO_XYZ: Mat3 = derive_rgb_to_xyz(
[0.680, 0.320], // red
[0.265, 0.690], // green
[0.150, 0.060], // blue
<D65 as Illuminant>::WHITE_POINT_XYZ,
);
impl Primaries for CustomPrimaries {
type Native = D65;
const R: [f32; 2] = [0.680, 0.320];
const G: [f32; 2] = [0.265, 0.690];
const B: [f32; 2] = [0.150, 0.060];
const TO_XYZ_NATIVE: Mat3 = CUSTOM_TO_XYZ;
const FROM_XYZ_NATIVE: Mat3 = Mat3::invert(&CUSTOM_TO_XYZ);
}§colr-types
The colr-types crate provides the color model ZSTs and marker traits without Color<S, M> or its operations. Depend on it directly when you need to parameterize on color model at the type level without pulling in the full operation set, for example in a GPU backend that manages its own storage.
§Design notes
All colorimetric matrices are derived in const fn at compile time, including RGB-to-XYZ, chromatic adaptation, and composed primaries-to-primaries transforms. There is no matrix inversion or multiplication at runtime.
Color<S, M> is #[repr(transparent)] over its storage type. The color model is carried only as PhantomData. There is no runtime memory overhead from the type-level bookkeeping.
§Minimum Supported Rust Version
The minimum supported version of Rust is 1.85. Edition 2024 requires Rust 1.85. Inline const {} blocks, used to evaluate transform matrices at compile time in generic contexts, stabilized in Rust 1.82.
§Contributing
This crate is early in development. Consider raising an issue if it doesn’t fit your needs, is missing functionality, or is unergonomic in any way.
Contributions are welcome for any color space or transfer function defined in an open standard. If a standard space is missing, please open an issue before implementing it so the approach can be agreed on first. Spaces from proprietary or closed formats belong in a separate crate.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work, as defined in the Apache-2.0 license, shall be dual licensed as below, without any additional terms or conditions.
§License
Licensed under either of
- Apache License, Version 2.0 (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
at your option.
Modules§
- chromatic_
adaptation - Chromatic adaptation transforms XYZ tristimulus values from one illuminant to another.
- decode
- Signal decoding (EOTF): encoded signal to linear light.
- device
- Device-dependent color conversion trait.
- encode
- Signal encoding from linear light to a stored signal value.
- illuminant
- CIE standard illuminants and reference white points.
- layout
- Channel layout types for RGB, luma, and YCbCr color models.
- math
- Compile-time matrix arithmetic (
Mat3) and floating-point math abstraction (Float). - model
- Color model type definitions.
- observer
- Standard colorimetric observers.
- primaries
- RGB primary sets.
- tonemap
- Display Rendering Transforms, also known as tone mapping operators. The latter was chosen for simplicity.
- transfer
- Transfer functions.
Structs§
- Color
- A color value in color model
Mbacked by raw typeS. - LCh
- CIE LCh*, the polar form of CIELab.
- Lab
- CIE 1976 Lab* color space under reference white W.
- Oklab
- Oklab perceptual color space by Björn Ottosson, 2020.
- Oklch
- Oklch, the polar form of Oklab.
- Premultiplied
- Color channels have been multiplied by alpha.
- Straight
- Alpha channel is independent of the color channels.
- Xyz
- CIE XYZ tristimulus space normalized to Y = 1 under illuminant W.
Traits§
- Alpha
State - Marks how the alpha channel relates to the color channels.
- Backing
Store - A marker trait which implies
Scan act as storage for a model. - Channel
Map - Maps all N channels to their storage indices.
Type Aliases§
- Aces2065
- ACES 2065-1 archival space, linear AP0 under the ACES white point. Layout defaults to
Rgba. - AcesCc
- ACEScc logarithmic encoding of AP1. Layout defaults to
Rgba. - AcesCct
- ACEScct quasi-logarithmic encoding of AP1. Layout defaults to
Rgba. - AcesCg
- ACEScg working space, linear AP1 under the ACES white point. Layout defaults to
Rgba. - DciP3
- DCI-P3 for theatrical projection. Layout defaults to
Rgba. - Display
P3 - Display P3 with sRGB transfer function. Layout defaults to
Rgba. - Hdr10
- Rec. 2020 with PQ transfer function for HDR10. Layout defaults to
Rgba. - Hlg
- Rec. 2020 with HLG transfer function. Layout defaults to
Rgba. - Linear
P3 - Linear-light Display P3. Layout defaults to
Rgba. - Linear
ProPhoto - Linear-light ProPhoto. Layout defaults to
Rgba. - Linear
Rec2020 - Linear-light Rec. 2020. Layout defaults to
Rgba. - Linear
Srgb - Linear-light sRGB. Layout defaults to
Rgba. - ProPhoto
- ProPhoto ROMM RGB. Layout defaults to
Rgba. - Rec709
- Rec. 709 color space per ITU-R BT.709-6. Layout defaults to
Rgba. - Spectral
Color31 Radiance - Spectral radiance on the 31-band ICC grid (400-700 nm, 10 nm).
- Spectral
Color31 Reflectance - Spectral reflectance on the 31-band ICC grid (400-700 nm, 10 nm).
- Spectral
Color31 Transmittance - Spectral transmittance on the 31-band ICC grid (400-700 nm, 10 nm).
- Spectral
Color36 Radiance - Spectral radiance on the 36-band Stam rendering grid (380-730 nm, 10 nm).
- Spectral
Color36 Reflectance - Spectral reflectance on the 36-band Stam rendering grid (380-730 nm, 10 nm).
- Spectral
Color36 Transmittance - Spectral transmittance on the 36-band Stam rendering grid (380-730 nm, 10 nm).
- Spectral
Color41 Radiance - Spectral radiance on the 41-band full-visible grid (380-780 nm, 10 nm).
- Spectral
Color41 Reflectance - Spectral reflectance on the 41-band full-visible grid (380-780 nm, 10 nm).
- Spectral
Color41 Transmittance - Spectral transmittance on the 41-band full-visible grid (380-780 nm, 10 nm).
- Spectral
Color81 Radiance - Spectral radiance on the 81-band CIE standard grid (380-780 nm, 5 nm).
- Spectral
Color81 Reflectance - Spectral reflectance on the 81-band CIE standard grid (380-780 nm, 5 nm).
- Spectral
Color81 Transmittance - Spectral transmittance on the 81-band CIE standard grid (380-780 nm, 5 nm).
- Srgb
- sRGB color space per IEC 61966-2-1:1999. Layout defaults to
Rgba.