palette

repository·master·Indexed 21 days ago

https://github.com/ogeon/palette

A color management and conversion library for Rust that uses the type system to ensure correctness across various color spaces. It supports a wide range of built-in and user-defined color spaces, providing traits like FromColor and IntoColor for conversions, and a cast module for efficient pixel buffer manipulation. The library includes palette_math for floating-point operations and gamma lookup tables, and palette_derive for procedural macros such as FromColorUnclamped, WithAlpha, and ArrayCast.

Tokens
25.3K
Snippets
83
Records
100
Agent score
73%

What's inside palette

  1. Overview of the palette library

    master
    palette is a color management and conversion library designed for correctness, flexibility, and ease of use. It leverages the Rust type system to prevent color space mistakes and supports a wide range of color spaces, including user-defined variants. The library is designed to integrate easily with other libraries and provides robust color conversion capabilities.
  2. Perform color operations with operator traits

    master

    Palette implements various color operations (like saturate, desaturate, hue shift, and lightening) as operator traits. This allows you to write generic functions that operate on any color space that supports the specific operation.

    Common traits include:

    • ShiftHue
    • Lighten
    • Mix (for interpolation)

    Additionally, Palette provides SVG blend and composition functions, such as .over() for alpha blending.

    use palette::{Hsl, Hsv, Lighten, Mix, ShiftHue};
    
    fn transform_color<C>(color: C, amount: f32) -> C
    where
        C: ShiftHue<Scalar = f32> + Lighten<Scalar = f32> + Mix<Scalar = f32> + Copy,
    {
        let new_color = color.shift_hue(170.0).lighten(1.0);
    
        // Interpolate between the old and new color.
        color.mix(new_color, amount)
    }
    
    let new_hsl = transform_color(Hsl::new_srgb(0.00, 0.70, 0.20), 0.8);
    let new_hsv = transform_color(Hsv::new_srgb(0.00, 0.82, 0.34), 0.8);
  3. Use palette_derive macros via the palette crate

    master

    The palette_derive crate provides derive macros for the palette crate. Instead of adding palette_derive as a direct dependency, you should add palette to your Cargo.toml. The macros are re-exported through the main palette crate.

    To use them, ensure palette is in your dependencies and use the macros as provided by the palette namespace.

    // In your Cargo.toml
    [dependencies]
    palette = "0.7.7"
    
    // In your code
    use palette::SomeDeriveMacro;
    
    #[derive(SomeDeriveMacro)]
    struct MyColor;
  4. Create custom color spaces

    master

    Built-in color spaces are highly customizable. You can define new color spaces by composing different components, such as primaries, white points, and transfer functions (encodings).

    For example, you can define a custom RGB standard by combining encoding, white_point, and rgb::Rgb types.

    use palette::{
        encoding,
        white_point,
        rgb::Rgb,
        Srgb
    };
    
    // Combining sRGB primaries, the CIE equal energy white point and the sRGB transfer function
    type EqualEnergyStandard = (encoding::Srgb, white_point::E, encoding::Srgb);
    type EqualEnergySrgb<T> = Rgb<EqualEnergyStandard, T>;
    
    let ee_rgb = EqualEnergySrgb::new(1.0, 0.5, 0.3);
  5. Install Palette via Cargo

    master

    To use Palette in your project, add it to your Cargo.toml dependencies. By default, it includes std, alloc, named constants, and approx comparison support.

    For standard usage:

    [dependencies]
    palette = "0.7.7"

    If you are working in an embedded or no_std environment, you must disable default features and enable libm to provide floating-point math without the standard library:

    [dependencies.palette]
    version = "0.7.7"
    default-features = false
    features = ["libm"]
    [dependencies]
    palette = "0.7.7"
  6. Configure palette_math for #![no_std] and embedded environments

    master

    To use palette_math in a #![no_std] environment, you must disable the default std feature. You should then enable the libm feature to provide floating-point math operations via the libm crate, and the alloc feature if you require types that allocate memory (like Vec).

    # Uses libm instead of std for floating point math:
    palette_math = { version = "0.7.7", features = ["libm"], default-features = false }
  7. Implement conversion for custom color types

    master

    To integrate a custom color type into Palette, you can use the palette_derive macros and implement the necessary conversion traits.

    1. Use #[derive(FromColorUnclamped, WithAlpha)] on your struct.
    2. Use #[palette(skip_derives(Rgb), rgb_standard = "...")] to specify how to handle RGB conversions.
    3. Use #[palette(alpha)] to mark the alpha channel.
    4. Implement FromColorUnclamped<T> for your type to allow conversion from Palette types.
    5. Implement FromColorUnclamped<YourType> for Palette types to allow conversion into your type.
    6. Implement Clamp to define how your color values should be constrained.
    use palette::{
        convert::FromColorUnclamped,
        encoding,
        rgb::Rgb,
        IntoColor, WithAlpha, Clamp, Srgb, Lcha
    };
    
    #[derive(FromColorUnclamped, WithAlpha)]
    #[palette(skip_derives(Rgb), rgb_standard = "encoding::Srgb")]
    struct Color {
        r: f32,
        g: f32,
        b: f32,
        #[palette(alpha)]
        a: f32,
    }
    
    impl Clamp for Color {
        fn clamp(self) -> Self {
            Color {
                r: self.r.min(1.0).max(0.0),
                g: self.g.min(1.0).max(0.0),
                b: self.b.min(1.0).max(0.0),
                a: self.a.min(1.0).max(0.0),
            }
        }
    }
    
    // To allow generic usage:
    fn generic_do_something(color: impl IntoColor<Color>) {
        let color = color.into_color();
        // ...
    }
  8. Work with pixels and buffers using the cast module

    master

    The cast module provides traits and functions to treat slices or arrays of components (like &[u8]) as slices of Palette colors without cloning the entire buffer. This is highly efficient for image or pixel buffers.

    • Use components_as_mut() to convert a mutable slice of components into a mutable slice of Palette colors (e.g., &mut [Srgb<u8>]).
    • Use components_as() to convert a slice of components into a slice of Palette colors (e.g., &[Srgb<u8>]).
    • You can also convert a single color from an object that implements AsMut<[u8; N]>.
    use palette::{cast::ComponentsAsMut, Srgb};
    
    // Convert `my_rgb_image` into `&mut [Srgb<u8>]` without copying.
    fn swap_red_and_blue(my_rgb_image: &mut [u8]) {
        let my_rgb_image: &mut [Srgb<u8>] = my_rgb_image.components_as_mut();
    
        for color in my_rgb_image {
            std::mem::swap(&mut color.red, &mut color.blue);
        }
    }
  9. What is the UintCast trait?

    master

    The UintCast trait is a marker trait used to identify types that can be represented as an unsigned integer with the same memory layout and size. This allows for zero-cost conversions between color types and integer types (e.g., converting an Srgba<u8> to a u32).

    Safety Requirements

    To implement UintCast safely, the type must satisfy these conditions:

    • It must be inhabited (not Infallible).
    • It must allow any bit pattern (or have a way to recover from invalid values).
    • It must be a wrapper around Self::Uint or be safe to transmute to/from Self::Uint.
    • It must not contain any internal padding.
    • It must be repr(C) or repr(transparent).
    • It must have the same size and alignment as Self::Uint.
    • It is assumed not to implement Drop.
  10. Chain multiple in-place conversions with `then_into_color_mut`

    master

    When performing multiple sequential conversions (e.g., Srgb $\rightarrow$ Hsv $\rightarrow$ Hsl $\rightarrow$ Srgb), a standard guard drop would convert the colors back to the intermediate type at each step (e.g., Hsv $\rightarrow$ Hsl $\rightarrow$ Hsv $\rightarrow$ Srgb).

    To optimize this, use FromColorMutGuard::then_into_color_mut. This method replaces the current guard with a new one that points to the next color type, but ensures that when the final guard is dropped, the colors are restored directly to the original starting type, skipping the intermediate restoration steps.

    use palette::{FromColorMut, ShiftHueAssign, LightenAssign, Srgb, Hsv, Hsl};
    
    let mut rgb = [
        Srgb::new(1.0, 0.0, 0.0),
        Srgb::new(0.0, 1.0, 0.0),
        Srgb::new(0.0, 0.0, 1.0),
    ];
    
    {
        let mut hsv = <[Hsv]>::from_color_mut(&mut rgb);
        hsv.shift_hue_assign(60.0);
    
        // Chain to Hsl. The final drop will restore directly to Srgb.
        let mut hsl = hsv.then_into_color_mut::<[Hsl]>();
        hsl.lighten_assign(0.5);
    
    } // Colors are restored directly to Srgb here.