Here’s what’s new in Palette since the release of 0.7.0.
For those who aren’t familiar with it, Palette is a Rust library for working with colors, including color conversion and color management. Its features range from common tasks, like color blending or converting HSL to RGB, to more advanced topics. It leverages Rust’s type system, by encoding color space information as type parameters, to be able to prevent mistakes and to keep it open to extension and customization.
Color Difference Metrics
Palette has had the ColorDifference
and RelativeContrast
traits for quite some time now. The former is an implementation of CIEDE2000 while the latter is the WCAG 2.1 relative contrast metric, for measuring color difference and luminance contrast. To make this more clear, and as part of an effort to improve the color difference traits, the ColorDifference
and RelativeContrast
traits have been deprecated in favor of the Ciede2000
and Wcag21RelativeContrast
traits, respectively.
There are also other ways to calculate color differences that may be more suitable for some applications. Two of them have been added to Palette in 0.7.2:
EuclideanDistance
: One of the fastest metrics to calculate, but not the most accurate. It treats the colors as points in a Euclidean space and simply calculates the distance between them.HyAb
: Another metric that’s similarly fast to compute. It’s a hybrid between Euclidean and Manhattan/Taxidriver distance, that separates luminance from chroma in a way that’s more representative of how they are perceived.
Both of these metrics will give better results in color spaces that are more perceptually uniform, such as Lab
, Oklab
and Luv
, than for example Rgb
.
Saturating Addition And Subtraction
Colors with integer components have gotten saturating_add
and saturating_sub
methods, via the SaturatingAdd
and SaturatingSub
traits. These prevent overflows by clamping the result of addition and subtraction to the minimum and maximum values of each component.
use palette::{num::SaturatingAdd, Srgb};
fn main() {
let color1 = Srgb::<u8>::new(200, 20, 20);
let color2 = Srgb::<u8>::new(100, 10, 10);
let color_sum = color1.saturating_add(color2);
// Srgb{255,30,30}
println!("{:?}", color_sum);
}
Struct of Arrays (SoA)
Some applications need to work with color components/channels separated from each other, rather than interleaved in the same buffer. For example, if you would like to reduce the color and luminance noise in an image, you may get a performance boost from processing the components of Lab
in separate buffers. Palette 0.7.2 adds some utilities that help with transforming an array of color structs into a color struct of component arrays (and back). There are also iterators and some Vec
methods, such as push
and pop
.
Here’s an example that shows the idea, but not necessarily in the best performing way:
use palette::{FromColor, IntoColor, Lab, LinSrgb};
fn example_denoise(
luminance_blur: usize,
chroma_blur: usize,
width: usize,
height: usize,
pixels: &mut [LinSrgb<f32>],
) {
// This makes `lab_pixels` have `Vec<f32>`s as `l`, `a` and `b` values.
let mut lab_pixels: Lab<_, Vec<f32>> = pixels
.iter()
.map(|&rgb| Lab::from_color(rgb))
.collect();
// Process the `l`, `a` and `b` values separately.
blur(luminance_blur, width, height, &mut lab_pixels.l);
blur(chroma_blur, width, height, &mut lab_pixels.a);
blur(chroma_blur, width, height, &mut lab_pixels.b);
// Write the new colors back into the `pixels` buffer.
for (rgb, lab) in pixels.iter_mut().zip(lab_pixels) {
*rgb = lab.into_color();
}
}
fn blur(amount: usize, width: usize, height: usize, values: &mut [f32]) {
// ...
}
As with anything performance related, make sure to benchmark the options for your specific application.
Fixes Since 0.7.0
0.7.1
- There were implementations of
Add
,Sub
,Mul
andDiv
that allowedPreAlpha
to be used on the right hand side withf32
orf64
on the left hand side. This would unfortunately cause sporadic infinite recursion inrustc
and has been removed for now. - An old issue with how
Alpha
andPreAlpha
was serialized has been resolved. Users of some formats that make a difference between maps and structs were not able to parse the serialized value. The generated (de)serialization code has been replaces with a custom implementation that’s more fitting and able to read its own handwriting.
0.7.2
- The conversion from
Okhsv
toOklab
has been fixed to not give NaN values whenvalue
is 0saturation
is > 0.
Wrapping Up
I want to give a big thank you to everyone who contributed or took part in discussions. Your help is always appreciated!
Palette can be found on GitHub, and on crates.io.
Thank you for reading!