Skip to content

Commit

Permalink
Flesh out full and partial CAM16 with traits, constructors and transp…
Browse files Browse the repository at this point in the history
…arency
  • Loading branch information
Ogeon committed Mar 31, 2024
1 parent ca34a3a commit af713ef
Show file tree
Hide file tree
Showing 8 changed files with 630 additions and 65 deletions.
172 changes: 144 additions & 28 deletions palette/src/cam16/full.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
use crate::{
angle::{RealAngle, SignedAngle},
bool_mask::LazySelect,
bool_mask::{HasBoolMask, LazySelect},
hues::Cam16Hue,
num::{
Abs, Arithmetics, Clamp, ClampAssign, One, PartialCmp, Powf, Real, Signum, Sqrt,
Trigonometry, Zero,
},
white_point, Xyz,
num::{Abs, Arithmetics, One, PartialCmp, Powf, Real, Signum, Sqrt, Trigonometry, Zero},
white_point, Alpha, GetHue, Xyz,
};

use super::{
BakedParameters, Cam16Chromaticity, Cam16Luminance, PartialCam16, WhitePointParameter,
};

/// CIE CAM16 with an alpha component. See the [`Cam16a` implementation in
/// `Alpha`](crate::Alpha#Cam16a).
pub type Cam16a<T> = Alpha<Cam16<T>, T>;

/// The CIE CAM16 color appearance model.
///
/// It's a set of six technically defined attributes that describe the
Expand All @@ -33,8 +34,9 @@ use super::{
/// correlated and derived from each other. This library provides a separate
/// [`PartialCam16`][super::PartialCam16] to make it easier to correctly specify
/// a minimum attribute set.
#[derive(Clone, Copy, Debug, WithAlpha)]
#[derive(Clone, Copy, Debug, WithAlpha, Default)]
#[palette(palette_internal, component = "T")]
#[repr(C)]
pub struct Cam16<T> {
/// The [lightness](https://en.wikipedia.org/wiki/Lightness) (J) of the color.
#[doc(alias = "J")]
Expand Down Expand Up @@ -111,7 +113,7 @@ impl<T> Cam16<T> {
/// Reconstruct a full set of CIE CAM16 attributes, using the original viewing conditions.
#[inline]
pub fn from_partial<WpParam, L, C>(
partial: PartialCam16<T, L, C>,
partial: PartialCam16<L, C, T>,
parameters: impl Into<BakedParameters<WpParam, T>>,
) -> Self
where
Expand All @@ -128,7 +130,7 @@ impl<T> Cam16<T> {
///
/// It's also possible to use `PartialCam16::from` or `Cam16::into`.
#[inline]
pub fn into_partial<L, C>(self) -> PartialCam16<T, L, C>
pub fn into_partial<L, C>(self) -> PartialCam16<L, C, T>
where
L: Cam16Luminance<T>,
C: Cam16Chromaticity<T>,
Expand All @@ -137,33 +139,144 @@ impl<T> Cam16<T> {
}
}

impl<T> crate::Clamp for Cam16<T>
///<span id="Cam16a"></span>[`Cam16a`](crate::cam16::Cam16a) implementations.
impl<T, A> Alpha<Cam16<T>, A> {
/// Derive CIE CAM16 attributes with transparency for the provided color,
/// under the provided viewing conditions.
#[inline]
pub fn from_xyz<WpParam>(
color: Alpha<Xyz<WpParam::StaticWp, T>, A>,
parameters: impl Into<BakedParameters<WpParam, T>>,
) -> Self
where
WpParam: WhitePointParameter<T>,
T: Real + Arithmetics + Powf + Sqrt + Abs + Signum + Trigonometry + RealAngle + Clone,
{
let Alpha { color, alpha } = color;

Alpha {
color: Cam16::from_xyz(color, parameters),
alpha,
}
}

/// Construct an XYZ color with transparency, that matches these CIE CAM16
/// attributes, under the provided viewing conditions.
///
/// This assumes that all of the correlated attributes are consistent, as
/// only some of them are actually used. You may want to use
/// [`PartialCam16a`](crate::cam16::PartialCam16a) for more control over
/// which set of attributes that should be.
#[inline]
pub fn into_xyz<WpParam>(
self,
parameters: impl Into<BakedParameters<WpParam, T>>,
) -> Alpha<Xyz<WpParam::StaticWp, T>, A>
where
WpParam: WhitePointParameter<T>,
T: Real
+ One
+ Zero
+ Sqrt
+ Powf
+ Abs
+ Signum
+ Arithmetics
+ Trigonometry
+ RealAngle
+ SignedAngle
+ PartialCmp
+ Clone,
T::Mask: LazySelect<Xyz<white_point::Any, T>>,
{
let Alpha { color, alpha } = self;

Alpha {
color: color.into_xyz(parameters),
alpha,
}
}

/// Reconstruct a full set of CIE CAM16 attributes with transparency, using
/// the original viewing conditions.
#[inline]
pub fn from_partial<WpParam, L, C>(
partial: Alpha<PartialCam16<L, C, T>, A>,
parameters: impl Into<BakedParameters<WpParam, T>>,
) -> Self
where
WpParam: WhitePointParameter<T>,
T: Real + Zero + Arithmetics + Sqrt + PartialCmp + Clone,
T::Mask: LazySelect<T> + Clone,
L: Cam16Luminance<T>,
C: Cam16Chromaticity<T>,
{
let Alpha { color, alpha } = partial;

Alpha {
color: Cam16::from_partial(color, parameters),
alpha,
}
}

/// Create a partial set of CIE CAM16 attributes with transparency.
///
/// It's also possible to use `PartialCam16a::from` or `Cam16a::into`.
#[inline]
pub fn into_partial<L, C>(self) -> Alpha<PartialCam16<L, C, T>, A>
where
L: Cam16Luminance<T>,
C: Cam16Chromaticity<T>,
{
let Alpha { color, alpha } = self;

Alpha {
color: PartialCam16::from_full(color),
alpha,
}
}
}

impl<T> GetHue for Cam16<T>
where
T: Clamp + Zero,
T: Clone,
{
fn clamp(self) -> Self {
Self {
lightness: self.lightness.clamp_min(T::zero()),
chroma: self.chroma.clamp_min(T::zero()),
hue: self.hue,
brightness: self.brightness.clamp_min(T::zero()),
colorfulness: self.colorfulness.clamp_min(T::zero()),
saturation: self.saturation.clamp_min(T::zero()),
}
type Hue = Cam16Hue<T>;

fn get_hue(&self) -> Cam16Hue<T> {
self.hue.clone()
}
}

impl<T> crate::ClampAssign for Cam16<T>
impl<T> HasBoolMask for Cam16<T>
where
T: ClampAssign + Zero,
T: HasBoolMask,
{
fn clamp_assign(&mut self) {
self.lightness.clamp_min_assign(T::zero());
self.chroma.clamp_min_assign(T::zero());
self.brightness.clamp_min_assign(T::zero());
self.colorfulness.clamp_min_assign(T::zero());
self.saturation.clamp_min_assign(T::zero());
type Mask = T::Mask;
}

// Macro implementations

impl_is_within_bounds! {
Cam16 {
lightness => [T::zero(), None],
chroma => [T::zero(), None],
brightness => [T::zero(), None],
colorfulness => [T::zero(), None],
saturation => [T::zero(), None]
}
where T: Zero
}
impl_clamp! {
Cam16 {
lightness => [T::zero()],
chroma => [T::zero()],
brightness => [T::zero()],
colorfulness => [T::zero()],
saturation => [T::zero()]
}
other {hue}
where T: Zero
}

impl_eq_hue!(
Expand All @@ -172,7 +285,10 @@ impl_eq_hue!(
[lightness, chroma, brightness, colorfulness, saturation]
);

// Unit test

#[cfg(test)]
#[cfg(feature = "approx")]
mod test {
use crate::{
cam16::{ChromaticityType, LuminanceType, Parameters, PartialCam16},
Expand Down
2 changes: 1 addition & 1 deletion palette/src/cam16/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl<T> Parameters<Xyz<white_point::Any, T>, T> {
}
}

#[cfg(test)]
#[cfg(all(test, feature = "approx"))]
impl<Wp> Parameters<StaticWp<Wp>, f64> {
/// Only used in unit tests and corresponds to the defaults from https://observablehq.com/@jrus/cam16.
pub(crate) const TEST_DEFAULTS: Self = Self {
Expand Down
Loading

0 comments on commit af713ef

Please sign in to comment.