#![doc(html_root_url = "https://docs.rs/palette/0.4.0/palette/")]
#![cfg_attr(feature = "strict", deny(missing_docs))]
#![cfg_attr(feature = "strict", deny(warnings))]
#[cfg_attr(test, macro_use)]
extern crate approx;
#[macro_use]
extern crate palette_derive;
extern crate num_traits;
#[cfg(feature = "phf")]
extern crate phf;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
#[cfg(all(test, feature = "serde"))]
extern crate serde_json;
use num_traits::{Float, NumCast, ToPrimitive, Zero};
use approx::ApproxEq;
use blend::PreAlpha;
use encoding::Linear;
use luma::Luma;
use rgb::{Rgb, RgbSpace, Rgba};
#[doc(hidden)]
pub use palette_derive::*;
pub use alpha::Alpha;
pub use blend::Blend;
pub use gradient::Gradient;
pub use hsl::{Hsl, Hsla};
pub use hsv::{Hsv, Hsva};
pub use hwb::{Hwb, Hwba};
pub use lab::{Lab, Laba};
pub use lch::{Lch, Lcha};
pub use luma::{GammaLuma, GammaLumaa, LinLuma, LinLumaa, SrgbLuma, SrgbLumaa};
pub use rgb::{GammaSrgb, GammaSrgba, LinSrgb, LinSrgba, Srgb, Srgba};
pub use xyz::{Xyz, Xyza};
pub use yxy::{Yxy, Yxya};
pub use convert::{FromColor, IntoColor};
pub use encoding::pixel::Pixel;
pub use hues::{LabHue, RgbHue};
pub use matrix::Mat3;
#[cfg(test)]
macro_rules! assert_ranges {
(@make_tuple $first:pat, $next:ident,) => (($first, $next));
(@make_tuple $first:pat, $next:ident, $($rest:ident,)*) => (
assert_ranges!(@make_tuple ($first, $next), $($rest,)*)
);
(
$ty:ident < $($ty_params:ty),+ >;
limited {$($limited:ident: $limited_from:expr => $limited_to:expr),+}
limited_min {$($limited_min:ident: $limited_min_from:expr => $limited_min_to:expr),*}
unlimited {$($unlimited:ident: $unlimited_from:expr => $unlimited_to:expr),*}
) => (
{
use std::iter::repeat;
use Limited;
{
print!("checking below limits ... ");
$(
let from = $limited_from;
let to = $limited_to;
let diff = to - from;
let $limited = (1..11).map(|i| from - (i as f64 / 10.0) * diff);
)+
$(
let from = $limited_min_from;
let to = $limited_min_to;
let diff = to - from;
let $limited_min = (1..11).map(|i| from - (i as f64 / 10.0) * diff);
)*
$(
let from = $unlimited_from;
let to = $unlimited_to;
let diff = to - from;
let $unlimited = (1..11).map(|i| from - (i as f64 / 10.0) * diff);
)*
for assert_ranges!(@make_tuple (), $($limited,)+ $($limited_min,)* $($unlimited,)* ) in repeat(()) $(.zip($limited))+ $(.zip($limited_min))* $(.zip($unlimited))* {
let c: $ty<$($ty_params),+> = $ty {
$($limited: $limited.into(),)+
$($limited_min: $limited_min.into(),)*
$($unlimited: $unlimited.into(),)*
..$ty::default() };
let clamped = c.clamp();
let expected: $ty<$($ty_params),+> = $ty {
$($limited: $limited_from.into(),)+
$($limited_min: $limited_min_from.into(),)*
$($unlimited: $unlimited.into(),)*
..$ty::default() };
assert!(!c.is_valid());
assert_relative_eq!(clamped, expected);
}
println!("ok")
}
{
print!("checking within limits ... ");
$(
let from = $limited_from;
let to = $limited_to;
let diff = to - from;
let $limited = (0..11).map(|i| from + (i as f64 / 10.0) * diff);
)+
$(
let from = $limited_min_from;
let to = $limited_min_to;
let diff = to - from;
let $limited_min = (0..11).map(|i| from + (i as f64 / 10.0) * diff);
)*
$(
let from = $unlimited_from;
let to = $unlimited_to;
let diff = to - from;
let $unlimited = (0..11).map(|i| from + (i as f64 / 10.0) * diff);
)*
for assert_ranges!(@make_tuple (), $($limited,)+ $($limited_min,)* $($unlimited,)* ) in repeat(()) $(.zip($limited))+ $(.zip($limited_min))* $(.zip($unlimited))* {
let c: $ty<$($ty_params),+> = $ty {
$($limited: $limited.into(),)+
$($limited_min: $limited_min.into(),)*
$($unlimited: $unlimited.into(),)*
..$ty::default() };
let clamped = c.clamp();
assert!(c.is_valid());
assert_relative_eq!(clamped, c);
}
println!("ok")
}
{
print!("checking above limits ... ");
$(
let from = $limited_from;
let to = $limited_to;
let diff = to - from;
let $limited = (1..11).map(|i| to + (i as f64 / 10.0) * diff);
)+
$(
let from = $limited_min_from;
let to = $limited_min_to;
let diff = to - from;
let $limited_min = (1..11).map(|i| to + (i as f64 / 10.0) * diff);
)*
$(
let from = $unlimited_from;
let to = $unlimited_to;
let diff = to - from;
let $unlimited = (1..11).map(|i| to + (i as f64 / 10.0) * diff);
)*
for assert_ranges!(@make_tuple (), $($limited,)+ $($limited_min,)* $($unlimited,)* ) in repeat(()) $(.zip($limited))+ $(.zip($limited_min))* $(.zip($unlimited))* {
let c: $ty<$($ty_params),+> = $ty {
$($limited: $limited.into(),)+
$($limited_min: $limited_min.into(),)*
$($unlimited: $unlimited.into(),)*
..$ty::default() };
let clamped = c.clamp();
let expected: $ty<$($ty_params),+> = $ty {
$($limited: $limited_to.into(),)+
$($limited_min: $limited_min.into(),)*
$($unlimited: $unlimited.into(),)*
..$ty::default() };
assert!(!c.is_valid());
assert_relative_eq!(clamped, expected);
}
println!("ok")
}
}
);
}
#[macro_use]
mod macros;
pub mod blend;
pub mod gradient;
#[cfg(feature = "named")]
pub mod named;
mod alpha;
mod hsl;
mod hsv;
mod hwb;
mod lab;
mod lch;
pub mod luma;
pub mod rgb;
mod xyz;
mod yxy;
mod hues;
pub mod chromatic_adaptation;
mod convert;
pub mod encoding;
mod equality;
mod matrix;
pub mod white_point;
macro_rules! make_color {
($(
#[$variant_comment:meta]
$variant: ident < $variant_ty_param:ty > $(and $($representations:ident),+ )* {$(
#[$ctor_comment:meta]
$ctor_name:ident $( <$( $ty_params:ident: $ty_param_traits:ident $( <$( $ty_inner_traits:ident ),*> )*),*> )* ($($ctor_field:ident : $ctor_ty:ty),*) [alpha: $alpha_ty:ty] => $ctor_original:ident;
)+}
)+) => (
pub type Colora<S = encoding::Srgb, T = f32> = Alpha<Color<S, T>, T>;
#[derive(Debug)]
pub enum Color<S = encoding::Srgb, T = f32>
where T: Float + Component,
S: RgbSpace,
{
$(#[$variant_comment] $variant($variant<$variant_ty_param, T>)),+
}
impl<S, T> Copy for Color<S, T>
where S: RgbSpace,
T: Float + Component,
{}
impl<S, T> Clone for Color<S, T>
where S: RgbSpace,
T: Float + Component,
{
fn clone(&self) -> Color<S, T> { *self }
}
impl<S, T> Default for Color<S, T>
where S: RgbSpace,
T: Float + Component,
{
fn default() -> Color<S, T> { Color::Rgb(Default::default()) }
}
impl<T: Float + Component> Color<encoding::Srgb, T> {
$(
$(
#[$ctor_comment]
pub fn $ctor_name$(<$($ty_params : $ty_param_traits$( <$( $ty_inner_traits ),*> )*),*>)*($($ctor_field: $ctor_ty),*) -> Color<encoding::Srgb, T> {
Color::$variant($variant::$ctor_original($($ctor_field),*))
}
)+
)+
}
impl<T: Float + Component> Alpha<Color<encoding::Srgb, T>, T> {
$(
$(
#[$ctor_comment]
pub fn $ctor_name$(<$($ty_params : $ty_param_traits$( <$( $ty_inner_traits ),*> )*),*>)*($($ctor_field: $ctor_ty,)* alpha: $alpha_ty) -> Colora<encoding::Srgb, T> {
Alpha::<$variant<_, T>, T>::$ctor_original($($ctor_field,)* alpha).into()
}
)+
)+
}
impl<S, T> Mix for Color<S, T>
where T: Float + Component,
S: RgbSpace,
{
type Scalar = T;
fn mix(&self, other: &Color<S, T>, factor: T) -> Color<S, T> {
Rgb::<Linear<S>, T>::from(*self).mix(&Rgb::<Linear<S>, T>::from(*other), factor).into()
}
}
impl<S, T> Shade for Color<S, T>
where T: Float + Component,
S: RgbSpace,
{
type Scalar = T;
fn lighten(&self, amount: T) -> Color<S, T> {
Lab::from(*self).lighten(amount).into()
}
}
impl<S, T> GetHue for Color<S, T>
where T: Float + Component,
S: RgbSpace,
{
type Hue = LabHue<T>;
fn get_hue(&self) -> Option<LabHue<T>> {
Lch::from(*self).get_hue()
}
}
impl<S, T> Hue for Color<S, T>
where T: Float + Component,
S: RgbSpace,
{
fn with_hue<H: Into<Self::Hue>>(&self, hue: H) -> Color<S, T> {
Lch::from(*self).with_hue(hue).into()
}
fn shift_hue<H: Into<Self::Hue>>(&self, amount: H) -> Color<S, T> {
Lch::from(*self).shift_hue(amount).into()
}
}
impl<S, T> Saturate for Color<S, T>
where T: Float + Component,
S: RgbSpace,
{
type Scalar = T;
fn saturate(&self, factor: T) -> Color<S, T> {
Lch::from(*self).saturate(factor).into()
}
}
impl<S, T> Blend for Color<S, T>
where T: Float + Component,
S: RgbSpace,
{
type Color = Rgb<Linear<S>, T>;
fn into_premultiplied(self) -> PreAlpha<Rgb<Linear<S>, T>, T> {
Rgba::<Linear<S>, T>::from(self).into()
}
fn from_premultiplied(color: PreAlpha<Rgb<Linear<S>, T>, T>) -> Self {
Rgba::<Linear<S>, T>::from(color).into()
}
}
impl<S, T> ApproxEq for Color<S, T>
where T: Float + Component + ApproxEq,
T::Epsilon: Float,
S: RgbSpace,
{
type Epsilon = T::Epsilon;
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}
fn default_max_ulps() -> u32 {
T::default_max_ulps()
}
fn relative_eq(&self, other: &Self, epsilon: Self::Epsilon, max_relative: Self::Epsilon) -> bool {
match (*self, *other) {
$((Color::$variant(ref s), Color::$variant(ref o)) => s.relative_eq(o, epsilon, max_relative),)+
_ => false
}
}
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool{
match (*self, *other) {
$((Color::$variant(ref s), Color::$variant(ref o)) => s.ulps_eq(o, epsilon, max_ulps),)+
_ => false
}
}
}
$(
impl<S, T> From<$variant<$variant_ty_param, T>> for Color<S, T>
where T: Float + Component,
S: RgbSpace,
{
fn from(color: $variant<$variant_ty_param, T>) -> Color<S, T> {
Color::$variant(color)
}
}
impl<S, T> From<Alpha<$variant<$variant_ty_param, T>, T>> for Color<S, T>
where T: Float + Component,
S: RgbSpace,
{
fn from(color: Alpha<$variant<$variant_ty_param, T>,T>) -> Color<S, T> {
Color::$variant(color.color)
}
}
impl<S, T> From<Alpha<$variant<$variant_ty_param, T>, T>> for Alpha<Color<S, T>,T>
where T: Float + Component,
S: RgbSpace,
{
fn from(color: Alpha<$variant<$variant_ty_param, T>,T>) -> Alpha<Color<S, T>,T> {
Alpha {
color: Color::$variant(color.color),
alpha: color.alpha,
}
}
}
)+
)
}
fn clamp<T: PartialOrd>(v: T, min: T, max: T) -> T {
if v < min {
min
} else if v > max {
max
} else {
v
}
}
make_color! {
Luma<Linear<S::WhitePoint>> {
linear_y(luma: T)[alpha: T] => new;
}
Rgb<Linear<S>> {
linear_rgb(red: T, green: T, blue: T)[alpha: T] => new;
}
Xyz<S::WhitePoint> {
xyz(x: T, y: T, z: T)[alpha: T] => new;
}
Yxy<S::WhitePoint> {
yxy(x: T, y: T, luma: T)[alpha: T] => new;
}
Lab<S::WhitePoint> {
lab(l: T, a: T, b: T)[alpha: T] => new;
}
Lch<S::WhitePoint> {
lch(l: T, chroma: T, hue: LabHue<T>)[alpha: T] => new;
}
Hsv<S> {
hsv(hue: RgbHue<T>, saturation: T, value: T)[alpha: T] => new;
}
Hsl<S> {
hsl(hue: RgbHue<T>, saturation: T, lightness: T)[alpha: T] => new;
}
Hwb<S> {
hwb(hue: RgbHue<T>, whiteness: T, balckness: T)[alpha: T] => new;
}
}
pub trait Limited {
fn is_valid(&self) -> bool;
fn clamp(&self) -> Self;
fn clamp_self(&mut self);
}
pub trait Mix {
type Scalar: Float;
fn mix(&self, other: &Self, factor: Self::Scalar) -> Self;
}
pub trait Shade: Sized {
type Scalar: Float;
fn lighten(&self, amount: Self::Scalar) -> Self;
fn darken(&self, amount: Self::Scalar) -> Self {
self.lighten(-amount)
}
}
pub trait GetHue {
type Hue;
fn get_hue(&self) -> Option<Self::Hue>;
}
pub trait Hue: GetHue {
fn with_hue<H: Into<Self::Hue>>(&self, hue: H) -> Self;
fn shift_hue<H: Into<Self::Hue>>(&self, amount: H) -> Self;
}
pub trait Saturate: Sized {
type Scalar: Float;
fn saturate(&self, factor: Self::Scalar) -> Self;
fn desaturate(&self, factor: Self::Scalar) -> Self {
self.saturate(-factor)
}
}
pub trait ComponentWise {
type Scalar;
fn component_wise<F: FnMut(Self::Scalar, Self::Scalar) -> Self::Scalar>(
&self,
other: &Self,
f: F,
) -> Self;
fn component_wise_self<F: FnMut(Self::Scalar) -> Self::Scalar>(&self, f: F) -> Self;
}
pub trait Component: Copy + Zero + PartialOrd + NumCast {
const LIMITED: bool;
fn max_intensity() -> Self;
fn convert<T: Component>(&self) -> T;
}
impl Component for f32 {
const LIMITED: bool = false;
fn max_intensity() -> Self {
1.0
}
fn convert<T: Component>(&self) -> T {
let scaled = *self * cast::<f32, _>(T::max_intensity());
if T::LIMITED {
cast(clamp(scaled, 0.0, cast(T::max_intensity())))
} else {
cast(scaled)
}
}
}
impl Component for f64 {
const LIMITED: bool = false;
fn max_intensity() -> Self {
1.0
}
fn convert<T: Component>(&self) -> T {
let scaled = *self * cast::<f64, _>(T::max_intensity());
if T::LIMITED {
cast(clamp(scaled, 0.0, cast(T::max_intensity())))
} else {
cast(scaled)
}
}
}
impl Component for u8 {
const LIMITED: bool = true;
fn max_intensity() -> Self {
std::u8::MAX
}
fn convert<T: Component>(&self) -> T {
let scaled = cast::<f64, _>(T::max_intensity())
* (cast::<f64, _>(*self) / cast::<f64, _>(Self::max_intensity()));
if T::LIMITED {
cast(clamp(scaled, 0.0, cast(T::max_intensity())))
} else {
cast(scaled)
}
}
}
impl Component for u16 {
const LIMITED: bool = true;
fn max_intensity() -> Self {
std::u16::MAX
}
fn convert<T: Component>(&self) -> T {
let scaled = cast::<f64, _>(T::max_intensity())
* (cast::<f64, _>(*self) / cast::<f64, _>(Self::max_intensity()));
if T::LIMITED {
cast(clamp(scaled, 0.0, cast(T::max_intensity())))
} else {
cast(scaled)
}
}
}
impl Component for u32 {
const LIMITED: bool = true;
fn max_intensity() -> Self {
std::u32::MAX
}
fn convert<T: Component>(&self) -> T {
let scaled = cast::<f64, _>(T::max_intensity())
* (cast::<f64, _>(*self) / cast::<f64, _>(Self::max_intensity()));
if T::LIMITED {
cast(clamp(scaled, 0.0, cast(T::max_intensity())))
} else {
cast(scaled)
}
}
}
impl Component for u64 {
const LIMITED: bool = true;
fn max_intensity() -> Self {
std::u64::MAX
}
fn convert<T: Component>(&self) -> T {
let scaled = cast::<f64, _>(T::max_intensity())
* (cast::<f64, _>(*self) / cast::<f64, _>(Self::max_intensity()));
if T::LIMITED {
cast(clamp(scaled, 0.0, cast(T::max_intensity())))
} else {
cast(scaled)
}
}
}
#[inline]
fn cast<T: NumCast, P: ToPrimitive>(prim: P) -> T {
NumCast::from(prim).unwrap()
}