#rgb #color-palette #color #color-conversion #convert #conversion #linear

无std palette

专注于正确性、灵活性和易用性,将颜色进行转换和管理

17个版本

0.7.6 2024年4月28日
0.7.5 2024年2月25日
0.7.4 2024年1月28日
0.7.3 2023年8月10日
0.2.1 2016年2月23日

#4 in 图像

Download history 26452/week @ 2024-05-02 24963/week @ 2024-05-09 23878/week @ 2024-05-16 22662/week @ 2024-05-23 22489/week @ 2024-05-30 21819/week @ 2024-06-06 23497/week @ 2024-06-13 23103/week @ 2024-06-20 22077/week @ 2024-06-27 22619/week @ 2024-07-04 30103/week @ 2024-07-11 29310/week @ 2024-07-18 27292/week @ 2024-07-25 28939/week @ 2024-08-01 31812/week @ 2024-08-08 26308/week @ 2024-08-15

119,131 每月下载量
用于 430 个crate(182直接使用)

MIT/Apache

1.5MB
27K SLoC

调色板

一个专注于保持正确性、灵活性和易用性的颜色管理和转换库。它利用类型系统来防止错误,支持广泛的颜色空间(包括用户定义的变体)并提供与其他库集成的不同方式。

0.7.6版本公告帖.

功能摘要

  • 颜色空间的类型系统表示,包括RGB、HSL、HSV、HWB、L*a*b*、L*C*h°、XYZ和xyY。
  • 从和到颜色缓冲区的无拷贝转换允许简单地与其他crate和系统集成。
  • 作为特质实现的颜色操作,如算术、加亮/变暗、色调偏移、混合/插值和SVG混合函数。
  • 可以通过类型参数自定义颜色空间,以支持不同的精度、线性度、白点、RGB标准等。
  • 支持 #[no_std]
  • 可选的 serderandbytemuck 集成。

最小支持的Rust版本 (MSRV)

此版本的Palette已与Rust版本 1.60.0stablebetanightly 频道进行自动测试。库的将来版本可能会提高最小支持的版本以利用新的语言特性,但这通常会被视为破坏性更改。可能会对安全补丁、依赖项在次要或补丁版本中提高MSRV以及类似更改做出例外。

入门指南

将以下行添加到您的 Cargo.toml 文件中

[dependencies]
palette = "0.7.6"

或者如果您想排除 std

[dependencies.palette]
version = "0.7.6"
default-features = false
features = ["libm"] # Uses libm instead of std for floating point math

Cargo功能

以下功能默认启用

  • "named" - 启用颜色常量,位于 named 模块。
  • "named_from_str" - 启用 named::from_str,将名称字符串映射到颜色。
  • "std" - 启用标准库的使用。同时启用 "alloc"
  • "alloc" - 启用分配类型的实现,如 VecBox
  • "approx" - 启用使用 approx 进行近似比较。

这些功能默认禁用

  • "serializing" - 启用使用 serde 进行颜色序列化和反序列化。
  • "random" - 启用使用 rand 生成随机颜色。
  • "libm" - 使用 libm 浮点数学库(当禁用 std 功能时)。
  • "bytemuck" - 启用使用 bytemuck 在普通数据类型之间进行转换。
  • "wide" - 启用使用 wide 的 SIMD 类型支持。
  • "find-crate" - 启用 derive 以在 Cargo.toml 中重命名 palette crate 时找到 palette crate。

在嵌入式环境中使用 palette

Palette 通过禁用 "std" 功能支持 #![no_std] 环境。它通过 "libm" 功能使用 libm,以提供通常在 std 中的浮点运算,并通过 "alloc" 功能提供使用分配类型的特性。然而,没有标准库,无法使用 serde 进行序列化。

示例

这些是特征摘要中列出的某些功能的示例。

转换

可以使用 FromColorIntoColor 特性将一种色彩空间转换为另一种。它们类似于 FromInto,但专为色彩定制。

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

let my_rgb = Srgb::new(0.8, 0.3, 0.3);

let mut my_lch = Lch::from_color(my_rgb);
my_lch.hue += 180.0;

let mut my_hsl: Hsl = my_lch.into_color();
my_hsl.lightness *= 0.6;

let my_new_rgb = Srgb::from_color(my_hsl);

此图展示了原始色彩以及两次变换的结果。

The result of each step in the "converting" example.

大多数常见的色彩空间已在 Palette 中实现,但在某些情况下可能需要更定制的功能。转换特性使得将自定义色彩类型集成到系统中成为可能。例如,这可以用于添加新的色彩空间或创建更简单的用户界面 API。

下面将提供一个更长的、更高级的示例,展示如何为自定义色彩类型实现转换特性。

像素和缓冲区

当处理图像或像素缓冲区,或任何可以转换为组件切片(例如 &[u8])的色彩类型时,cast 模块提供了将它们转换为 Palette 色彩切片的特性,而无需克隆整个缓冲区。

use palette::{cast::ComponentsAsMut, Srgb};

// The input to this function could be data from an image file or
// maybe a texture in a game.
fn swap_red_and_blue(my_rgb_image: &mut [u8]) {
    // Convert `my_rgb_image` into `&mut [Srgb<u8>]` without copying.
    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);
    }
}
之前 之后
The fruit image before swapping the red and blue color channels. The fruit image with the red and blue color channels swapped.

还可以从切片或数组创建单个色彩。假设我们使用的是实现了 AsMut<[u8; 3]> 的东西。

use palette::Srgb;

fn swap_red_and_blue(mut my_rgb: impl AsMut<[u8; 3]>) {
    let my_rgb: &mut Srgb<u8> = my_rgb.as_mut().into();

    std::mem::swap(&mut my_rgb.red, &mut my_rgb.blue);
}

这使得与任何可以将色彩类型转换为切片和数组的 crate(例如 Palette)一起使用成为可能,只需编写少量的粘合代码,几乎没有开销。也可以将 Palette 类型转换为切片和数组。

色彩操作

Palette 内置了许多色彩操作,如饱和度/非饱和度、色调偏移等,以操作符特性的形式存在。这意味着可以编写通用的函数,在支持这些操作的任何色彩空间上执行这些操作。输出将根据色彩空间的特性而变化。

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);

此图展示了从色彩到 new_color 在 HSL 和 HSV 中的过渡。

Gradients showing the transition from the starting color to the modified color in HSL and HSV.

除了操作符特性之外,还实现了 SVG 混合和组合函数。

use palette::{
    blend::Compose,
    cast::{ComponentsAs, ComponentsAsMut},
    Srgb, WithAlpha,
};

// The input to this function could be data from image files.
fn alpha_blend_images(image1: &mut [u8], image2: &[u8]) {
    // Convert the images into `&mut [Srgb<u8>]` and `&[Srgb<u8>]` without copying.
    let image1: &mut [Srgb<u8>] = image1.components_as_mut();
    let image2: &[Srgb<u8>] = image2.components_as();

    for (color1, color2) in image1.iter_mut().zip(image2) {
        // Convert the colors to linear floating point format and give them transparency values.
        let color1_alpha = color1.into_linear().opaque();
        let color2_alpha = color2.into_linear().with_alpha(0.5);

        // Alpha blend `color2_alpha` over `color1_alpha`.
        let blended = color2_alpha.over(color1_alpha);

        // Convert the color part back to `Srgb<u8>` and overwrite the value in image1.
        *color1 = blended.color.into_encoding();
    }
}
图片 1 图片 2 结果
A photo of various fruit. A photo of kitten in a strawhat. Image 2 blended over Image 1 with 50% transparency.

还有将显式转换为和从预乘 Alpha 转换的选项,以避免不必要的来回转换,使用 PreAlpha 类型。

渐变

大多数色彩类型与渐变和插值 crate(如 enterpolation)直接兼容。

use enterpolation::{linear::ConstEquidistantLinear, Curve};
use palette::LinSrgb;

let gradient = ConstEquidistantLinear::<f32, _, 3>::equidistant_unchecked([
    LinSrgb::new(0.00, 0.05, 0.20),
    LinSrgb::new(0.70, 0.10, 0.20),
    LinSrgb::new(0.95, 0.90, 0.30),
]);

let taken_colors: Vec<_> = gradient.take(10).collect();

以下是渐变的连续形式及其从 .take(10) 的 10 种颜色。

An illustration of the gradient with the continuous form above a row of discrete color swatches.

自定义色彩空间

为了应对尽可能多的变化,内置的色彩空间已被设置为可定制。更常见的变体已公开为类型别名(如上文的 SrgbSrgbaLinSrgb),但完全可以创建自定义组合,包括全新的参数。例如,创建自己的 RGB 标准。

use palette::{
    encoding,
    white_point,
    rgb::Rgb,
    chromatic_adaptation::AdaptFrom,
    Srgb
};

// RgbStandard and RgbSpace are implemented for 2 and 3 element tuples,
// allowing mixing and matching of existing types. In this case we are
// combining sRGB primaries, the CIE equal energy white point and the
// sRGB transfer function (a.k.a. encoding or gamma).
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);

// We need to use chromatic adaptation when going between white points.
let srgb = Srgb::adapt_from(ee_rgb);

还可以为自定义类型实现特性,当内置选项不足时。

转换自定义色彩类型

以下示例展示了 Palette 用户如何从自定义的 Color 类型进行转换。虽然不是一行代码,但可以节省大量的重复手动工作。

use palette::{
    convert::FromColorUnclamped,
    encoding,
    rgb::Rgb,
    IntoColor, WithAlpha, Clamp, Srgb, Lcha
};

// This implements conversion to and from all Palette colors.
#[derive(FromColorUnclamped, WithAlpha)]
// We have to tell Palette that we will take care of converting to/from sRGB.
#[palette(skip_derives(Rgb), rgb_standard = "encoding::Srgb")]
struct Color {
    r: f32,
    g: f32,
    b: f32,
    // Let Palette know this is our alpha channel.
    #[palette(alpha)]
    a: f32,
}

// There's no blanket implementation for Self -> Self, unlike the From trait.
// This is to better allow cases like Self<A> -> Self<B>.
impl FromColorUnclamped<Color> for Color {
    fn from_color_unclamped(color: Color) -> Color {
        color
    }
}

// Convert from any kind of f32 sRGB.
impl<S> FromColorUnclamped<Rgb<S, f32>> for Color
where
    Srgb: FromColorUnclamped<Rgb<S, f32>>,
{
    fn from_color_unclamped(color: Rgb<S, f32>) -> Color {
        let srgb = Srgb::from_color_unclamped(color);
        Color { r: srgb.red, g: srgb.green, b: srgb.blue, a: 1.0 }
    }
}

// Convert into any kind of f32 sRGB.
impl<S> FromColorUnclamped<Color> for Rgb<S, f32>
where
    Rgb<S, f32>: FromColorUnclamped<Srgb>,
{
    fn from_color_unclamped(color: Color) -> Self {
        let srgb = Srgb::new(color.r, color.g, color.b);
        Self::from_color_unclamped(srgb)
    }
}

// Add the required clamping.
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),
        }
    }
}


// This function uses only our `Color`, but Palette users can convert to it.
fn do_something(color: Color) {
    // ...
}

do_something(Color { r: 1.0, g: 0.0, b: 1.0, a: 0.5 });
do_something(Lcha::new(60.0, 116.0, 328.0, 0.5).into_color());


// This function has the conversion built in and takes any compatible
// color type as input.
fn generic_do_something(color: impl IntoColor<Color>) {
    let color = color.into_color();
    // ...
}

generic_do_something(Color { r: 1.0, g: 0.0, b: 1.0, a: 0.5 });
generic_do_something(Lcha::new(60.0, 116.0, 328.0, 0.5));

许可

许可协议为以下之一

根据您的选择。

依赖项

~0.4–1.4MB
~31K SLoC