Module convert

Module convert 

Source
Expand description

Traits for converting between color spaces.

Each color space type, such as Rgb and Hsl, implement a number of conversion traits:

use palette::{FromColor, IntoColor, Srgb, Hsl};

let rgb = Srgb::new(0.3f32, 0.8, 0.1);

let hsl1: Hsl = rgb.into_color();
let hsl2 = Hsl::from_color(rgb);

Most of the color space types can be converted directly to each other, with these traits. If you look at the implemented traits for any color type, you will see a substantial list of FromColorUnclamped implementations. There are, however, exceptions and restrictions in some cases:

  • It’s not always possible to change the component type while converting. This can only be enabled in specific cases, to allow type inference to work. The input and output component types need to be the same in the general case.
  • It’s not always possible to change meta types while converting. Meta types are the additional input types on colors, such as white point or RGB standard. Similar to component types, these are generally restricted to help type inference.
  • Some color spaces want specific component types. For example, Xyz and many other color spaces require real-ish numbers (f32, f64, etc.).
  • Some color spaces want specific meta types. For example, Oklab requires the white point to be D65.

These limitations are usually the reason for why the compiler gives an error when calling into_color, from_color, or the corresponding unclamped methods. They are possible to work around by splitting the conversion into multiple steps.

§In-place Conversion

It’s possible for some color spaces to be converted in-place, meaning the destination color will use the memory space of the source color. The requirement for this is that the source and destination color types have the same memory layout. That is, the same component types and the same number of components. This is verified by the ArrayCast trait.

In-place conversion is done with the FromColorMut and IntoColorMut traits, as well as their unclamped counterparts, FromColorUnclampedMut and IntoColorUnclampedMut. They work for both single colors and slices of colors.

use palette::{convert::FromColorMut, Srgb, Hsl, Hwb};

let mut rgb_colors: Vec<Srgb<f32>> = vec![/* ... */];

{
    // Creates a scope guard that prevents `rgb_colors` from being modified as RGB.
    let hsl_colors = <[Hsl]>::from_color_mut(&mut rgb_colors);

    // The converted colors can be converted again, without keeping the previous guard around.
    let hwb_colors = hsl_colors.then_into_color_mut::<[Hwb]>();

    // The colors are automatically converted back to RGB at the end of the scope.
    // The use of `then_into_color_mut` above makes this conversion a single HWB -> RGB step,
    // instead of HWB -> HSL -> RGB, since it consumed the HSL guard.
}

§Reusing Intermediate Data

Some conversions produce intermediate data that is the same for all values. For example, conversion between Rgb and Xyz uses a Matrix3, while Cam16 uses a set of viewing condition as BakedParameters. These values can be used as “converters” via the Convert and ConvertOnce traits.

Remember: As with anything related to optimization, the performance difference may vary, depending on the compiler’s ability to detect and optimize the repeated work on its own.

It’s sometimes possible to save some computation time by explicitly caching and reusing these intermediate values:

use palette::{
    Srgb, Xyz, FromColor,
    cam16::{Cam16, Parameters},
    convert::Convert,
};

// Parameters only need to "bake" once per set of viewing conditions:
let parameters = Parameters::default_static_wp(40.0).bake();

let pixels: &[Srgb<u8>] = palette::cast::from_component_slice(&image_data);
for &color in pixels {
    let input = Xyz::from_color(color.into_linear());
    let cam16: Cam16<f32> = parameters.convert(input);

    // ...
}

It may also be possible to combine multiple matrix steps into a single matrix, instead of applying them one-by-one:

use palette::{
    Srgb, Xyz, lms::BradfordLms,
    convert::Convert,
    white_point::D65,
};

// While each matrix may be a compile time constant, the compiler may not
// combine them into a single matrix by itself:
let matrix = Xyz::matrix_from_rgb()
    .then(BradfordLms::<D65, f32>::matrix_from_xyz());

let pixels: &[Srgb<u8>] = palette::cast::from_component_slice(&image_data);
for &color in pixels {
    let lms = matrix.convert(color.into_linear());

    // ...
}

§Deriving FromColorUnclamped

FromColorUnclamped can be derived in a mostly automatic way. The other traits are blanket implemented based on it. The default minimum requirement is to implement FromColorUnclamped<Xyz>, but it can also be customized to make use of generics and have other manual implementations.

It is also recommended to derive or implement WithAlpha, to be able to convert between all Alpha wrapped color types.

§Configuration Attributes

The derives can be configured using one or more #[palette(...)] attributes. They can be attached to either the item itself, or to the fields.

#[derive(FromColorUnclamped)]
#[palette(
    component = "T",
    rgb_standard = "S",
)]
#[repr(C)]
struct ExampleType<S, T> {
    // ...
    #[palette(alpha)]
    alpha: T,
    standard: std::marker::PhantomData<S>,
}

§Item Attributes

  • skip_derives(Luma, Rgb): No conversion derives will be implemented for these colors. They are instead to be implemented manually, and serve as the basis for the automatic implementations.

  • white_point = "some::white_point::Type": Sets the white point type that should be used when deriving. The default is D65, but it may be any other type, including type parameters.

  • component = "some::component::Type": Sets the color component type that should be used when deriving. The default is f32, but it may be any other type, including type parameters.

  • rgb_standard = "some::rgb_standard::Type": Sets the RGB standard type that should be used when deriving. The default is to either use Srgb or a best effort to convert between standards, but sometimes it has to be set to a specific type. This also accepts type parameters.

  • luma_standard = "some::rgb_standard::Type": Sets the Luma standard type that should be used when deriving, similar to rgb_standard.

§Field Attributes

  • alpha: Specifies field as the color’s transparency value.

§Examples

Minimum requirements implementation:

use palette::convert::FromColorUnclamped;
use palette::{Srgb, Xyz, IntoColor};

/// A custom version of Xyz that stores integer values from 0 to 100.
#[derive(PartialEq, Debug, FromColorUnclamped)]
struct Xyz100 {
    x: u8,
    y: u8,
    z: u8,
}

// We have to implement at least one "manual" conversion. The default
// is to and from `Xyz`, but it can be customized with `skip_derives(...)`.
impl FromColorUnclamped<Xyz> for Xyz100 {
    fn from_color_unclamped(color: Xyz) -> Xyz100 {
        Xyz100 {
            x: (color.x * 100.0) as u8,
            y: (color.y * 100.0) as u8,
            z: (color.z * 100.0) as u8,
        }
    }
}

impl FromColorUnclamped<Xyz100> for Xyz {
    fn from_color_unclamped(color: Xyz100) -> Xyz {
        Xyz::new(
            color.x as f32 / 100.0,
            color.y as f32 / 100.0,
            color.z as f32 / 100.0,
        )
    }
}

// Start with an Xyz100 color.
let xyz = Xyz100 {
    x: 59,
    y: 75,
    z: 42,
};

// Convert the color to sRGB.
let rgb: Srgb = xyz.into_color();

assert_eq!(rgb.into_format(), Srgb::new(196u8, 238, 154));

With generic components:

#[macro_use]
extern crate approx;

use palette::cast::{ComponentsAs, ArrayCast};
use palette::rgb::{Rgb, RgbSpace, RgbStandard};
use palette::encoding::Linear;
use palette::white_point::D65;
use palette::convert::{FromColorUnclamped, IntoColorUnclamped};
use palette::{Hsv, Srgb, IntoColor};

/// sRGB, but with a reversed memory layout.
#[derive(Copy, Clone, ArrayCast, FromColorUnclamped)]
#[palette(
    skip_derives(Rgb),
    component = "T",
    rgb_standard = "palette::encoding::Srgb"
)]
#[repr(C)] // Makes sure the memory layout is as we want it.
struct Bgr<T> {
    blue: T,
    green: T,
    red: T,
}

// It converts from and into any linear Rgb type that has the
// D65 white point, which is the default if we don't specify
// anything else with the `white_point` attribute argument.
impl<S, T> FromColorUnclamped<Bgr<T>> for Rgb<S, T>
where
    S: RgbStandard,
    S::Space: RgbSpace<WhitePoint = D65>,
    Srgb<T>: IntoColorUnclamped<Rgb<S, T>>,
{
    fn from_color_unclamped(color: Bgr<T>) -> Rgb<S, T> {
        Srgb::new(color.red, color.green, color.blue)
            .into_color_unclamped()
    }
}

impl<S, T> FromColorUnclamped<Rgb<S, T>> for Bgr<T>
where
    S: RgbStandard,
    S::Space: RgbSpace<WhitePoint = D65>,
    Srgb<T>: FromColorUnclamped<Rgb<S, T>>,
{
    fn from_color_unclamped(color: Rgb<S, T>) -> Bgr<T> {
        let color = Srgb::from_color_unclamped(color);
        Bgr {
            blue: color.blue,
            green: color.green,
            red: color.red,
        }
    }
}

fn main() {
    let buffer = vec![
        0.0f64,
        0.0,
        0.0,
        0.0,
        0.5,
        0.25,
    ];
    let buffer: &[Bgr<_>] = buffer.components_as();
    let hsv: Hsv<_, f64> = buffer[1].into_color();

    assert_relative_eq!(hsv, Hsv::new(90.0, 1.0, 0.5));
}

With alpha component:

#[macro_use]
extern crate approx;

use palette::{LinSrgba, Srgb, IntoColor, WithAlpha};
use palette::rgb::Rgb;
use palette::convert::{FromColorUnclamped, IntoColorUnclamped};

/// CSS style sRGB.
#[derive(PartialEq, Debug, FromColorUnclamped, WithAlpha)]
#[palette(
    skip_derives(Rgb),
    rgb_standard = "palette::encoding::Srgb"
)]
struct CssRgb {
    red: u8,
    green: u8,
    blue: u8,
    #[palette(alpha)]
    alpha: f32,
}

// We will write a conversion function for opaque RGB and
// `impl_default_conversions` will take care of preserving
// the transparency for us.
impl<S> FromColorUnclamped<Rgb<S, f32>> for CssRgb
where
    Srgb<f32>: FromColorUnclamped<Rgb<S, f32>>
{
    fn from_color_unclamped(color: Rgb<S, f32>) -> CssRgb{
        let srgb = Srgb::from_color_unclamped(color)
            .into_format();

        CssRgb {
            red: srgb.red,
            green: srgb.green,
            blue: srgb.blue,
            alpha: 1.0
        }
    }
}

impl<S> FromColorUnclamped<CssRgb> for Rgb<S, f32>
where
    Srgb<f32>: IntoColorUnclamped<Rgb<S, f32>>
{
    fn from_color_unclamped(color: CssRgb) -> Rgb<S, f32>{
        Srgb::new(color.red, color.green, color.blue)
            .into_format()
            .into_color_unclamped()
    }
}

fn main() {
    let css_color = CssRgb {
        red: 187,
        green: 0,
        blue: 255,
        alpha: 0.3,
    };
    let color: LinSrgba = css_color.into_color();

    assert_relative_eq!(color, LinSrgba::new(0.496933, 0.0, 1.0, 0.3), epsilon = 0.000001);
}

Structs§

FromColorMutGuard
A scope guard that restores the guarded colors to their original type when dropped.
FromColorUnclampedMutGuard
A scope guard that restores the guarded colors to their original type, without clamping, when dropped.
Matrix3
A statically typed 3x3 conversion matrix.
OutOfBounds
The error type for a color conversion that converted a color into a color with invalid values.

Traits§

Convert
Represents types that can convert a value from one type to another.
ConvertOnce
Represents types that can convert a value from one type to another at least once.
FromColor
A trait for converting one color from another, in a possibly lossy way.
FromColorMut
Temporarily convert colors in place.
FromColorUnclamped
A trait for unchecked conversion of one color from another.
FromColorUnclampedMut
Temporarily convert colors in place, without clamping.
IntoColor
A trait for converting a color into another, in a possibly lossy way.
IntoColorMut
Temporarily convert colors in place. The Into counterpart to FromColorMut.
IntoColorUnclamped
A trait for unchecked conversion of a color into another.
IntoColorUnclampedMut
Temporarily convert colors in place. The Into counterpart to FromColorUnclampedMut.
TryFromColor
A trait for fallible conversion of one color from another.
TryIntoColor
A trait for fallible conversion of a color into another.

Derive Macros§

FromColorUnclamped