Skip to content

Commit

Permalink
Add traits for converting to and from CAM16
Browse files Browse the repository at this point in the history
  • Loading branch information
Ogeon committed Apr 20, 2024
1 parent 4355225 commit 9973f6b
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 158 deletions.
60 changes: 59 additions & 1 deletion palette/src/cam16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,66 @@ pub use ucs_jab::UniformCam16UcsJab;
pub use ucs_jmh::UniformCam16UcsJmh;

mod full;
mod math;
pub(crate) mod math;
mod parameters;
mod partial;
mod ucs_jab;
mod ucs_jmh;

/// A trait for converting into a CAM16 color type from `C` without clamping.
pub trait Cam16FromUnclamped<WpParam, C> {
/// The number type that's used in `parameters` when converting.
type Scalar;

/// Converts `color` into `Self`, using the provided parameters.
fn cam16_from_unclamped(color: C, parameters: BakedParameters<WpParam, Self::Scalar>) -> Self;
}

/// A trait for converting into a CAM16 color type `C` without clamping.
pub trait IntoCam16Unclamped<WpParam, C> {
/// The number type that's used in `parameters` when converting.
type Scalar;

/// Converts `self` into `C`, using the provided parameters.
fn into_cam16_unclamped(self, parameters: BakedParameters<WpParam, Self::Scalar>) -> C;
}

impl<WpParam, T, U> IntoCam16Unclamped<WpParam, T> for U
where
T: Cam16FromUnclamped<WpParam, U>,
{
type Scalar = T::Scalar;

fn into_cam16_unclamped(self, parameters: BakedParameters<WpParam, Self::Scalar>) -> T {
T::cam16_from_unclamped(self, parameters)
}
}

/// A trait for converting from a CAM16 color type `C` without clamping.
pub trait FromCam16Unclamped<WpParam, C> {
/// The number type that's used in `parameters` when converting.
type Scalar;

/// Converts `cam16` into `Self`, using the provided parameters.
fn from_cam16_unclamped(cam16: C, parameters: BakedParameters<WpParam, Self::Scalar>) -> Self;
}

/// A trait for converting from a CAM16 color type into `C` without clamping.
pub trait Cam16IntoUnclamped<WpParam, C> {
/// The number type that's used in `parameters` when converting.
type Scalar;

/// Converts `self` into `C`, using the provided parameters.
fn cam16_into_unclamped(self, parameters: BakedParameters<WpParam, Self::Scalar>) -> C;
}

impl<WpParam, T, U> Cam16IntoUnclamped<WpParam, T> for U
where
T: FromCam16Unclamped<WpParam, U>,
{
type Scalar = T::Scalar;

fn cam16_into_unclamped(self, parameters: BakedParameters<WpParam, Self::Scalar>) -> T {
T::from_cam16_unclamped(self, parameters)
}
}
162 changes: 99 additions & 63 deletions palette/src/cam16/full.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use crate::{
angle::{RealAngle, SignedAngle},
angle::RealAngle,
bool_mask::{HasBoolMask, LazySelect},
hues::Cam16Hue,
num::{
Abs, Arithmetics, FromScalar, One, PartialCmp, Powf, Real, Signum, Sqrt, Trigonometry, Zero,
},
num::{Abs, Arithmetics, FromScalar, PartialCmp, Powf, Real, Signum, Sqrt, Trigonometry, Zero},
Alpha, GetHue, Xyz,
};

use super::{BakedParameters, Cam16Jch, WhitePointParameter};
use super::{
BakedParameters, Cam16FromUnclamped, Cam16IntoUnclamped, Cam16Jch, Cam16Jmh, Cam16Jsh,
Cam16Qch, Cam16Qmh, Cam16Qsh, FromCam16Unclamped, IntoCam16Unclamped, WhitePointParameter,
};

/// CIE CAM16 with an alpha component.
///
Expand Down Expand Up @@ -193,20 +194,11 @@ impl<T> Cam16<T> {
parameters: impl Into<BakedParameters<WpParam, T::Scalar>>,
) -> Self
where
WpParam: WhitePointParameter<T>,
T: Real
+ FromScalar
+ Arithmetics
+ Powf
+ Sqrt
+ Abs
+ Signum
+ Trigonometry
+ RealAngle
+ Clone,
T::Scalar: Clone,
Xyz<WpParam::StaticWp, T>: IntoCam16Unclamped<WpParam, Self, Scalar = T::Scalar>,
T: FromScalar,
WpParam: WhitePointParameter<T::Scalar>,
{
super::math::xyz_to_cam16(color.with_white_point(), parameters.into().inner)
color.into_cam16_unclamped(parameters.into())
}

/// Construct an XYZ color that matches these CIE CAM16 attributes, under
Expand Down Expand Up @@ -251,25 +243,11 @@ impl<T> Cam16<T> {
parameters: impl Into<BakedParameters<WpParam, T::Scalar>>,
) -> Xyz<WpParam::StaticWp, T>
where
Self: Cam16IntoUnclamped<WpParam, Xyz<WpParam::StaticWp, T>, Scalar = T::Scalar>,
WpParam: WhitePointParameter<T>,
T: Real
+ FromScalar
+ One
+ Zero
+ Sqrt
+ Powf
+ Abs
+ Signum
+ Arithmetics
+ Trigonometry
+ RealAngle
+ SignedAngle
+ PartialCmp
+ Clone,
T::Mask: LazySelect<T> + Clone,
T::Scalar: Clone,
T: FromScalar,
{
Cam16Jch::from_full(self).into_xyz(parameters)
self.cam16_into_unclamped(parameters.into())
}
}

Expand Down Expand Up @@ -308,18 +286,9 @@ impl<T, A> Alpha<Cam16<T>, A> {
parameters: impl Into<BakedParameters<WpParam, T::Scalar>>,
) -> Self
where
WpParam: WhitePointParameter<T>,
T: Real
+ FromScalar
+ Arithmetics
+ Powf
+ Sqrt
+ Abs
+ Signum
+ Trigonometry
+ RealAngle
+ Clone,
T::Scalar: Clone,
Xyz<WpParam::StaticWp, T>: IntoCam16Unclamped<WpParam, Cam16<T>, Scalar = T::Scalar>,
T: FromScalar,
WpParam: WhitePointParameter<T::Scalar>,
{
let Alpha { color, alpha } = color;

Expand Down Expand Up @@ -371,23 +340,9 @@ impl<T, A> Alpha<Cam16<T>, A> {
parameters: impl Into<BakedParameters<WpParam, T::Scalar>>,
) -> Alpha<Xyz<WpParam::StaticWp, T>, A>
where
Cam16<T>: Cam16IntoUnclamped<WpParam, Xyz<WpParam::StaticWp, T>, Scalar = T::Scalar>,
WpParam: WhitePointParameter<T>,
T: Real
+ FromScalar
+ One
+ Zero
+ Sqrt
+ Powf
+ Abs
+ Signum
+ Arithmetics
+ Trigonometry
+ RealAngle
+ SignedAngle
+ PartialCmp
+ Clone,
T::Mask: LazySelect<T> + Clone,
T::Scalar: Clone,
T: FromScalar,
{
let Alpha { color, alpha } = self;

Expand All @@ -398,6 +353,87 @@ impl<T, A> Alpha<Cam16<T>, A> {
}
}

impl<WpParam, T> Cam16FromUnclamped<WpParam, Xyz<WpParam::StaticWp, T>> for Cam16<T>
where
WpParam: WhitePointParameter<T::Scalar>,
T: Real
+ FromScalar
+ Arithmetics
+ Powf
+ Sqrt
+ Abs
+ Signum
+ Trigonometry
+ RealAngle
+ Clone,
T::Scalar: Clone,
{
type Scalar = T::Scalar;

fn cam16_from_unclamped(
color: Xyz<WpParam::StaticWp, T>,
parameters: BakedParameters<WpParam, Self::Scalar>,
) -> Self {
super::math::xyz_to_cam16(color.with_white_point(), parameters.inner)
}
}

macro_rules! impl_from_cam16_partial {
($($name: ident),+) => {
$(
impl<WpParam, T> Cam16FromUnclamped<WpParam, $name<T>> for Cam16<T>
where
WpParam: WhitePointParameter<T>,
T: Real + FromScalar + Zero + Arithmetics + Sqrt + PartialCmp + Clone,
T::Mask: LazySelect<T> + Clone,
T::Scalar: Clone
{
type Scalar = T::Scalar;

fn cam16_from_unclamped(
cam16: $name<T>,
parameters: crate::cam16::BakedParameters<WpParam, Self::Scalar>,
) -> Self {
let (
luminance,
chromaticity,
hue,
) = cam16.into_dynamic();

let (lightness, brightness) = luminance.into_cam16(parameters.clone());
let (chroma, colorfulness, saturation) =
chromaticity.into_cam16(lightness.clone(), parameters);

Cam16 {
lightness,
chroma,
hue,
brightness,
colorfulness,
saturation,
}
}
}

impl<WpParam, T> FromCam16Unclamped<WpParam, $name<T>> for Cam16<T>
where
Self: Cam16FromUnclamped<WpParam, $name<T>>,
{
type Scalar = <Self as Cam16FromUnclamped<WpParam, $name<T>>>::Scalar;

fn from_cam16_unclamped(
cam16: $name<T>,
parameters: crate::cam16::BakedParameters<WpParam, Self::Scalar>,
) -> Self {
Self::cam16_from_unclamped(cam16, parameters)
}
}
)+
};
}

impl_from_cam16_partial!(Cam16Jmh, Cam16Jch, Cam16Jsh, Cam16Qmh, Cam16Qch, Cam16Qsh);

impl<T> GetHue for Cam16<T>
where
T: Clone,
Expand Down
24 changes: 12 additions & 12 deletions palette/src/cam16/math/chromaticity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
math::{self, DependentParameters},
BakedParameters,
},
num::{Arithmetics, PartialCmp, Real, Sqrt, Zero},
num::{Arithmetics, FromScalar, PartialCmp, Real, Sqrt, Zero},
};

/// One the apparent chromatic intensity metrics of CAM16.
Expand All @@ -27,10 +27,10 @@ impl<T> ChromaticityType<T> {
pub(crate) fn into_cam16<Wp>(
self,
lightness: T,
parameters: BakedParameters<Wp, T>,
parameters: BakedParameters<Wp, T::Scalar>,
) -> (T, T, T)
where
T: Real + Zero + Arithmetics + Sqrt + PartialCmp + Clone,
T: Real + FromScalar + Zero + Arithmetics + Sqrt + PartialCmp + Clone,
T::Mask: LazySelect<T> + Clone,
{
let DependentParameters { c, a_w, f_l_4, .. } = parameters.inner;
Expand All @@ -40,15 +40,15 @@ impl<T> ChromaticityType<T> {
ChromaticityType::Chroma(chroma) => {
let colorfulness = lazy_select! {
if is_black.clone() => T::zero(),
else => math::chroma_to_colorfulness(chroma.clone(), f_l_4)
else => math::chroma_to_colorfulness(chroma.clone(), T::from_scalar(f_l_4))
};
let saturation = lazy_select! {
if is_black.clone() => T::zero(),
else => math::chroma_to_saturation(
chroma.clone(),
lightness,
c,
a_w,
T::from_scalar(c),
T::from_scalar(a_w),
)
};
let chroma = is_black.select(T::zero(), chroma);
Expand All @@ -58,15 +58,15 @@ impl<T> ChromaticityType<T> {
ChromaticityType::Colorfulness(colorfulness) => {
let chroma = lazy_select! {
if is_black.clone() => T::zero(),
else => math::colorfulness_to_chroma(colorfulness.clone(), f_l_4)
else => math::colorfulness_to_chroma(colorfulness.clone(), T::from_scalar(f_l_4))
};
let saturation = lazy_select! {
if is_black.clone() => T::zero(),
else => math::chroma_to_saturation(
chroma.clone(),
lightness,
c,
a_w,
T::from_scalar(c),
T::from_scalar(a_w),
)
};
let colorfulness = is_black.select(T::zero(), colorfulness);
Expand All @@ -79,13 +79,13 @@ impl<T> ChromaticityType<T> {
else => math::saturation_to_chroma(
saturation.clone(),
lightness,
c,
a_w,
T::from_scalar(c),
T::from_scalar(a_w),
)
};
let colorfulness = lazy_select! {
if is_black.clone() => T::zero(),
else => math::chroma_to_colorfulness(chroma.clone(), f_l_4)
else => math::chroma_to_colorfulness(chroma.clone(), T::from_scalar(f_l_4))
};
let saturation = is_black.select(T::zero(), saturation);

Expand Down
Loading

0 comments on commit 9973f6b

Please sign in to comment.