diff --git a/palette/src/cam16/full.rs b/palette/src/cam16/full.rs index 8a5c1ee1f..e2130b6be 100644 --- a/palette/src/cam16/full.rs +++ b/palette/src/cam16/full.rs @@ -7,11 +7,16 @@ use crate::{ }; use super::{ - BakedParameters, Cam16Chromaticity, Cam16Luminance, PartialCam16, WhitePointParameter, + math::{ + chromaticity::{Cam16Chromaticity, ChromaticityType}, + luminance::{Cam16Luminance, LuminanceType}, + }, + BakedParameters, WhitePointParameter, }; -/// CIE CAM16 with an alpha component. See the [`Cam16a` implementation in -/// `Alpha`](crate::Alpha#Cam16a). +/// 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. @@ -31,9 +36,9 @@ pub type Cam16a = Alpha, T>; /// are there to bridge the gap. /// /// Not all attributes are used when converting _from_ CAM16, since they are -/// 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. +/// correlated and derived from each other. This library also provides partial +/// versions of this struct, to make it easier to correctly specify a minimum +/// attribute set. #[derive(Clone, Copy, Debug, WithAlpha, Default)] #[palette(palette_internal, component = "T")] #[repr(C)] @@ -82,9 +87,9 @@ impl Cam16 { /// 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 - /// [`PartialCam16`] for more control over which set of attributes that - /// should be. + /// only some of them are actually used. You may want to use one of the + /// partial CAM16 representations for more control over which set of + /// attributes that should be. #[inline] pub fn into_xyz( self, @@ -107,35 +112,12 @@ impl Cam16 { + Clone, T::Mask: LazySelect>, { - super::math::cam16_to_xyz(self.into(), parameters.into().inner).with_white_point() - } - - /// Reconstruct a full set of CIE CAM16 attributes, using the original viewing conditions. - #[inline] - pub fn from_partial( - partial: PartialCam16, - parameters: impl Into>, - ) -> Self - where - WpParam: WhitePointParameter, - T: Real + Zero + Arithmetics + Sqrt + PartialCmp + Clone, - T::Mask: LazySelect + Clone, - L: Cam16Luminance, - C: Cam16Chromaticity, - { - partial.into_full(parameters) - } - - /// Create a partial set of CIE CAM16 attributes. - /// - /// It's also possible to use `PartialCam16::from` or `Cam16::into`. - #[inline] - pub fn into_partial(self) -> PartialCam16 - where - L: Cam16Luminance, - C: Cam16Chromaticity, - { - PartialCam16::from_full(self) + let partial = ( + LuminanceType::from_cam16(self.lightness, self.brightness), + ChromaticityType::from_cam16(self.chroma, self.colorfulness, self.saturation), + self.hue, + ); + super::math::cam16_to_xyz(partial, parameters.into().inner).with_white_point() } } @@ -164,9 +146,9 @@ impl Alpha, A> { /// 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. + /// only some of them are actually used. You may want to use one of the + /// partial CAM16 representations for more control over which set of + /// attributes that should be. #[inline] pub fn into_xyz( self, @@ -196,45 +178,6 @@ impl Alpha, A> { 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 @@ -291,7 +234,10 @@ impl_eq_hue!( #[cfg(feature = "approx")] mod test { use crate::{ - cam16::{ChromaticityType, LuminanceType, Parameters, PartialCam16}, + cam16::{ + math::chromaticity::ChromaticityType, math::luminance::LuminanceType, BakedParameters, + Parameters, + }, convert::{FromColorUnclamped, IntoColorUnclamped}, Srgb, }; @@ -301,7 +247,7 @@ mod test { macro_rules! assert_cam16_to_rgb { ($cam16:expr, $rgb:expr, $($params:tt)*) => { let cam16 = $cam16; - let parameters = Parameters::TEST_DEFAULTS; + let parameters = BakedParameters::from(Parameters::TEST_DEFAULTS); let rgb: Srgb = cam16.into_xyz(parameters).into_color_unclamped(); assert_relative_eq!(rgb, $rgb, $($params)*); @@ -318,13 +264,16 @@ mod test { for luminance in luminances { for chromaticity in chromaticities { - let partial = PartialCam16 { - hue: cam16.hue, - chromaticity, + let partial = ( luminance, - }; + chromaticity, + cam16.hue, + ); + + let xyz = crate::cam16::math::cam16_to_xyz(partial, parameters.inner).with_white_point(); + assert_relative_eq!( - Srgb::::from_color_unclamped(dbg!(partial).into_xyz(parameters)), + Srgb::::from_color_unclamped(xyz), $rgb, $($params)* ); diff --git a/palette/src/cam16/math.rs b/palette/src/cam16/math.rs index 9b817a19b..2f620e2b7 100644 --- a/palette/src/cam16/math.rs +++ b/palette/src/cam16/math.rs @@ -6,7 +6,6 @@ use core::{ use crate::{ angle::{RealAngle, SignedAngle}, bool_mask::LazySelect, - cam16::LuminanceType, clamp, hues::Cam16Hue, num::{ @@ -16,7 +15,12 @@ use crate::{ xyz::Xyz, }; -use super::{Cam16, ChromaticityType, DynPartialCam16, Parameters}; +use super::{Cam16, Parameters}; + +use self::{chromaticity::ChromaticityType, luminance::LuminanceType}; + +pub(crate) mod chromaticity; +pub(crate) mod luminance; // This module is originally based on https://observablehq.com/@jrus/cam16. // https://rawpedia.rawtherapee.com/CIECAM02 is also informative. @@ -113,7 +117,7 @@ where #[inline] pub(crate) fn cam16_to_xyz( - cam16: DynPartialCam16, + cam16: (LuminanceType, ChromaticityType, Cam16Hue), parameters: DependentParameters, ) -> Xyz where @@ -133,7 +137,7 @@ where T::Mask: LazySelect>, { // Weird naming, but we just want to know if it's black or not here. - let is_black = match &cam16.luminance { + let is_black = match &cam16.0 { LuminanceType::Lightness(lightness) => lightness.eq(&T::zero()), LuminanceType::Brightness(brightness) => brightness.eq(&T::zero()), }; @@ -146,7 +150,7 @@ where // Assumes that lightness has been checked to be non-zero in `cam16_to_xyz`. fn non_black_cam16_to_xyz( - cam16: DynPartialCam16, + cam16: (LuminanceType, ChromaticityType, Cam16Hue), parameters: DependentParameters, ) -> Xyz where @@ -162,9 +166,9 @@ where + SignedAngle + Clone, { - let h_rad = cam16.hue.into_radians(); + let h_rad = cam16.2.into_radians(); let (sin_h, cos_h) = h_rad.clone().sin_cos(); - let j_root = match cam16.luminance { + let j_root = match cam16.0 { LuminanceType::Lightness(j) => lightness_to_j_root(j), LuminanceType::Brightness(q) => brightness_to_j_root( q, @@ -173,7 +177,7 @@ where parameters.f_l_4.clone(), ), }; - let alpha = match cam16.chromaticity { + let alpha = match cam16.1 { ChromaticityType::Chroma(c) => c / &j_root, ChromaticityType::Colorfulness(m) => colorfulness_to_chroma(m, parameters.f_l_4) / &j_root, ChromaticityType::Saturation(s) => { diff --git a/palette/src/cam16/partial/chromaticity.rs b/palette/src/cam16/math/chromaticity.rs similarity index 92% rename from palette/src/cam16/partial/chromaticity.rs rename to palette/src/cam16/math/chromaticity.rs index b2823c6e8..95fccc32a 100644 --- a/palette/src/cam16/partial/chromaticity.rs +++ b/palette/src/cam16/math/chromaticity.rs @@ -9,7 +9,7 @@ use crate::{ /// Common methods for types representing apparent chromatic intensity metrics /// of CAM16. -pub trait Cam16Chromaticity { +pub(crate) trait Cam16Chromaticity { /// Create `Self` from a CAM16 chromaticity attribute. fn from_cam16(chroma: T, colorfulness: T, saturation: T) -> Self; @@ -17,14 +17,10 @@ pub trait Cam16Chromaticity { fn into_dynamic(self) -> ChromaticityType; } -/// One of the apparent chromatic intensity metrics of CAM16, to be used in -/// [`PartialCam16`](crate::cam16::PartialCam16). -/// -/// Combined with the hue and one of [`LuminanceType`], it can describe a -/// complete color as [`PartialCam16`](crate::cam16::PartialCam16). +/// One the apparent chromatic intensity metrics of CAM16. #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[non_exhaustive] -pub enum ChromaticityType { +pub(crate) enum ChromaticityType { /// The [chroma](https://en.wikipedia.org/wiki/Colorfulness#Chroma) (C) of a /// color. #[doc(alias = "C")] @@ -176,10 +172,10 @@ impl From> for ChromaticityType { } /// The [chroma](https://en.wikipedia.org/wiki/Colorfulness#Chroma) (C) of a -/// color, to be used in [`PartialCam16`](crate::cam16::PartialCam16). +/// color. #[derive(Clone, Copy, Debug)] #[repr(transparent)] -pub struct Chroma(pub T); +pub(crate) struct Chroma(pub T); impl Cam16Chromaticity for Chroma { fn from_cam16(chroma: T, _colorfulness: T, _saturation: T) -> Self { @@ -218,10 +214,10 @@ impl From for Chroma { impl_eq!(Chroma, [0]); /// The [colorfulness](https://en.wikipedia.org/wiki/Colorfulness) (M) of a -/// color, to be used in [`PartialCam16`](crate::cam16::PartialCam16). +/// color. #[derive(Clone, Copy, Debug)] #[repr(transparent)] -pub struct Colorfulness(pub T); +pub(crate) struct Colorfulness(pub T); impl Cam16Chromaticity for Colorfulness { fn from_cam16(_chroma: T, colorfulness: T, _saturation: T) -> Self { @@ -260,10 +256,10 @@ impl From for Colorfulness { impl_eq!(Colorfulness, [0]); /// The [saturation](https://en.wikipedia.org/wiki/Colorfulness#Saturation) (s) -/// of a color, to be used in [`PartialCam16`](crate::cam16::PartialCam16). +/// of a color. #[derive(Clone, Copy, Debug)] #[repr(transparent)] -pub struct Saturation(pub T); +pub(crate) struct Saturation(pub T); impl Cam16Chromaticity for Saturation { fn from_cam16(_chroma: T, _colorfulness: T, saturation: T) -> Self { diff --git a/palette/src/cam16/partial/luminance.rs b/palette/src/cam16/math/luminance.rs similarity index 91% rename from palette/src/cam16/partial/luminance.rs rename to palette/src/cam16/math/luminance.rs index 169e7ab73..313d701de 100644 --- a/palette/src/cam16/partial/luminance.rs +++ b/palette/src/cam16/math/luminance.rs @@ -5,7 +5,7 @@ use crate::{ }; /// Common methods for types representing apparent luminance metrics of CAM16. -pub trait Cam16Luminance { +pub(crate) trait Cam16Luminance { /// Create `Self` from a CAM16 luminance attribute. fn from_cam16(lightness: T, brightness: T) -> Self; @@ -13,13 +13,10 @@ pub trait Cam16Luminance { fn into_dynamic(self) -> LuminanceType; } -/// One of the apparent luminance metrics of CAM16. -/// -/// Combined with the hue and one of [`ChromaticityType`], it can describe a -/// complete color as [`PartialCam16`](crate::cam16::PartialCam16). +/// One the apparent luminance metrics of CAM16. #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[non_exhaustive] -pub enum LuminanceType { +pub(crate) enum LuminanceType { /// The [lightness](https://en.wikipedia.org/wiki/Lightness) (J) of a color. #[doc(alias = "J")] Lightness(T), @@ -121,11 +118,10 @@ impl From> for LuminanceType { } } -/// The [lightness](https://en.wikipedia.org/wiki/Lightness) (J) of a color, to -/// be used in [`PartialCam16`](crate::cam16::PartialCam16). +/// The [lightness](https://en.wikipedia.org/wiki/Lightness) (J) of a color. #[derive(Clone, Copy, Debug)] #[repr(transparent)] -pub struct Lightness(pub T); +pub(crate) struct Lightness(pub T); impl Cam16Luminance for Lightness { fn from_cam16(lightness: T, _brightness: T) -> Self { @@ -163,11 +159,10 @@ impl From for Lightness { impl_eq!(Lightness, [0]); -/// The [brightness](https://en.wikipedia.org/wiki/Brightness) (Q) of a color, -/// to be used in [`PartialCam16`](crate::cam16::PartialCam16). +/// The [brightness](https://en.wikipedia.org/wiki/Brightness) (Q) of a color. #[derive(Clone, Copy, Debug)] #[repr(transparent)] -pub struct Brightness(pub T); +pub(crate) struct Brightness(pub T); impl Cam16Luminance for Brightness { fn from_cam16(_lightness: T, brightness: T) -> Self { diff --git a/palette/src/cam16/partial.rs b/palette/src/cam16/partial.rs index 27b79e1a9..a23f69362 100644 --- a/palette/src/cam16/partial.rs +++ b/palette/src/cam16/partial.rs @@ -1,425 +1,484 @@ use crate::{ - angle::{RealAngle, SignedAngle}, - bool_mask::LazySelect, + cam16::Cam16UcsJmh, convert::FromColorUnclamped, - hues::Cam16Hue, - num::{ - Abs, Arithmetics, ClampAssign, Exp, One, PartialCmp, Powf, Real, Signum, Sqrt, - Trigonometry, Zero, - }, - white_point, Alpha, Xyz, + num::{Arithmetics, Exp, One, Real}, + Alpha, }; -use super::{BakedParameters, Cam16, Cam16UcsJmh, WhitePointParameter}; - -pub use chromaticity::*; -pub use luminance::*; - -mod chromaticity; -mod luminance; - -/// An alias for [`PartialCam16`], where the chromaticity and luminance -/// attributes are decided during runtime. -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, 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, 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, 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, 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, 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, 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. -/// -/// It's likely preferred to use one of its aliases: -/// -/// * [`DynPartialCam16`]: dynamic attributes. -/// * [`PartialCam16Jch`]: lightness and chroma. -/// * [`PartialCam16Jmh`]: lightness and colorfulness. -/// * [`PartialCam16Jsh`]: lightness and saturation. -/// * [`PartialCam16Qch`]: brightness and chroma. -/// * [`PartialCam16Qmh`]: brightness and colorfulness. -/// * [`PartialCam16Qsh`]: brightness and saturation. -/// -/// This is enough information for converting CAM16 to other color spaces. -#[derive(Clone, Copy, Debug, WithAlpha, FromColorUnclamped)] -#[palette( - palette_internal, - component = "T", - color_group = "cam16", - skip_derives(Cam16, PartialCam16, Cam16UcsJmh) -)] -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, - - /// The [chroma](https://en.wikipedia.org/wiki/Colorfulness#Chroma) (C), - /// [colorfulness](https://en.wikipedia.org/wiki/Colorfulness) (M), or - /// [saturation](https://en.wikipedia.org/wiki/Colorfulness#Saturation) (s) - /// of the color. - pub chromaticity: C, - - /// The [hue](https://en.wikipedia.org/wiki/Hue) (h) of the color. - pub hue: Cam16Hue, -} - -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, -{ - /// Derive partial CIE CAM16 attributes for the provided color, under the provided - /// viewing conditions. - #[inline] - pub fn from_xyz( - color: Xyz, - parameters: impl Into>, - ) -> Self - where - WpParam: WhitePointParameter, - T: Real + Arithmetics + Powf + Sqrt + Abs + Signum + Trigonometry + RealAngle + Clone, - { - super::math::xyz_to_cam16(color.with_white_point(), parameters.into().inner).into() - } - - /// Construct an XYZ color from these CIE CAM16 attributes, under the - /// provided viewing conditions. - #[inline] - pub fn into_xyz( - self, - parameters: impl Into>, - ) -> Xyz - where - WpParam: WhitePointParameter, - T: Real - + One - + Zero - + Sqrt - + Powf - + Abs - + Signum - + Arithmetics - + Trigonometry - + RealAngle - + SignedAngle - + PartialCmp - + Clone, - T::Mask: LazySelect>, - { - super::math::cam16_to_xyz(self.into_dynamic(), parameters.into().inner).with_white_point() - } - - /// Create a partial set of CIE CAM16 attributes. - /// - /// It's also possible to use `PartialCam16::from` or `Cam16::into`. - #[inline] - pub fn from_full(full: Cam16) -> Self { - let Cam16 { - lightness, - chroma, - hue, - brightness, - colorfulness, - saturation, - } = full; - - PartialCam16 { - hue, - chromaticity: C::from_cam16(chroma, colorfulness, saturation), - luminance: L::from_cam16(lightness, brightness), - } - } - - /// Reconstruct a full set of CIE CAM16 attributes, using the original viewing conditions. - #[inline] - pub fn into_full(self, parameters: impl Into>) -> Cam16 - where - WpParam: WhitePointParameter, - T: Real + Zero + Arithmetics + Sqrt + PartialCmp + Clone, - T::Mask: LazySelect + Clone, - { - let parameters = parameters.into(); - let PartialCam16 { - hue, - chromaticity, - luminance, - } = self.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, +macro_rules! make_partial_cam16 { + ( + $(#[$type_meta: meta])* + $module: ident :: $name: ident { + $(#[$luminance_meta: meta])+ + $luminance: ident : $luminance_ty: ident, + $(#[$chromaticity_meta: meta])+ + $chromaticity: ident : $chromaticity_ty: ident } - } - - /// Turn the chromaticity and luminance into dynamically decided attributes. - pub fn into_dynamic(self) -> DynPartialCam16 { - let PartialCam16 { - hue, - chromaticity, - luminance, - } = self; - - PartialCam16 { - hue, - chromaticity: chromaticity.into_dynamic(), - luminance: luminance.into_dynamic(), + ) => { + pub use $module::$name; + + #[doc = concat!("Partial CIE CAM16, with ", stringify!($luminance), " and ", stringify!($chromaticity), ", and helper types.")] + pub mod $module { + use crate::{ + angle::{RealAngle, SignedAngle}, + bool_mask::{HasBoolMask, LazySelect}, + cam16::{BakedParameters, Cam16, WhitePointParameter}, + convert::FromColorUnclamped, + hues::{Cam16Hue, Cam16HueIter}, + num::{Abs, Arithmetics, One, PartialCmp, Powf, Real, Signum, Sqrt, Trigonometry, Zero}, + white_point, Alpha, Xyz, + }; + + use crate::cam16::math::chromaticity::*; + use crate::cam16::math::luminance::*; + + #[doc = concat!("Partial CIE CAM16, with ", stringify!($luminance), " and ", stringify!($chromaticity), ".")] + /// + /// It contains enough information for converting CAM16 to other + /// color spaces. + /// + /// The full list of partial CAM16 variants is: + /// + /// * [`Cam16Jch`](crate::cam16::Cam16Jch): lightness and chroma. + /// * [`Cam16Jmh`](crate::cam16::Cam16Jmh): lightness and + /// colorfulness. + /// * [`Cam16Jsh`](crate::cam16::Cam16Jsh): lightness and + /// saturation. + /// * [`Cam16Qch`](crate::cam16::Cam16Qch): brightness and chroma. + /// * [`Cam16Qmh`](crate::cam16::Cam16Qmh): brightness and + /// colorfulness. + /// * [`Cam16Qsh`](crate::cam16::Cam16Qsh): brightness and + /// saturation. + #[derive(Clone, Copy, Debug, Default, ArrayCast, WithAlpha, FromColorUnclamped)] + #[palette( + palette_internal, + component = "T", + color_group = "cam16", + skip_derives(Cam16, $name) + )] + $(#[$type_meta])* + #[repr(C)] + pub struct $name { + $(#[$luminance_meta])+ + pub $luminance: T, + + $(#[$chromaticity_meta])+ + pub $chromaticity: T, + + /// The [hue](https://en.wikipedia.org/wiki/Hue) (h) of the color. + #[palette(unsafe_same_layout_as = "T")] + pub hue: Cam16Hue, + } + + impl $name { + /// Create a partial CIE CAM16 color. + pub fn new($luminance: T, $chromaticity: T, hue: H) -> Self + where + H: Into>, + { + Self::new_const($luminance.into(), $chromaticity.into(), hue.into()) + } + + #[doc = concat!("Create a partial CIE CAM16 color. This is the same as `", stringify!($name), "::new`")] + /// without the generic hue type. It's temporary until `const fn` + /// supports traits. + pub const fn new_const($luminance: T, $chromaticity: T, hue: Cam16Hue) -> Self { + Self { + $luminance, + $chromaticity, + hue, + } + } + + #[doc = concat!("Convert to a `(", stringify!($luminance), ", ", stringify!($chromaticity), ", hue)` tuple.")] + pub fn into_components(self) -> (T, T, Cam16Hue) { + (self.$luminance, self.$chromaticity, self.hue) + } + + #[doc = concat!("Convert from a `(", stringify!($luminance), ", ", stringify!($chromaticity), ", hue)` tuple.")] + pub fn from_components(($luminance, $chromaticity, hue): (T, T, H)) -> Self + where + H: Into>, + { + Self::new($luminance, $chromaticity, hue) + } + + /// Derive partial CIE CAM16 attributes for the provided color, under the provided + /// viewing conditions. + #[inline] + pub fn from_xyz( + color: Xyz, + parameters: impl Into>, + ) -> Self + where + WpParam: WhitePointParameter, + T: Real + Arithmetics + Powf + Sqrt + Abs + Signum + Trigonometry + RealAngle + Clone, + { + crate::cam16::math::xyz_to_cam16(color.with_white_point(), parameters.into().inner).into() + } + + /// Construct an XYZ color from these CIE CAM16 attributes, under the + /// provided viewing conditions. + #[inline] + pub fn into_xyz( + self, + parameters: impl Into>, + ) -> Xyz + where + WpParam: WhitePointParameter, + T: Real + + One + + Zero + + Sqrt + + Powf + + Abs + + Signum + + Arithmetics + + Trigonometry + + RealAngle + + SignedAngle + + PartialCmp + + Clone, + T::Mask: LazySelect>, + { + crate::cam16::math::cam16_to_xyz(self.into_dynamic(), parameters.into().inner).with_white_point() + } + + /// Create a partial set of CIE CAM16 attributes. + /// + #[doc = concat!("It's also possible to use `", stringify!($name), "::from` or `Cam16::into`.")] + #[inline] + pub fn from_full(full: Cam16) -> Self { + Self { + hue: full.hue, + $chromaticity: full.$chromaticity, + $luminance: full.$luminance, + } + } + + /// Reconstruct a full set of CIE CAM16 attributes, using the original viewing conditions. + #[inline] + pub fn into_full(self, parameters: impl Into>) -> Cam16 + where + WpParam: WhitePointParameter, + T: Real + Zero + Arithmetics + Sqrt + PartialCmp + Clone, + T::Mask: LazySelect + Clone, + { + let parameters = parameters.into(); + let ( + luminance, + chromaticity, + hue, + ) = self.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, + } + } + + // Turn the chromaticity and luminance into dynamically decided + // attributes, to help conversion to a full set of attributes. + #[inline(always)] + fn into_dynamic(self) -> (LuminanceType, ChromaticityType, Cam16Hue) { + ( + $luminance_ty(self.$luminance).into_dynamic(), + $chromaticity_ty(self.$chromaticity).into_dynamic(), + self.hue, + ) + } + } + + #[doc = concat!(r#""[`"#, stringify!($name), "a`](crate::cam16::", stringify!($name), "a)")] + ///implementations. + impl Alpha<$name, A> { + /// Create a partial CIE CAM16 color with transparency. + pub fn new>>($luminance: T, $chromaticity: T, hue: H, alpha: A) -> Self{ + Self::new_const($luminance.into(), $chromaticity.into(), hue.into(), alpha) + } + + /// Create a partial CIE CAM16 color with transparency. This is the + #[doc = concat!("same as `", stringify!($name), "::new` without the generic hue type. It's temporary until")] + /// `const fn` supports traits. + pub const fn new_const($luminance: T, $chromaticity: T, hue: Cam16Hue, alpha: A) -> Self { + Alpha { + color: $name::new_const($luminance, $chromaticity, hue), + alpha, + } + } + + #[doc = concat!("Convert to a `(", stringify!($luminance), ", ", stringify!($chromaticity), ", hue, alpha)` tuple.")] + pub fn into_components(self) -> (T, T, Cam16Hue, A) { + ( + self.color.$luminance, + self.color.$chromaticity, + self.color.hue, + self.alpha, + ) + } + + #[doc = concat!("Convert from a `(", stringify!($luminance), ", ", stringify!($chromaticity), ", hue, alpha)` tuple.")] + pub fn from_components>>( + ($luminance, $chromaticity, hue, alpha): (T, T, H, A), + ) -> Self { + Self::new($luminance, $chromaticity, hue, alpha) + } + + /// 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: $name::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. + /// + #[doc = concat!("It's also possible to use `", stringify!($name), "a::from` or `Cam16a::into`.")] + #[inline] + pub fn from_full(full: Alpha, A>) -> Self { + let Alpha { color, alpha } = full; + + Alpha { + color: $name::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, + } + } + } + + impl FromColorUnclamped for $name { + fn from_color_unclamped(val: Self) -> Self { + val + } + } + + impl FromColorUnclamped> for $name { + fn from_color_unclamped(val: Cam16) -> Self { + Self::from_full(val) + } + } + + impl From> for $name + { + fn from(value: Cam16) -> Self { + Self::from_full(value) + } + } + + impl HasBoolMask for $name + where + T: HasBoolMask, + { + type Mask = T::Mask; + } + + #[cfg(feature = "bytemuck")] + unsafe impl bytemuck::Zeroable for $name where T: bytemuck::Zeroable {} + + #[cfg(feature = "bytemuck")] + unsafe impl bytemuck::Pod for $name where T: bytemuck::Pod {} + + impl_reference_component_methods_hue!($name, [$luminance, $chromaticity]); + impl_struct_of_arrays_methods_hue!($name, [$luminance, $chromaticity]); + + impl_tuple_conversion_hue!($name as (T, T, H), Cam16Hue); + + impl_is_within_bounds! { + $name { + $luminance => [T::zero(), None], + $chromaticity => [T::zero(), None] + } + where T: Zero + } + impl_clamp! { + $name { + $luminance => [T::zero()], + $chromaticity => [T::zero()] + } + other {hue} + where T: Zero + } + + impl_mix_hue!($name {$luminance, $chromaticity}); + impl_hue_ops!($name, Cam16Hue); + + impl_color_add!($name, [$luminance, $chromaticity, hue]); + impl_color_sub!($name, [$luminance, $chromaticity, hue]); + + impl_array_casts!($name, [T; 3]); + impl_simd_array_conversion_hue!($name, [$luminance, $chromaticity]); + impl_struct_of_array_traits_hue!($name, Cam16HueIter, [$luminance, $chromaticity]); + + impl_eq_hue!($name, Cam16Hue, [$luminance, $chromaticity, hue]); } - } + }; } -///[`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) - } +/// Partial CIE CAM16 with lightness, chroma, and an alpha component. +/// +/// See the [`Cam16Jcha` implementation in `Alpha`](crate::Alpha#Cam16Jcha). +pub type Cam16Jcha = Alpha, T>; +make_partial_cam16! { + cam16_jch::Cam16Jch { + /// The [lightness](https://en.wikipedia.org/wiki/Lightness) (J) of the + /// color. + lightness: Lightness, + + /// The [chroma](https://en.wikipedia.org/wiki/Colorfulness#Chroma) (C) + /// of the color. + chroma: Chroma} } -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, - } - } +/// Partial CIE CAM16 with lightness, colorfulness, and an alpha component. +/// +/// See the [`Cam16Jmha` implementation in `Alpha`](crate::Alpha#Cam16Jmha). +pub type Cam16Jmha = Alpha, T>; +make_partial_cam16! { + #[palette(skip_derives(Cam16UcsJmh))] + cam16_jmh::Cam16Jmh { + /// The [lightness](https://en.wikipedia.org/wiki/Lightness) (J) of the + /// color. + lightness: Lightness, + + /// The [colorfulness](https://en.wikipedia.org/wiki/Colorfulness) (M) + /// of the color. + colorfulness: Colorfulness} +} - /// Turn the chromaticity and luminance into dynamically decided attributes. - pub fn into_dynamic(self) -> Alpha, A> { - let Alpha { color, alpha } = self; +/// Partial CIE CAM16 with lightness, saturation, and an alpha component. +/// +/// See the [`Cam16Jsha` implementation in `Alpha`](crate::Alpha#Cam16Jsha). +pub type Cam16Jsha = Alpha, T>; +make_partial_cam16! { + cam16_jsh::Cam16Jsh { + /// The [lightness](https://en.wikipedia.org/wiki/Lightness) (J) of the + /// color. + lightness: Lightness, + + /// The + /// [saturation](https://en.wikipedia.org/wiki/Colorfulness#Saturation) + /// (s) of the color. + saturation: Saturation} +} - Alpha { - color: color.into_dynamic(), - alpha, - } - } +/// Partial CIE CAM16 with brightness, chroma, and an alpha component. +/// +/// See the [`Cam16Qcha` implementation in `Alpha`](crate::Alpha#Cam16Qcha). +pub type Cam16Qcha = Alpha, T>; +make_partial_cam16! { + cam16_qch::Cam16Qch { + /// The [brightness](https://en.wikipedia.org/wiki/Brightness) (Q) of + /// the color. + brightness: Brightness, + + /// The [chroma](https://en.wikipedia.org/wiki/Colorfulness#Chroma) (C) + /// of the color. + chroma: Chroma} } -impl FromColorUnclamped> for PartialCam16 { - fn from_color_unclamped(val: PartialCam16) -> Self { - val - } +/// Partial CIE CAM16 with brightness, colorfulness, and an alpha component. +/// +/// See the [`Cam16Qmha` implementation in `Alpha`](crate::Alpha#Cam16Qmha). +pub type Cam16Qmha = Alpha, T>; +make_partial_cam16! { + cam16_qmh::Cam16Qmh { + /// The [brightness](https://en.wikipedia.org/wiki/Brightness) (Q) of + /// the color. + brightness: Brightness, + + /// The [colorfulness](https://en.wikipedia.org/wiki/Colorfulness) (M) + /// of the color. + colorfulness: Colorfulness} } -impl FromColorUnclamped> for PartialCam16 -where - L: Cam16Luminance, - C: Cam16Chromaticity, -{ - fn from_color_unclamped(val: Cam16) -> Self { - Self::from_full(val) - } +/// Partial CIE CAM16 with brightness, saturation, and an alpha component. +/// +/// See the [`Cam16Qsha` implementation in `Alpha`](crate::Alpha#Cam16Qsha). +pub type Cam16Qsha = Alpha, T>; +make_partial_cam16! { + cam16_qsh::Cam16Qsh { + /// The [brightness](https://en.wikipedia.org/wiki/Brightness) (Q) of + /// the color. + brightness: Brightness, + + /// The + /// [saturation](https://en.wikipedia.org/wiki/Colorfulness#Saturation) + /// (s) of the color. + saturation: Saturation} } -impl FromColorUnclamped> for PartialCam16Jmh +impl FromColorUnclamped> for Cam16Jmh where T: Real + One + Exp + Arithmetics + Clone, { @@ -431,213 +490,16 @@ where Self { hue: val.hue, - chromaticity: Colorfulness(colorfulness), - luminance: Lightness(lightness), - } - } -} - -impl FromColorUnclamped> for DynPartialCam16 -where - PartialCam16Jmh: FromColorUnclamped>, -{ - fn from_color_unclamped(val: Cam16UcsJmh) -> Self { - PartialCam16Jmh::from_color_unclamped(val).into_dynamic() - } -} - -impl crate::Clamp for PartialCam16 -where - C: crate::Clamp, - L: crate::Clamp, -{ - fn clamp(self) -> Self { - Self { - hue: self.hue, - chromaticity: self.chromaticity.clamp(), - luminance: self.luminance.clamp(), + colorfulness, + lightness, } } } -impl crate::ClampAssign for PartialCam16 -where - T: ClampAssign + Zero, - C: crate::ClampAssign, - L: crate::ClampAssign, -{ - fn clamp_assign(&mut self) { - self.chromaticity.clamp_assign(); - self.luminance.clamp_assign(); - } -} - -impl From> for PartialCam16 -where - L: Cam16Luminance, - C: Cam16Chromaticity, -{ - fn from(value: Cam16) -> Self { - Self::from_full(value) - } -} - -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, - PartialCam16Qsh, - }; + use super::{Cam16Jch, Cam16Jmh, Cam16Jsh, Cam16Qch, Cam16Qmh, Cam16Qsh}; use crate::{ cam16::{Cam16, Parameters, ParametersStaticWp}, convert::IntoColorUnclamped, @@ -649,33 +511,33 @@ mod test { ($cam16: expr) => {assert_partial_to_full!($cam16,)}; ($cam16: expr, $($params:tt)*) => { assert_relative_eq!( - PartialCam16Jch::from($cam16).into_full(ParametersStaticWp::::TEST_DEFAULTS), + Cam16Jch::from($cam16).into_full(ParametersStaticWp::::TEST_DEFAULTS), $cam16, $($params)* ); assert_relative_eq!( - PartialCam16Jmh::from($cam16).into_full(ParametersStaticWp::::TEST_DEFAULTS), + Cam16Jmh::from($cam16).into_full(ParametersStaticWp::::TEST_DEFAULTS), $cam16, $($params)* ); assert_relative_eq!( - PartialCam16Jsh::from($cam16).into_full(ParametersStaticWp::::TEST_DEFAULTS), + Cam16Jsh::from($cam16).into_full(ParametersStaticWp::::TEST_DEFAULTS), $cam16, $($params)* ); assert_relative_eq!( - PartialCam16Qch::from($cam16).into_full(ParametersStaticWp::::TEST_DEFAULTS), + Cam16Qch::from($cam16).into_full(ParametersStaticWp::::TEST_DEFAULTS), $cam16, $($params)* ); assert_relative_eq!( - PartialCam16Qmh::from($cam16).into_full(ParametersStaticWp::::TEST_DEFAULTS), + Cam16Qmh::from($cam16).into_full(ParametersStaticWp::::TEST_DEFAULTS), $cam16, $($params)* ); assert_relative_eq!( - PartialCam16Qsh::from($cam16).into_full(ParametersStaticWp::::TEST_DEFAULTS), + Cam16Qsh::from($cam16).into_full(ParametersStaticWp::::TEST_DEFAULTS), $cam16, $($params)* ); diff --git a/palette/src/cam16/ucs_jab.rs b/palette/src/cam16/ucs_jab.rs index 9135aa1d3..2428ff1a8 100644 --- a/palette/src/cam16/ucs_jab.rs +++ b/palette/src/cam16/ucs_jab.rs @@ -11,8 +11,10 @@ use crate::{ use super::Cam16UcsJmh; -/// Cartesian CAM16-UCS with an alpha component. See the [`Cam16UcsJaba` -/// implementation in `Alpha`](crate::Alpha#Cam16UcsJaba). +/// Cartesian CAM16-UCS with an alpha component. +/// +/// See the [`Cam16UcsJaba` implementation in +/// `Alpha`](crate::Alpha#Cam16UcsJaba). pub type Cam16UcsJaba = Alpha, T>; /// The Cartesian form of CAM16-UCS, or J'a'b'. diff --git a/palette/src/cam16/ucs_jmh.rs b/palette/src/cam16/ucs_jmh.rs index ed2c74c79..5f8115c72 100644 --- a/palette/src/cam16/ucs_jmh.rs +++ b/palette/src/cam16/ucs_jmh.rs @@ -8,10 +8,12 @@ use crate::{ Alpha, }; -use super::{Cam16UcsJab, Colorfulness, Lightness, PartialCam16Jmh}; +use super::{Cam16Jmh, Cam16UcsJab}; -/// Polar CAM16-UCS with an alpha component. See the [`Cam16UcsJmha` -/// implementation in `Alpha`](crate::Alpha#Cam16UcsJmha). +/// Polar CAM16-UCS with an alpha component. +/// +/// See the [`Cam16UcsJmha` implementation in +/// `Alpha`](crate::Alpha#Cam16UcsJmha). pub type Cam16UcsJmha = Alpha, T>; /// The polar form of CAM16-UCS, or J'M'h'. @@ -20,10 +22,8 @@ pub type Cam16UcsJmha = Alpha, T>; #[palette( palette_internal, component = "T", - cam16_chromaticity = "Colorfulness", - cam16_luminance = "Lightness", color_group = "cam16", - skip_derives(PartialCam16, Cam16UcsJmh, Cam16UcsJab) + skip_derives(Cam16Jmh, Cam16UcsJmh, Cam16UcsJab) )] #[repr(C)] pub struct Cam16UcsJmh { @@ -139,15 +139,15 @@ impl FromColorUnclamped> for Cam16UcsJmh { } } -impl FromColorUnclamped> for Cam16UcsJmh +impl FromColorUnclamped> for Cam16UcsJmh where T: Real + One + Ln + Arithmetics, { - fn from_color_unclamped(val: PartialCam16Jmh) -> Self { + fn from_color_unclamped(val: Cam16Jmh) -> Self { let colorfulness = - (T::one() + T::from_f64(0.0228) * val.chromaticity.0).ln() / T::from_f64(0.0228); + (T::one() + T::from_f64(0.0228) * val.colorfulness).ln() / T::from_f64(0.0228); let lightness = - T::from_f64(1.7) * &val.luminance.0 / (T::one() + T::from_f64(0.007) * val.luminance.0); + T::from_f64(1.7) * &val.lightness / (T::one() + T::from_f64(0.007) * val.lightness); Cam16UcsJmh { lightness, diff --git a/palette/src/convert.rs b/palette/src/convert.rs index a243d750d..0bd8c8415 100644 --- a/palette/src/convert.rs +++ b/palette/src/convert.rs @@ -159,14 +159,6 @@ //! other than `Xyz`. The current groups are `"base"` (default) and `"cam16"`. //! Specify this before `skip_derives`. //! -//! * `cam16_chromaticity = "some::cam16_chromaticity::Type`: Sets the -//! chromaticity type to use when converting to and from partial CIE CAM16. -//! This is only relevant if the color is part of the `cam16` group. -//! -//! * `cam16_luminance = "some::cam16_luminance::Type`: Sets the luminance type -//! to use when converting to and from partial CIE CAM16. This is only -//! relevant if the color is part of the `cam16` group. -//! //! ### Field Attributes //! //! * `alpha`: Specifies field as the color's transparency value. diff --git a/palette_derive/src/color_types.rs b/palette_derive/src/color_types.rs index 9fa1d6dc7..dadfb52ce 100644 --- a/palette_derive/src/color_types.rs +++ b/palette_derive/src/color_types.rs @@ -1,17 +1,17 @@ +use proc_macro2::{Span, TokenStream}; +use syn::{parse_quote, GenericParam, Generics, Ident, Type}; + +use crate::{ + convert::util::{InputUser, UsedInput, WhitePointSource}, + meta::TypeItemAttributes, + util, +}; + pub(crate) struct ColorGroup { pub(crate) root_type: ColorInfo, pub(crate) colors: &'static [ColorType], } -pub(crate) struct ColorType { - pub(crate) info: ColorInfo, - pub(crate) preferred_source: &'static str, -} - -pub(crate) struct ColorInfo { - pub(crate) name: &'static str, -} - impl ColorGroup { pub(crate) fn check_availability(&self, name: &str) -> Result<(), ColorError> { if name == self.root_type.name { @@ -35,6 +35,95 @@ impl ColorGroup { colors: self.colors.iter(), } } + + pub(crate) fn find_by_name(&self, name: &str) -> Option<&ColorInfo> { + if self.root_type.name == name { + Some(&self.root_type) + } else { + self.colors.iter().find_map(|color| { + if color.info.name == name { + Some(&color.info) + } else { + None + } + }) + } + } +} + +pub(crate) struct ColorType { + pub(crate) info: ColorInfo, + pub(crate) preferred_source: &'static str, +} + +type MetaTypeGeneratorFn = fn( + self_color: &ColorInfo, + meta_type_source: MetaTypeSource, + white_point: &Type, + used_input: &mut UsedInput, + user: InputUser, + meta: &TypeItemAttributes, +) -> syn::Result; + +pub(crate) struct ColorInfo { + pub(crate) name: &'static str, + pub(crate) module: Option<&'static str>, + pub(crate) default_white_point: InternalExternal>, + pub(crate) get_meta_type: Option, +} + +impl ColorInfo { + pub(crate) fn get_path(&self, internal: bool) -> TokenStream { + if let Some(module) = self.module { + util::path([module, self.name], internal) + } else { + util::path([self.name], internal) + } + } + + pub(crate) fn get_type( + &self, + meta_type_source: MetaTypeSource, + component: &Type, + white_point: &Type, + used_input: &mut UsedInput, + user: InputUser, + meta: &TypeItemAttributes, + ) -> syn::Result { + let meta_type: Option = self + .get_meta_type + .map(|get| get(self, meta_type_source, white_point, used_input, user, meta)) + .transpose()?; + + let color_path = self.get_path(meta.internal); + + if let Some(meta_type) = meta_type { + Ok(parse_quote!(#color_path::<#meta_type, #component>)) + } else { + Ok(parse_quote!(#color_path::<#component>)) + } + } + + pub(crate) fn get_default_white_point(&self, internal: bool) -> (Type, WhitePointSource) { + let path = if internal { + self.default_white_point.internal + } else { + self.default_white_point.external + }; + + path.map(|path| { + ( + util::path_type(path, internal), + WhitePointSource::ConcreteType, + ) + }) + .unwrap_or_else(|| (parse_quote!(_Wp), WhitePointSource::GeneratedGeneric)) + } +} + +pub(crate) struct InternalExternal { + pub(crate) internal: T, + pub(crate) external: T, } pub(crate) struct ColorNames { @@ -55,93 +144,317 @@ impl Iterator for ColorNames { } const BASE_COLORS: ColorGroup = ColorGroup { - root_type: ColorInfo { name: "Xyz" }, + root_type: ColorInfo { + name: "Xyz", + module: None, + default_white_point: InternalExternal { + internal: None, + external: Some(&["white_point", "D65"]), + }, + get_meta_type: Some(get_white_point), + }, colors: &[ ColorType { - info: ColorInfo { name: "Rgb" }, + info: ColorInfo { + name: "Rgb", + module: Some("rgb"), + default_white_point: InternalExternal { + internal: None, + external: Some(&["white_point", "D65"]), + }, + get_meta_type: Some(get_rgb_standard), + }, preferred_source: "Xyz", }, ColorType { - info: ColorInfo { name: "Luma" }, + info: ColorInfo { + name: "Luma", + module: Some("luma"), + default_white_point: InternalExternal { + internal: None, + external: Some(&["white_point", "D65"]), + }, + get_meta_type: Some(get_luma_standard), + }, preferred_source: "Xyz", }, ColorType { - info: ColorInfo { name: "Hsl" }, + info: ColorInfo { + name: "Hsl", + module: None, + default_white_point: InternalExternal { + internal: None, + external: Some(&["white_point", "D65"]), + }, + get_meta_type: Some(get_rgb_standard), + }, preferred_source: "Rgb", }, ColorType { - info: ColorInfo { name: "Hsluv" }, + info: ColorInfo { + name: "Hsluv", + module: None, + default_white_point: InternalExternal { + internal: None, + external: Some(&["white_point", "D65"]), + }, + get_meta_type: Some(get_white_point), + }, preferred_source: "Lchuv", }, ColorType { - info: ColorInfo { name: "Hsv" }, + info: ColorInfo { + name: "Hsv", + module: None, + default_white_point: InternalExternal { + internal: None, + external: Some(&["white_point", "D65"]), + }, + get_meta_type: Some(get_rgb_standard), + }, preferred_source: "Rgb", }, ColorType { - info: ColorInfo { name: "Hwb" }, + info: ColorInfo { + name: "Hwb", + module: None, + default_white_point: InternalExternal { + internal: None, + external: Some(&["white_point", "D65"]), + }, + get_meta_type: Some(get_rgb_standard), + }, preferred_source: "Hsv", }, ColorType { - info: ColorInfo { name: "Lab" }, + info: ColorInfo { + name: "Lab", + module: None, + default_white_point: InternalExternal { + internal: None, + external: Some(&["white_point", "D65"]), + }, + get_meta_type: Some(get_white_point), + }, preferred_source: "Xyz", }, ColorType { - info: ColorInfo { name: "Lch" }, + info: ColorInfo { + name: "Lch", + module: None, + default_white_point: InternalExternal { + internal: None, + external: Some(&["white_point", "D65"]), + }, + get_meta_type: Some(get_white_point), + }, preferred_source: "Lab", }, ColorType { - info: ColorInfo { name: "Lchuv" }, + info: ColorInfo { + name: "Lchuv", + module: None, + default_white_point: InternalExternal { + internal: None, + external: Some(&["white_point", "D65"]), + }, + get_meta_type: Some(get_white_point), + }, preferred_source: "Luv", }, ColorType { - info: ColorInfo { name: "Luv" }, + info: ColorInfo { + name: "Luv", + module: None, + default_white_point: InternalExternal { + internal: None, + external: Some(&["white_point", "D65"]), + }, + get_meta_type: Some(get_white_point), + }, preferred_source: "Xyz", }, ColorType { - info: ColorInfo { name: "Oklab" }, + info: ColorInfo { + name: "Oklab", + module: None, + default_white_point: InternalExternal { + internal: Some(&["white_point", "D65"]), + external: Some(&["white_point", "D65"]), + }, + get_meta_type: None, + }, preferred_source: "Xyz", }, ColorType { - info: ColorInfo { name: "Oklch" }, + info: ColorInfo { + name: "Oklch", + module: None, + default_white_point: InternalExternal { + internal: Some(&["white_point", "D65"]), + external: Some(&["white_point", "D65"]), + }, + get_meta_type: None, + }, preferred_source: "Oklab", }, ColorType { - info: ColorInfo { name: "Okhsl" }, + info: ColorInfo { + name: "Okhsl", + module: None, + default_white_point: InternalExternal { + internal: Some(&["white_point", "D65"]), + external: Some(&["white_point", "D65"]), + }, + get_meta_type: None, + }, preferred_source: "Oklab", }, ColorType { - info: ColorInfo { name: "Okhsv" }, + info: ColorInfo { + name: "Okhsv", + module: None, + default_white_point: InternalExternal { + internal: Some(&["white_point", "D65"]), + external: Some(&["white_point", "D65"]), + }, + get_meta_type: None, + }, preferred_source: "Oklab", }, ColorType { - info: ColorInfo { name: "Okhwb" }, + info: ColorInfo { + name: "Okhwb", + module: None, + default_white_point: InternalExternal { + internal: Some(&["white_point", "D65"]), + external: Some(&["white_point", "D65"]), + }, + get_meta_type: None, + }, preferred_source: "Okhsv", }, ColorType { - info: ColorInfo { name: "Yxy" }, + info: ColorInfo { + name: "Yxy", + module: None, + default_white_point: InternalExternal { + internal: None, + external: Some(&["white_point", "D65"]), + }, + get_meta_type: Some(get_white_point), + }, preferred_source: "Xyz", }, ], }; const CAM16_COLORS: ColorGroup = ColorGroup { - root_type: ColorInfo { name: "Cam16" }, + root_type: ColorInfo { + name: "Cam16", + module: Some("cam16"), + default_white_point: InternalExternal { + internal: None, + external: None, + }, + get_meta_type: None, + }, colors: &[ + // Partial CAM16 + ColorType { + info: ColorInfo { + name: "Cam16Jch", + module: Some("cam16"), + default_white_point: InternalExternal { + internal: None, + external: None, + }, + get_meta_type: None, + }, + preferred_source: "Cam16", + }, + ColorType { + info: ColorInfo { + name: "Cam16Jmh", + module: Some("cam16"), + default_white_point: InternalExternal { + internal: None, + external: None, + }, + get_meta_type: None, + }, + preferred_source: "Cam16", + }, + ColorType { + info: ColorInfo { + name: "Cam16Jsh", + module: Some("cam16"), + default_white_point: InternalExternal { + internal: None, + external: None, + }, + get_meta_type: None, + }, + preferred_source: "Cam16", + }, + ColorType { + info: ColorInfo { + name: "Cam16Qch", + module: Some("cam16"), + default_white_point: InternalExternal { + internal: None, + external: None, + }, + get_meta_type: None, + }, + preferred_source: "Cam16", + }, ColorType { info: ColorInfo { - name: "PartialCam16", + name: "Cam16Qmh", + module: Some("cam16"), + default_white_point: InternalExternal { + internal: None, + external: None, + }, + get_meta_type: None, }, preferred_source: "Cam16", }, + ColorType { + info: ColorInfo { + name: "Cam16Qsh", + module: Some("cam16"), + default_white_point: InternalExternal { + internal: None, + external: None, + }, + get_meta_type: None, + }, + preferred_source: "Cam16", + }, + // CAM16 UCS ColorType { info: ColorInfo { name: "Cam16UcsJmh", + module: Some("cam16"), + default_white_point: InternalExternal { + internal: None, + external: None, + }, + get_meta_type: None, }, - preferred_source: "PartialCam16", + preferred_source: "Cam16Jmh", }, ColorType { info: ColorInfo { name: "Cam16UcsJab", + module: Some("cam16"), + default_white_point: InternalExternal { + internal: None, + external: None, + }, + get_meta_type: None, }, preferred_source: "Cam16UcsJmh", }, @@ -172,3 +485,106 @@ impl Default for AvailableColorGroup { pub(crate) enum ColorError { UnknownColor, } + +fn get_rgb_standard( + self_color: &ColorInfo, + meta_type_source: MetaTypeSource, + white_point: &Type, + used_input: &mut UsedInput, + user: InputUser, + meta: &TypeItemAttributes, +) -> syn::Result { + if let Some(rgb_standard) = &meta.rgb_standard { + Ok(rgb_standard.clone()) + } else { + match meta_type_source { + MetaTypeSource::Generics(generics) => { + used_input.white_point.set_used(user); + + let rgb_standard_path = util::path(["rgb", "RgbStandard"], meta.internal); + let rgb_space_path = util::path(["rgb", "RgbSpace"], meta.internal); + + generics.params.push(GenericParam::Type( + Ident::new("_S", Span::call_site()).into(), + )); + let where_clause = generics.make_where_clause(); + + where_clause + .predicates + .push(parse_quote!(_S: #rgb_standard_path)); + where_clause + .predicates + .push(parse_quote!(_S::Space: #rgb_space_path)); + + Ok(parse_quote!(_S)) + } + MetaTypeSource::OtherColor(other_color) => { + match other_color.name { + "Rgb" | "Hsl" | "Hsv" | "Hwb" => Ok(parse_quote!(_S)), + _ => Err(syn::parse::Error::new( + Span::call_site(), + format!( + "could not determine which RGB standard to use when converting to and from `{}` via `{}`", + other_color.name, + self_color.name + ), + )), + } + } + } + } +} + +fn get_luma_standard( + _self_color: &ColorInfo, + meta_type_source: MetaTypeSource, + white_point: &Type, + used_input: &mut UsedInput, + user: InputUser, + meta: &TypeItemAttributes, +) -> syn::Result { + if let Some(luma_standard) = meta.luma_standard.as_ref() { + return Ok(luma_standard.clone()); + } + + used_input.white_point.set_used(user); + + match meta_type_source { + MetaTypeSource::Generics(generics) => { + let luma_standard_path = util::path(["luma", "LumaStandard"], meta.internal); + + generics.params.push(GenericParam::Type( + Ident::new("_S", Span::call_site()).into(), + )); + + generics + .make_where_clause() + .predicates + .push(parse_quote!(_S: #luma_standard_path)); + + Ok(parse_quote!(_S)) + } + MetaTypeSource::OtherColor(_) => { + let linear_path = util::path(["encoding", "Linear"], meta.internal); + + Ok(parse_quote!(#linear_path<#white_point>)) + } + } +} + +fn get_white_point( + _self_color: &ColorInfo, + _meta_type_source: MetaTypeSource, + white_point: &Type, + used_input: &mut UsedInput, + user: InputUser, + _meta: &TypeItemAttributes, +) -> syn::Result { + used_input.white_point.set_used(user); + Ok(white_point.clone()) +} + +pub(crate) enum MetaTypeSource<'a> { + OtherColor(&'a ColorInfo), + Generics(&'a mut Generics), +} diff --git a/palette_derive/src/convert/from_color_unclamped.rs b/palette_derive/src/convert/from_color_unclamped.rs index 15f436bf9..ad39d687f 100644 --- a/palette_derive/src/convert/from_color_unclamped.rs +++ b/palette_derive/src/convert/from_color_unclamped.rs @@ -3,12 +3,15 @@ use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; use syn::{parse_quote, DeriveInput, Generics, Ident, Result, Type}; -use crate::meta::{ - parse_field_attributes, parse_namespaced_attributes, FieldAttributes, IdentOrIndex, - TypeItemAttributes, +use crate::{ + color_types::{ColorInfo, MetaTypeSource}, + convert::util::{InputUser, WhitePointSource}, + meta::{ + parse_field_attributes, parse_namespaced_attributes, FieldAttributes, IdentOrIndex, + TypeItemAttributes, + }, + util, }; -use crate::util; -use crate::{color_types::ColorInfo, convert::util::WhitePointSource}; use super::util::{component_type, find_nearest_color, get_convert_color_type, white_point_type}; @@ -130,142 +133,26 @@ fn prepare_from_impl_for_pair( meta: &TypeItemAttributes, mut generics: Generics, ) -> Result> { - let linear_path = util::path(["encoding", "Linear"], meta.internal); - let nearest_color_name = find_nearest_color(color.name, meta)?; + let nearest_color = find_nearest_color(color, meta)?; // Figures out which white point the target type prefers, unless it's specified in `white_point`. let (white_point, white_point_source) = if let Some((white_point, source)) = white_point { (white_point, source) } else { - match color.name { - "Oklab" | "Oklch" | "Okhsv" | "Okhsl" | "Okhwb" => ( - util::path_type(&["white_point", "D65"], meta.internal), - WhitePointSource::ConcreteType, - ), - "Cam16" | "Cam16UcsJmh" | "Cam16UcsJab" | "PartialCam16" => { - // These color types perform white balance when converting, so - // they don't require any specific white point. Source types - // that don't specify a white point will cause the type - // parameter to be unconstrained. Those implementations will be - // skipped further down. - (parse_quote!(_Wp), WhitePointSource::GeneratedGeneric) - } - _ => { - if meta.internal { - (parse_quote!(_Wp), WhitePointSource::GeneratedGeneric) - } else { - // This replicates the old behavior, but should be dropped - // in a future version. The default should then be a - // generated `_Wp` type parameter, like above. - ( - util::path_type(&["white_point", "D65"], meta.internal), - WhitePointSource::ConcreteType, - ) - } - } - } + color.get_default_white_point(meta.internal) }; - let (color_ty, mut used_input) = get_convert_color_type( - color.name, - &white_point, + let (color_ty, mut used_input) = + get_convert_color_type(color, &white_point, component, meta, &mut generics)?; + + let nearest_color_ty = nearest_color.get_type( + MetaTypeSource::OtherColor(color), component, + &white_point, + &mut used_input, + InputUser::Nearest, meta, - &mut generics, - meta.internal, - ); - - // Figures out the remaining preferred meta types. Failing to figure out - // what they are is currently a hard error. - let nearest_color_path = util::color_path(nearest_color_name, meta.internal); - let target_color_rgb_standard = match color.name { - "Rgb" | "Hsl" | "Hsv" | "Hwb" => Some(parse_quote!(_S)), - _ => None, - }; - let target_color_cam16_chromaticity = match color.name { - "PartialCam16" => Some(parse_quote!(_C)), - "Cam16UcsJmh" | "Cam16UcsJab" => { - let path = util::path(["cam16", "Colorfulness"], meta.internal); - Some(parse_quote!(#path<#component>)) - } - _ => None, - }; - let target_color_cam16_luminance = match color.name { - "PartialCam16" => Some(parse_quote!(_L)), - "Cam16UcsJmh" | "Cam16UcsJab" => { - let path = util::path(["cam16", "Lightness"], meta.internal); - Some(parse_quote!(#path<#component>)) - } - _ => None, - }; - - let nearest_color_ty: Type = match nearest_color_name { - "Rgb" | "Hsl" | "Hsv" | "Hwb" => { - let rgb_standard = meta.rgb_standard - .clone() - .or(target_color_rgb_standard) - .ok_or_else(|| { - syn::parse::Error::new( - Span::call_site(), - format!( - "could not determine which RGB standard to use when converting to and from `{}` via `{}`", - color.name, - nearest_color_name - ), - ) - })?; - - parse_quote!(#nearest_color_path::<#rgb_standard, #component>) - } - "Luma" => { - if let Some(luma_standard) = meta.luma_standard.as_ref() { - parse_quote!(#nearest_color_path::<#luma_standard, #component>) - } else { - used_input.white_point.used_by_nearest(); - parse_quote!(#nearest_color_path::<#linear_path<#white_point>, #component>) - } - } - "Oklab" | "Oklch" | "Okhsv" | "Okhsl" | "Okhwb" => { - parse_quote!(#nearest_color_path::<#component>) - } - "Cam16" | "Cam16UcsJmh" | "Cam16UcsJab" => { - parse_quote!(#nearest_color_path::<#component>) - } - "PartialCam16" => { - let cam16_chromaticity = meta.cam16_chromaticity - .clone() - .or(target_color_cam16_chromaticity) - .ok_or_else( - || syn::parse::Error::new( - Span::call_site(), - format!( - "could not determine which CAM16 chromaticity to use when converting to and from `{}` via `{}`", - color.name, - nearest_color_name - ), - ) - )?; - let cam16_luminance = meta.cam16_luminance - .clone() - .or(target_color_cam16_luminance) - .ok_or_else( - || syn::parse::Error::new( - Span::call_site(), - format!( - "could not determine which CAM16 luminance to use when converting to and from `{}` via `{}`", - color.name, - nearest_color_name - ), - ) - )?; - - parse_quote!(#nearest_color_path::<#component, #cam16_luminance, #cam16_chromaticity>) - } - _ => { - used_input.white_point.used_by_nearest(); - parse_quote!(#nearest_color_path::<#white_point, #component>) - } - }; + )?; // Skip implementing the trait where it wouldn't be able to constrain the // white point. This is only happening when certain optional features are diff --git a/palette_derive/src/convert/mod.rs b/palette_derive/src/convert/mod.rs index de6c01cb4..0b88a7e56 100644 --- a/palette_derive/src/convert/mod.rs +++ b/palette_derive/src/convert/mod.rs @@ -1,4 +1,4 @@ pub use self::from_color_unclamped::derive as derive_from_color_unclamped; mod from_color_unclamped; -mod util; +pub mod util; diff --git a/palette_derive/src/convert/util.rs b/palette_derive/src/convert/util.rs index b2480340b..479bd3ffd 100644 --- a/palette_derive/src/convert/util.rs +++ b/palette_derive/src/convert/util.rs @@ -1,10 +1,13 @@ use std::collections::HashMap; use proc_macro2::Span; -use syn::spanned::Spanned; -use syn::{parse_quote, GenericParam, Generics, Ident, Result, Type}; +use syn::{parse_quote, Generics, Result, Type}; -use crate::{meta::TypeItemAttributes, util}; +use crate::{ + color_types::{ColorInfo, MetaTypeSource}, + meta::TypeItemAttributes, + util, +}; pub fn white_point_type( white_point: Option<&Type>, @@ -40,125 +43,29 @@ pub fn component_type(component: Option) -> Type { } pub fn get_convert_color_type( - color: &str, + color: &ColorInfo, white_point: &Type, component: &Type, meta: &TypeItemAttributes, generics: &mut Generics, - internal: bool, -) -> (Type, UsedInput) { - let color_path = util::color_path(color, internal); - - match color { - "Luma" => { - let luma_standard_path = util::path(["luma", "LumaStandard"], internal); - - if let Some(luma_standard) = &meta.luma_standard { - ( - parse_quote!(#color_path<#luma_standard, #component>), - UsedInput::default(), - ) - } else { - generics.params.push(GenericParam::Type( - Ident::new("_S", Span::call_site()).into(), - )); - - generics - .make_where_clause() - .predicates - .push(parse_quote!(_S: #luma_standard_path)); - ( - parse_quote!(#color_path<_S, #component>), - UsedInput { - white_point: WhitePointUsed::BY_TARGET, - }, - ) - } - } - "Rgb" | "Hsl" | "Hsv" | "Hwb" => { - let rgb_standard_path = util::path(["rgb", "RgbStandard"], internal); - let rgb_space_path = util::path(["rgb", "RgbSpace"], internal); - - if let Some(rgb_standard) = &meta.rgb_standard { - ( - parse_quote!(#color_path<#rgb_standard, #component>), - UsedInput::default(), - ) - } else { - generics.params.push(GenericParam::Type( - Ident::new("_S", Span::call_site()).into(), - )); - let where_clause = generics.make_where_clause(); - - where_clause - .predicates - .push(parse_quote!(_S: #rgb_standard_path)); - where_clause - .predicates - .push(parse_quote!(_S::Space: #rgb_space_path)); - - ( - parse_quote!(#color_path<_S, #component>), - UsedInput { - white_point: WhitePointUsed::BY_TARGET, - }, - ) - } - } - "Oklab" | "Oklch" | "Okhsv" | "Okhsl" | "Okhwb" => { - (parse_quote!(#color_path<#component>), UsedInput::default()) - } - "Cam16" | "Cam16UcsJmh" | "Cam16UcsJab" => { - (parse_quote!(#color_path<#component>), UsedInput::default()) - } - "PartialCam16" => { - let cam_chromaticity_path = util::path(["cam16", "Cam16Chromaticity"], internal); - let cam_luminance_path = util::path(["cam16", "Cam16Luminance"], internal); - - let chromaticity = if let Some(chromaticity) = &meta.cam16_chromaticity { - chromaticity.clone() - } else { - generics.params.push(GenericParam::Type( - Ident::new("_C", Span::call_site()).into(), - )); - let where_clause = generics.make_where_clause(); - where_clause - .predicates - .push(parse_quote!(_C: #cam_chromaticity_path<#component>)); - - parse_quote!(_C) - }; - - let luminance = if let Some(luminance) = &meta.cam16_luminance { - luminance.clone() - } else { - generics.params.push(GenericParam::Type( - Ident::new("_L", Span::call_site()).into(), - )); - - let where_clause = generics.make_where_clause(); - where_clause - .predicates - .push(parse_quote!(_L: #cam_luminance_path<#component>)); - - parse_quote!(_L) - }; - - ( - parse_quote!(#color_path<#component, #luminance, #chromaticity>), - UsedInput::default(), - ) - } - _ => ( - parse_quote!(#color_path<#white_point, #component>), - UsedInput { - white_point: WhitePointUsed::BY_TARGET, - }, - ), - } +) -> syn::Result<(Type, UsedInput)> { + let mut used_input = UsedInput::default(); + let color_type = color.get_type( + MetaTypeSource::Generics(generics), + component, + white_point, + &mut used_input, + InputUser::Target, + meta, + )?; + + Ok((color_type, used_input)) } -pub fn find_nearest_color<'a>(color: &'a str, meta: &TypeItemAttributes) -> Result<&'a str> { +pub fn find_nearest_color<'a>( + color: &'a ColorInfo, + meta: &TypeItemAttributes, +) -> Result<&'a ColorInfo> { let mut stack = vec![(color, 0)]; let mut found = None; let mut visited = HashMap::new(); @@ -167,7 +74,7 @@ pub fn find_nearest_color<'a>(color: &'a str, meta: &TypeItemAttributes) -> Resu assert!(!meta.skip_derives.is_empty()); while let Some((color, distance)) = stack.pop() { - if meta.skip_derives.contains(color) { + if meta.skip_derives.contains(color.name) { if let Some((_, found_distance)) = found { if distance < found_distance { found = Some((color, distance)); @@ -179,25 +86,30 @@ pub fn find_nearest_color<'a>(color: &'a str, meta: &TypeItemAttributes) -> Resu } } - if let Some(&previous_distance) = visited.get(color) { + if let Some(&previous_distance) = visited.get(color.name) { if previous_distance <= distance { continue; } } - visited.insert(color, distance); + visited.insert(color.name, distance); // Start by pushing the plan B routes... for candidate in meta.color_group.get_group().colors { - if color == candidate.preferred_source { - stack.push((candidate.info.name, distance + 1)); + if color.name == candidate.preferred_source { + stack.push((&candidate.info, distance + 1)); } } // ...then push the preferred routes. They will be popped first. for candidate in meta.color_group.get_group().colors { - if color == candidate.info.name { - stack.push((candidate.preferred_source, distance + 1)); + if color.name == candidate.info.name { + let preferred = meta + .color_group + .get_group() + .find_by_name(candidate.preferred_source) + .expect("preferred sources have to exist in the group"); + stack.push((preferred, distance + 1)); } } } @@ -206,10 +118,10 @@ pub fn find_nearest_color<'a>(color: &'a str, meta: &TypeItemAttributes) -> Resu Ok(color) } else { Err(::syn::parse::Error::new( - color.span(), + Span::call_site(), format!( "none of the skipped colors can be used for converting from {}", - color + color.name ), )) } @@ -226,23 +138,21 @@ pub enum WhitePointSource { #[derive(Debug, Default)] pub struct UsedInput { - pub white_point: WhitePointUsed, + pub white_point: InputUsage, } #[derive(Debug, Default)] -pub struct WhitePointUsed { +pub struct InputUsage { used_by_target: bool, used_by_nearest: bool, } -impl WhitePointUsed { - const BY_TARGET: Self = WhitePointUsed { - used_by_target: true, - used_by_nearest: false, - }; - - pub(crate) fn used_by_nearest(&mut self) { - self.used_by_nearest = true; +impl InputUsage { + pub(crate) fn set_used(&mut self, user: InputUser) { + match user { + InputUser::Target => self.used_by_target = true, + InputUser::Nearest => self.used_by_nearest = true, + } } pub(crate) fn is_used(&self) -> bool { @@ -253,3 +163,18 @@ impl WhitePointUsed { !self.used_by_target && self.used_by_nearest } } + +impl From for InputUsage { + fn from(value: InputUser) -> Self { + let mut result = Self::default(); + result.set_used(value); + + result + } +} + +#[derive(Clone, Copy)] +pub enum InputUser { + Target, + Nearest, +} diff --git a/palette_derive/src/meta/type_item_attributes.rs b/palette_derive/src/meta/type_item_attributes.rs index db7d67e5d..0aceb172a 100644 --- a/palette_derive/src/meta/type_item_attributes.rs +++ b/palette_derive/src/meta/type_item_attributes.rs @@ -17,8 +17,6 @@ pub struct TypeItemAttributes { pub white_point: Option, pub rgb_standard: Option, pub luma_standard: Option, - pub cam16_chromaticity: Option, - pub cam16_luminance: Option, pub(crate) color_group: AvailableColorGroup, } @@ -72,12 +70,6 @@ impl AttributeArgumentParser for TypeItemAttributes { Some("luma_standard") => { get_meta_type_argument(argument, &mut self.luma_standard)?; } - Some("cam16_chromaticity") => { - get_meta_type_argument(argument, &mut self.cam16_chromaticity)?; - } - Some("cam16_luminance") => { - get_meta_type_argument(argument, &mut self.cam16_luminance)?; - } Some("color_group") => { let mut errors = Vec::new(); diff --git a/palette_derive/src/util.rs b/palette_derive/src/util.rs index 71ef95bd3..755b8701b 100644 --- a/palette_derive/src/util.rs +++ b/palette_derive/src/util.rs @@ -29,17 +29,6 @@ pub fn path_type(path: &[&str], internal: bool) -> Type { } } -pub fn color_path(color: &str, internal: bool) -> TokenStream { - match color { - "Luma" => path(["luma", "Luma"], internal), - "Rgb" => path(["rgb", "Rgb"], internal), - "Cam16" | "PartialCam16" | "Cam16UcsJmh" | "Cam16UcsJab" => { - path(["cam16", color], internal) - } - _ => path([color], internal), - } -} - #[cfg(feature = "find-crate")] fn find_crate_name() -> Ident { use find_crate::Error;