From af713ef99ce237ef12964613643b5229614b004b Mon Sep 17 00:00:00 2001 From: Erik Hedvall Date: Sun, 31 Mar 2024 17:35:52 +0200 Subject: [PATCH] Flesh out full and partial CAM16 with traits, constructors and transparency --- palette/src/cam16/full.rs | 172 ++++++++-- palette/src/cam16/parameters.rs | 2 +- palette/src/cam16/partial.rs | 400 +++++++++++++++++++++- palette/src/cam16/partial/chromaticity.rs | 57 ++- palette/src/cam16/partial/luminance.rs | 38 +- palette/src/cam16/ucs_jab.rs | 1 + palette/src/cam16/ucs_jmh.rs | 21 +- palette/src/macros/equality.rs | 4 +- 8 files changed, 630 insertions(+), 65 deletions(-) diff --git a/palette/src/cam16/full.rs b/palette/src/cam16/full.rs index 05f59f08d..8a5c1ee1f 100644 --- a/palette/src/cam16/full.rs +++ b/palette/src/cam16/full.rs @@ -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 = Alpha, T>; + /// The CIE CAM16 color appearance model. /// /// It's a set of six technically defined attributes that describe the @@ -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 { /// The [lightness](https://en.wikipedia.org/wiki/Lightness) (J) of the color. #[doc(alias = "J")] @@ -111,7 +113,7 @@ impl Cam16 { /// Reconstruct a full set of CIE CAM16 attributes, using the original viewing conditions. #[inline] pub fn from_partial( - partial: PartialCam16, + partial: PartialCam16, parameters: impl Into>, ) -> Self where @@ -128,7 +130,7 @@ impl Cam16 { /// /// It's also possible to use `PartialCam16::from` or `Cam16::into`. #[inline] - pub fn into_partial(self) -> PartialCam16 + pub fn into_partial(self) -> PartialCam16 where L: Cam16Luminance, C: Cam16Chromaticity, @@ -137,33 +139,144 @@ impl Cam16 { } } -impl crate::Clamp for Cam16 +///[`Cam16a`](crate::cam16::Cam16a) implementations. +impl Alpha, A> { + /// Derive CIE CAM16 attributes with transparency for the provided color, + /// under the provided viewing conditions. + #[inline] + pub fn from_xyz( + color: Alpha, A>, + parameters: impl Into>, + ) -> Self + where + WpParam: WhitePointParameter, + 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( + self, + parameters: impl Into>, + ) -> Alpha, A> + where + WpParam: WhitePointParameter, + T: Real + + One + + Zero + + Sqrt + + Powf + + Abs + + Signum + + Arithmetics + + Trigonometry + + RealAngle + + SignedAngle + + PartialCmp + + Clone, + T::Mask: LazySelect>, + { + 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( + partial: Alpha, A>, + parameters: impl Into>, + ) -> Self + where + WpParam: WhitePointParameter, + T: Real + Zero + Arithmetics + Sqrt + PartialCmp + Clone, + T::Mask: LazySelect + Clone, + L: Cam16Luminance, + C: Cam16Chromaticity, + { + 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(self) -> Alpha, A> + where + L: Cam16Luminance, + C: Cam16Chromaticity, + { + let Alpha { color, alpha } = self; + + Alpha { + color: PartialCam16::from_full(color), + alpha, + } + } +} + +impl GetHue for Cam16 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; + + fn get_hue(&self) -> Cam16Hue { + self.hue.clone() } } -impl crate::ClampAssign for Cam16 +impl HasBoolMask for Cam16 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!( @@ -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}, diff --git a/palette/src/cam16/parameters.rs b/palette/src/cam16/parameters.rs index 35cc9e73f..fe192ff63 100644 --- a/palette/src/cam16/parameters.rs +++ b/palette/src/cam16/parameters.rs @@ -134,7 +134,7 @@ impl Parameters, T> { } } -#[cfg(test)] +#[cfg(all(test, feature = "approx"))] impl Parameters, f64> { /// Only used in unit tests and corresponds to the defaults from https://observablehq.com/@jrus/cam16. pub(crate) const TEST_DEFAULTS: Self = Self { diff --git a/palette/src/cam16/partial.rs b/palette/src/cam16/partial.rs index 09cadeb9a..27b79e1a9 100644 --- a/palette/src/cam16/partial.rs +++ b/palette/src/cam16/partial.rs @@ -7,7 +7,7 @@ use crate::{ Abs, Arithmetics, ClampAssign, Exp, One, PartialCmp, Powf, Real, Signum, Sqrt, Trigonometry, Zero, }, - white_point, Xyz, + white_point, Alpha, Xyz, }; use super::{BakedParameters, Cam16, Cam16UcsJmh, WhitePointParameter}; @@ -20,25 +20,57 @@ mod luminance; /// An alias for [`PartialCam16`], where the chromaticity and luminance /// attributes are decided during runtime. -pub type DynPartialCam16 = PartialCam16, ChromaticityType>; +pub type DynPartialCam16 = PartialCam16, ChromaticityType, T>; + +/// Dynamic partial CIE CAM16 with an alpha component. See the [`PartialCam16a` +/// implementation in `Alpha`](crate::Alpha#PartialCam16a). +pub type DynPartialCam16a = Alpha, T>; /// An alias for [`PartialCam16`], with lightness and chroma. -pub type PartialCam16Jch = PartialCam16, Chroma>; +pub type PartialCam16Jch = PartialCam16, Chroma, T>; + +/// Partial CIE CAM16 with lightness, chroma, and an alpha component. See the +/// [`PartialCam16a` implementation in `Alpha`](crate::Alpha#PartialCam16a). +pub type PartialCam16Jcha = Alpha, T>; /// An alias for [`PartialCam16`], with lightness and colorfulness. -pub type PartialCam16Jmh = PartialCam16, Colorfulness>; +pub type PartialCam16Jmh = PartialCam16, Colorfulness, T>; + +/// Partial CIE CAM16 with lightness, colorfulness, and an alpha component. See +/// the [`PartialCam16a` implementation in `Alpha`](crate::Alpha#PartialCam16a). +pub type PartialCam16Jmha = Alpha, T>; /// An alias for [`PartialCam16`], with lightness and saturation. -pub type PartialCam16Jsh = PartialCam16, Saturation>; +pub type PartialCam16Jsh = PartialCam16, Saturation, T>; + +/// Partial CIE CAM16 with lightness, saturation, and an alpha component. See +/// the [`PartialCam16a` implementation in `Alpha`](crate::Alpha#PartialCam16a). +pub type PartialCam16Jsha = Alpha, T>; /// An alias for [`PartialCam16`], with brightness and chroma. -pub type PartialCam16Qch = PartialCam16, Chroma>; +pub type PartialCam16Qch = PartialCam16, Chroma, T>; + +/// Partial CIE CAM16 with brightness, chroma, and an alpha component. See the +/// [`PartialCam16a` implementation in `Alpha`](crate::Alpha#PartialCam16a). +pub type PartialCam16Qcha = Alpha, T>; /// An alias for [`PartialCam16`], with brightness and colorfulness. -pub type PartialCam16Qmh = PartialCam16, Colorfulness>; +pub type PartialCam16Qmh = PartialCam16, Colorfulness, T>; + +/// Partial CIE CAM16 with brightness, colorfulness, and an alpha component. See +/// the [`PartialCam16a` implementation in `Alpha`](crate::Alpha#PartialCam16a). +pub type PartialCam16Qmha = Alpha, T>; /// An alias for [`PartialCam16`], with brightness and saturation. -pub type PartialCam16Qsh = PartialCam16, Saturation>; +pub type PartialCam16Qsh = PartialCam16, Saturation, T>; + +/// Partial CIE CAM16 with brightness, saturation, and an alpha component. See +/// the [`PartialCam16a` implementation in `Alpha`](crate::Alpha#PartialCam16a). +pub type PartialCam16Qsha = Alpha, T>; + +/// Partial CIE CAM16 with an alpha component. See the [`PartialCam16a` +/// implementation in `Alpha`](crate::Alpha#PartialCam16a). +pub type PartialCam16a = Alpha, T>; /// A partial version of [`Cam16`] with only one of each kind of attribute. /// @@ -60,7 +92,7 @@ pub type PartialCam16Qsh = PartialCam16, Saturation>; color_group = "cam16", skip_derives(Cam16, PartialCam16, Cam16UcsJmh) )] -pub struct PartialCam16 { +pub struct PartialCam16 { /// The [lightness](https://en.wikipedia.org/wiki/Lightness) (J) or /// [brightness](https://en.wikipedia.org/wiki/Brightness) (Q) of the color. pub luminance: L, @@ -75,7 +107,45 @@ pub struct PartialCam16 { pub hue: Cam16Hue, } -impl PartialCam16 +impl PartialCam16 { + /// Create a partial CIE CAM16 color. + pub fn new(luminance: LIn, chromaticity: CIn, hue: H) -> Self + where + LIn: Into, + CIn: Into, + H: Into>, + { + Self::new_const(luminance.into(), chromaticity.into(), hue.into()) + } + + /// Create a partial CIE CAM16 color. This is the same as + /// `PartialCam16::new` without the generic luminance, chromaticity and hue + /// types. It's temporary until `const fn` supports traits. + pub const fn new_const(luminance: L, chromaticity: C, hue: Cam16Hue) -> Self { + Self { + luminance, + chromaticity, + hue, + } + } + + /// Convert to a `(luminance, chromaticity, hue)` tuple. + pub fn into_components(self) -> (L, C, Cam16Hue) { + (self.luminance, self.chromaticity, self.hue) + } + + /// Convert from a `(luminance, chromaticity, hue)` tuple. + pub fn from_components((luminance, chromaticity, hue): (LIn, CIn, H)) -> Self + where + LIn: Into, + CIn: Into, + H: Into>, + { + Self::new(luminance, chromaticity, hue) + } +} + +impl PartialCam16 where L: Cam16Luminance, C: Cam16Chromaticity, @@ -187,13 +257,159 @@ where } } -impl FromColorUnclamped> for PartialCam16 { - fn from_color_unclamped(val: PartialCam16) -> Self { +///[`PartialCam16a`](crate::cam16::PartialCam16a) +///implementations. +impl Alpha, A> { + /// Create a partial CIE CAM16 color with transparency. + pub fn new(luminance: LIn, chromaticity: CIn, hue: H, alpha: A) -> Self + where + LIn: Into, + CIn: Into, + H: Into>, + { + Self::new_const(luminance.into(), chromaticity.into(), hue.into(), alpha) + } + + /// Create a partial CIE CAM16 color with transparency. This is the same as + /// `PartialCam16a::new` without the generic luminance, chromaticity and hue + /// types. It's temporary until `const fn` supports traits. + pub const fn new_const(luminance: L, chromaticity: C, hue: Cam16Hue, alpha: A) -> Self { + Alpha { + color: PartialCam16::new_const(luminance, chromaticity, hue), + alpha, + } + } + + /// Convert to a `(luminance, chromaticity, hue, alpha)` tuple. + pub fn into_components(self) -> (L, C, Cam16Hue, A) { + ( + self.color.luminance, + self.color.chromaticity, + self.color.hue, + self.alpha, + ) + } + + /// Convert from a `(luminance, chromaticity, hue, alpha)` tuple. + pub fn from_components( + (luminance, chromaticity, hue, alpha): (LIn, CIn, H, A), + ) -> Self + where + LIn: Into, + CIn: Into, + H: Into>, + { + Self::new(luminance, chromaticity, hue, alpha) + } +} + +impl Alpha, A> +where + L: Cam16Luminance, + C: Cam16Chromaticity, +{ + /// Derive partial CIE CAM16 attributes with transparency, for the provided + /// color, under the provided viewing conditions. + #[inline] + pub fn from_xyz( + color: Alpha, A>, + parameters: impl Into>, + ) -> Self + where + WpParam: WhitePointParameter, + T: Real + Arithmetics + Powf + Sqrt + Abs + Signum + Trigonometry + RealAngle + Clone, + { + let Alpha { color, alpha } = color; + + Alpha { + color: PartialCam16::from_xyz(color, parameters), + alpha, + } + } + + /// Construct an XYZ color with transparency, from these CIE CAM16 + /// attributes, under the provided viewing conditions. + #[inline] + pub fn into_xyz( + self, + parameters: impl Into>, + ) -> Alpha, A> + where + WpParam: WhitePointParameter, + T: Real + + One + + Zero + + Sqrt + + Powf + + Abs + + Signum + + Arithmetics + + Trigonometry + + RealAngle + + SignedAngle + + PartialCmp + + Clone, + T::Mask: LazySelect>, + { + let Alpha { color, alpha } = self; + + Alpha { + color: color.into_xyz(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 from_full(full: Alpha, A>) -> Self { + let Alpha { color, alpha } = full; + + Alpha { + color: PartialCam16::from_full(color), + alpha, + } + } + + /// Reconstruct a full set of CIE CAM16 attributes with transparency, using + /// the original viewing conditions. + #[inline] + pub fn into_full( + self, + parameters: impl Into>, + ) -> Alpha, A> + where + WpParam: WhitePointParameter, + T: Real + Zero + Arithmetics + Sqrt + PartialCmp + Clone, + T::Mask: LazySelect + Clone, + { + let Alpha { color, alpha } = self; + + Alpha { + color: color.into_full(parameters), + alpha, + } + } + + /// Turn the chromaticity and luminance into dynamically decided attributes. + pub fn into_dynamic(self) -> Alpha, A> { + let Alpha { color, alpha } = self; + + Alpha { + color: color.into_dynamic(), + alpha, + } + } +} + +impl FromColorUnclamped> for PartialCam16 { + fn from_color_unclamped(val: PartialCam16) -> Self { val } } -impl FromColorUnclamped> for PartialCam16 +impl FromColorUnclamped> for PartialCam16 where L: Cam16Luminance, C: Cam16Chromaticity, @@ -230,7 +446,7 @@ where } } -impl crate::Clamp for PartialCam16 +impl crate::Clamp for PartialCam16 where C: crate::Clamp, L: crate::Clamp, @@ -244,7 +460,7 @@ where } } -impl crate::ClampAssign for PartialCam16 +impl crate::ClampAssign for PartialCam16 where T: ClampAssign + Zero, C: crate::ClampAssign, @@ -256,7 +472,7 @@ where } } -impl From> for PartialCam16 +impl From> for PartialCam16 where L: Cam16Luminance, C: Cam16Chromaticity, @@ -266,7 +482,157 @@ where } } +impl From<(LIn, CIn, H)> for PartialCam16 +where + LIn: Into, + CIn: Into, + H: Into>, +{ + fn from(components: (LIn, CIn, H)) -> Self { + Self::from_components(components) + } +} + +impl From> for (L, C, Cam16Hue) { + fn from(color: PartialCam16) -> (L, C, Cam16Hue) { + color.into_components() + } +} + +impl From<(LIn, CIn, H, A)> for crate::Alpha, A> +where + LIn: Into, + CIn: Into, + H: Into>, +{ + fn from(components: (LIn, CIn, H, A)) -> Self { + Self::from_components(components) + } +} + +impl From, A>> for (L, C, Cam16Hue, A) { + fn from(color: crate::Alpha, A>) -> (L, C, Cam16Hue, A) { + color.into_components() + } +} + +impl PartialEq for PartialCam16 +where + L: PartialEq, + C: PartialEq, + T: PartialEq, + Cam16Hue: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.luminance == other.luminance + && self.chromaticity == other.chromaticity + && self.hue == other.hue + } +} +impl Eq for PartialCam16 +where + L: Eq, + C: Eq, + T: Eq, + Cam16Hue: Eq, +{ +} + +#[cfg(feature = "approx")] +impl approx::AbsDiffEq for PartialCam16 +where + L: approx::AbsDiffEq, + C: approx::AbsDiffEq, + T: approx::AbsDiffEq, + T::Epsilon: Clone, + Cam16Hue: approx::AbsDiffEq, +{ + type Epsilon = T::Epsilon; + fn default_epsilon() -> Self::Epsilon { + T::default_epsilon() + } + fn abs_diff_eq(&self, other: &Self, epsilon: T::Epsilon) -> bool { + self.luminance + .abs_diff_eq(&other.luminance, epsilon.clone()) + && self + .chromaticity + .abs_diff_eq(&other.chromaticity, epsilon.clone()) + && self.hue.abs_diff_eq(&other.hue, epsilon) + } + fn abs_diff_ne(&self, other: &Self, epsilon: T::Epsilon) -> bool { + self.luminance + .abs_diff_ne(&other.luminance, epsilon.clone()) + || self + .chromaticity + .abs_diff_ne(&other.chromaticity, epsilon.clone()) + || self.hue.abs_diff_ne(&other.hue, epsilon) + } +} +#[cfg(feature = "approx")] +impl approx::RelativeEq for PartialCam16 +where + L: approx::RelativeEq, + C: approx::RelativeEq, + T: approx::RelativeEq, + T::Epsilon: Clone, + Cam16Hue: approx::RelativeEq + approx::AbsDiffEq, +{ + fn default_max_relative() -> T::Epsilon { + T::default_max_relative() + } + fn relative_eq(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool { + self.luminance + .relative_eq(&other.luminance, epsilon.clone(), max_relative.clone()) + && self.chromaticity.relative_eq( + &other.chromaticity, + epsilon.clone(), + max_relative.clone(), + ) + && self.hue.relative_eq(&other.hue, epsilon, max_relative) + } + fn relative_ne(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool { + self.luminance + .relative_ne(&other.luminance, epsilon.clone(), max_relative.clone()) + || self.chromaticity.relative_ne( + &other.chromaticity, + epsilon.clone(), + max_relative.clone(), + ) + || self.hue.relative_ne(&other.hue, epsilon, max_relative) + } +} +#[cfg(feature = "approx")] +impl approx::UlpsEq for PartialCam16 +where + L: approx::UlpsEq, + C: approx::UlpsEq, + T: approx::UlpsEq, + T::Epsilon: Clone, + Cam16Hue: approx::UlpsEq + approx::AbsDiffEq, +{ + fn default_max_ulps() -> u32 { + T::default_max_ulps() + } + fn ulps_eq(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool { + self.luminance + .ulps_eq(&other.luminance, epsilon.clone(), max_ulps) + && self + .chromaticity + .ulps_eq(&other.chromaticity, epsilon.clone(), max_ulps) + && self.hue.ulps_eq(&other.hue, epsilon, max_ulps) + } + fn ulps_ne(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool { + self.luminance + .ulps_ne(&other.luminance, epsilon.clone(), max_ulps) + || self + .chromaticity + .ulps_ne(&other.chromaticity, epsilon.clone(), max_ulps) + || self.hue.ulps_ne(&other.hue, epsilon, max_ulps) + } +} + #[cfg(test)] +#[cfg(feature = "approx")] mod test { use super::{ PartialCam16Jch, PartialCam16Jmh, PartialCam16Jsh, PartialCam16Qch, PartialCam16Qmh, @@ -321,7 +687,7 @@ mod test { // Uses the example color from https://observablehq.com/@jrus/cam16 let xyz = Srgb::from(0x5588cc).into_linear().into_color_unclamped(); let cam16: Cam16 = Cam16::from_xyz(xyz, Parameters::TEST_DEFAULTS); - assert_partial_to_full!(cam16); + assert_partial_to_full!(cam16, epsilon = 0.0000000000001); } #[test] diff --git a/palette/src/cam16/partial/chromaticity.rs b/palette/src/cam16/partial/chromaticity.rs index 0e02f4909..b2823c6e8 100644 --- a/palette/src/cam16/partial/chromaticity.rs +++ b/palette/src/cam16/partial/chromaticity.rs @@ -18,11 +18,11 @@ pub trait Cam16Chromaticity { } /// One of the apparent chromatic intensity metrics of CAM16, to be used in -/// [`PartialCam16`]. +/// [`PartialCam16`](crate::cam16::PartialCam16). /// /// Combined with the hue and one of [`LuminanceType`], it can describe a -/// complete color as [`PartialCam16`]. -#[derive(Clone, Copy, Debug)] +/// complete color as [`PartialCam16`](crate::cam16::PartialCam16). +#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[non_exhaustive] pub enum ChromaticityType { /// The [chroma](https://en.wikipedia.org/wiki/Colorfulness#Chroma) (C) of a @@ -157,9 +157,28 @@ where } } +impl From> for ChromaticityType { + fn from(value: Chroma) -> Self { + Self::Chroma(value.0) + } +} + +impl From> for ChromaticityType { + fn from(value: Colorfulness) -> Self { + Self::Colorfulness(value.0) + } +} + +impl From> for ChromaticityType { + fn from(value: Saturation) -> Self { + Self::Saturation(value.0) + } +} + /// The [chroma](https://en.wikipedia.org/wiki/Colorfulness#Chroma) (C) of a -/// color, to be used in [`PartialCam16`]. +/// color, to be used in [`PartialCam16`](crate::cam16::PartialCam16). #[derive(Clone, Copy, Debug)] +#[repr(transparent)] pub struct Chroma(pub T); impl Cam16Chromaticity for Chroma { @@ -190,9 +209,18 @@ where } } +impl From for Chroma { + fn from(value: T) -> Self { + Self(value) + } +} + +impl_eq!(Chroma, [0]); + /// The [colorfulness](https://en.wikipedia.org/wiki/Colorfulness) (M) of a -/// color, to be used in [`PartialCam16`]. +/// color, to be used in [`PartialCam16`](crate::cam16::PartialCam16). #[derive(Clone, Copy, Debug)] +#[repr(transparent)] pub struct Colorfulness(pub T); impl Cam16Chromaticity for Colorfulness { @@ -223,9 +251,18 @@ where } } +impl From for Colorfulness { + fn from(value: T) -> Self { + Self(value) + } +} + +impl_eq!(Colorfulness, [0]); + /// The [saturation](https://en.wikipedia.org/wiki/Colorfulness#Saturation) (s) -/// of a color, to be used in [`PartialCam16`]. +/// of a color, to be used in [`PartialCam16`](crate::cam16::PartialCam16). #[derive(Clone, Copy, Debug)] +#[repr(transparent)] pub struct Saturation(pub T); impl Cam16Chromaticity for Saturation { @@ -255,3 +292,11 @@ where self.0.clamp_min_assign(T::zero()); } } + +impl From for Saturation { + fn from(value: T) -> Self { + Self(value) + } +} + +impl_eq!(Saturation, [0]); diff --git a/palette/src/cam16/partial/luminance.rs b/palette/src/cam16/partial/luminance.rs index 9a8148740..169e7ab73 100644 --- a/palette/src/cam16/partial/luminance.rs +++ b/palette/src/cam16/partial/luminance.rs @@ -16,8 +16,8 @@ pub trait Cam16Luminance { /// One of the apparent luminance metrics of CAM16. /// /// Combined with the hue and one of [`ChromaticityType`], it can describe a -/// complete color as [`PartialCam16`]. -#[derive(Clone, Copy, Debug)] +/// complete color as [`PartialCam16`](crate::cam16::PartialCam16). +#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[non_exhaustive] pub enum LuminanceType { /// The [lightness](https://en.wikipedia.org/wiki/Lightness) (J) of a color. @@ -109,9 +109,22 @@ where } } +impl From> for LuminanceType { + fn from(value: Lightness) -> Self { + Self::Lightness(value.0) + } +} + +impl From> for LuminanceType { + fn from(value: Brightness) -> Self { + Self::Brightness(value.0) + } +} + /// The [lightness](https://en.wikipedia.org/wiki/Lightness) (J) of a color, to -/// be used in [`PartialCam16`]. +/// be used in [`PartialCam16`](crate::cam16::PartialCam16). #[derive(Clone, Copy, Debug)] +#[repr(transparent)] pub struct Lightness(pub T); impl Cam16Luminance for Lightness { @@ -142,9 +155,18 @@ where } } +impl From for Lightness { + fn from(value: T) -> Self { + Self(value) + } +} + +impl_eq!(Lightness, [0]); + /// The [brightness](https://en.wikipedia.org/wiki/Brightness) (Q) of a color, -/// to be used in [`PartialCam16`]. +/// to be used in [`PartialCam16`](crate::cam16::PartialCam16). #[derive(Clone, Copy, Debug)] +#[repr(transparent)] pub struct Brightness(pub T); impl Cam16Luminance for Brightness { @@ -174,3 +196,11 @@ where self.0.clamp_min_assign(T::zero()); } } + +impl From for Brightness { + fn from(value: T) -> Self { + Self(value) + } +} + +impl_eq!(Brightness, [0]); diff --git a/palette/src/cam16/ucs_jab.rs b/palette/src/cam16/ucs_jab.rs index 46eca4319..9135aa1d3 100644 --- a/palette/src/cam16/ucs_jab.rs +++ b/palette/src/cam16/ucs_jab.rs @@ -220,6 +220,7 @@ impl_hyab!(Cam16UcsJab { chroma1: a, chroma2: b }); +impl_lab_color_schemes!(Cam16UcsJab[lightness]); impl_color_add!(Cam16UcsJab, [lightness, a, b]); impl_color_sub!(Cam16UcsJab, [lightness, a, b]); diff --git a/palette/src/cam16/ucs_jmh.rs b/palette/src/cam16/ucs_jmh.rs index 25ca12b31..ed2c74c79 100644 --- a/palette/src/cam16/ucs_jmh.rs +++ b/palette/src/cam16/ucs_jmh.rs @@ -260,10 +260,14 @@ impl_rand_traits_cylinder!( #[cfg(test)] mod test { + use crate::cam16::Cam16UcsJmh; + + #[cfg(feature = "approx")] + use crate::color_difference::DeltaE; + + #[cfg(all(feature = "approx", feature = "alloc"))] use crate::{ - cam16::{Cam16UcsJab, Cam16UcsJmh}, - color_difference::{DeltaE, ImprovedDeltaE}, - convert::IntoColorUnclamped, + cam16::Cam16UcsJab, color_difference::ImprovedDeltaE, convert::IntoColorUnclamped, }; #[test] @@ -286,6 +290,7 @@ mod test { raw_pixel_conversion_fail_tests!(Cam16UcsJmh<>: lightness, colorfulness, hue); #[test] + #[cfg(feature = "approx")] fn delta_e_large_hue_diff() { let lhs1 = Cam16UcsJmh::::new(50.0, 64.0, -730.0); let rhs1 = Cam16UcsJmh::new(50.0, 64.0, 730.0); @@ -302,8 +307,9 @@ mod test { // Jab and Jmh have the same delta E. #[test] + #[cfg(all(feature = "approx", feature = "alloc"))] fn jab_delta_e_equality() { - let mut jab_colors: Vec> = Vec::new(); + let mut jab_colors: Vec> = alloc::vec::Vec::new(); for j_step in 0i8..5 { for a_step in -2i8..3 { @@ -317,7 +323,7 @@ mod test { } } - let jmh_colors: Vec> = jab_colors.clone().into_color_unclamped(); + let jmh_colors: alloc::vec::Vec> = jab_colors.clone().into_color_unclamped(); for (&lhs_jab, &lhs_jmh) in jab_colors.iter().zip(&jmh_colors) { for (&rhs_jab, &rhs_jmh) in jab_colors.iter().zip(&jmh_colors) { @@ -331,8 +337,9 @@ mod test { // Jab and Jmh have the same delta E, so should also have the same improved // delta E. #[test] + #[cfg(all(feature = "approx", feature = "alloc"))] fn jab_improved_delta_e_equality() { - let mut jab_colors: Vec> = Vec::new(); + let mut jab_colors: Vec> = alloc::vec::Vec::new(); for j_step in 0i8..5 { for a_step in -2i8..3 { @@ -346,7 +353,7 @@ mod test { } } - let jmh_colors: Vec> = jab_colors.clone().into_color_unclamped(); + let jmh_colors: alloc::vec::Vec> = jab_colors.clone().into_color_unclamped(); for (&lhs_jab, &lhs_jmh) in jab_colors.iter().zip(&jmh_colors) { for (&rhs_jab, &rhs_jmh) in jab_colors.iter().zip(&jmh_colors) { diff --git a/palette/src/macros/equality.rs b/palette/src/macros/equality.rs index 5624319cd..234eacceb 100644 --- a/palette/src/macros/equality.rs +++ b/palette/src/macros/equality.rs @@ -1,8 +1,8 @@ macro_rules! impl_eq { - ( $self_ty: ident , [$element: ident]) => { + ( $self_ty: ident , [$element: tt]) => { impl_eq!($self_ty<>, [$element]); }; - ( $self_ty: ident < $($ty_param: ident),* > , [$element: ident]) => { + ( $self_ty: ident < $($ty_param: ident),* > , [$element: tt]) => { impl<$($ty_param,)* T> PartialEq for $self_ty<$($ty_param,)* T> where T: PartialEq,