#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
#![doc(html_root_url = "https://docs.rs/palette/0.5.0/palette/")]
#![cfg_attr(feature = "strict", deny(missing_docs))]
#![cfg_attr(feature = "strict", deny(warnings))]
#[cfg(any(feature = "std", test))]
extern crate core;
#[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 = "serializing")]
#[macro_use]
extern crate serde;
#[cfg(all(test, feature = "serializing"))]
extern crate serde_json;
use num_traits::{NumCast, ToPrimitive, Zero};
use float::Float;
use luma::Luma;
#[doc(hidden)]
pub use palette_derive::*;
pub use alpha::Alpha;
pub use blend::Blend;
#[cfg(feature = "std")]
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::{ConvertFrom, ConvertInto, OutOfBounds, 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 core::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;
#[cfg(feature = "std")]
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;
pub mod float;
fn clamp<T: PartialOrd>(v: T, min: T, max: T) -> T {
if v < min {
min
} else if v > max {
max
} else {
v
}
}
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(Float::round(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(Float::round(scaled), 0.0, cast(T::max_intensity())))
} else {
cast(scaled)
}
}
}
impl Component for u8 {
const LIMITED: bool = true;
fn max_intensity() -> Self {
core::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(Float::round(scaled), 0.0, cast(T::max_intensity())))
} else {
cast(scaled)
}
}
}
impl Component for u16 {
const LIMITED: bool = true;
fn max_intensity() -> Self {
core::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(Float::round(scaled), 0.0, cast(T::max_intensity())))
} else {
cast(scaled)
}
}
}
impl Component for u32 {
const LIMITED: bool = true;
fn max_intensity() -> Self {
core::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(Float::round(scaled), 0.0, cast(T::max_intensity())))
} else {
cast(scaled)
}
}
}
impl Component for u64 {
const LIMITED: bool = true;
fn max_intensity() -> Self {
core::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(Float::round(scaled), 0.0, cast(T::max_intensity())))
} else {
cast(scaled)
}
}
}
#[inline]
fn cast<T: NumCast, P: ToPrimitive>(prim: P) -> T {
NumCast::from(prim).unwrap()
}