Skip to content

Commit 2835f9f

Browse files
committed
Make it possible to pre-calculate viewing condition parameters
1 parent 71d6738 commit 2835f9f

File tree

3 files changed

+195
-77
lines changed

3 files changed

+195
-77
lines changed

palette/src/cam16.rs

+179-40
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,19 @@ mod math;
2323
/// The CIE CAM16 color appearance model.
2424
///
2525
/// It's a set of six technically defined attributes that describe the
26-
/// appearance of a color in an environment, and it's a successor of
27-
/// [CIECAM02](https://en.wikipedia.org/wiki/CIECAM02). Not all attributes are
28-
/// needed to be known to convert _from_ CAM16, since they are correlated and
29-
/// derived from each other. This library provides a separate [`PartialCam16`]
30-
/// to make it easier to specify a minimum attribute set.
26+
/// appearance of a color under certain viewing conditions, and it's a successor
27+
/// of [CIECAM02](https://en.wikipedia.org/wiki/CIECAM02). The viewing
28+
/// conditions are defined using [`Parameters`] and two set of `Cam16`
29+
/// attributes are only really comparable if they were calculated from the same
30+
/// set of viewing condition parameters. The implementations of
31+
/// [`FromColor`][crate::FromColor], [`IntoColor`][crate::IntoColor], etc. use
32+
/// `Parameters::default()` as their viewing conditions. See [`FromCam16`] and
33+
/// [`IntoCam16`] for options with more control over the parameters.
34+
///
35+
/// Not all attributes are needed to be known to convert _from_ CAM16, since
36+
/// they are correlated and derived from each other. This library provides a
37+
/// separate [`PartialCam16`] to make it easier to specify a minimum attribute
38+
/// set.
3139
#[derive(Debug, WithAlpha, FromColorUnclamped)]
3240
#[palette(
3341
palette_internal,
@@ -155,14 +163,14 @@ impl<Wp, T> FromColorUnclamped<Cam16<Wp, T>> for Cam16<Wp, T> {
155163
impl<Wp, T> FromColorUnclamped<Xyz<Wp, T>> for Cam16<Wp, T>
156164
where
157165
Xyz<Wp, T>: IntoCam16<Wp, T>,
158-
T: Real,
166+
BakedParameters<Wp, T>: Default,
159167
{
160168
fn from_color_unclamped(val: Xyz<Wp, T>) -> Self {
161-
val.into_cam16(Parameters::default())
169+
val.into_cam16(BakedParameters::default())
162170
}
163171
}
164172

165-
/// A partial version of [`Cam16`] with only one of each kind of parameter.
173+
/// A partial version of [`Cam16`] with only one of each kind of attribute.
166174
///
167175
/// This is enough information for converting CAM16 to other color spaces.
168176
#[derive(Debug)]
@@ -335,34 +343,18 @@ pub enum LuminanceType<T> {
335343
pub trait IntoCam16<Wp, T> {
336344
/// Convert `self` into CAM16, with `parameters` that describe the viewing
337345
/// conditions.
338-
fn into_cam16(self, parameters: Parameters<Wp, T>) -> Cam16<Wp, T>;
346+
fn into_cam16(self, parameters: BakedParameters<Wp, T>) -> Cam16<Wp, T>;
339347
}
340348

341349
impl<C, Wp, T> IntoCam16<Wp, T> for C
342350
where
343351
C: IntoColorUnclamped<Xyz<Wp, T>>,
344-
T: Real
345-
+ One
346-
+ Zero
347-
+ Clamp
348-
+ PartialCmp
349-
+ Arithmetics
350-
+ Powf
351-
+ Sqrt
352-
+ Exp
353-
+ Abs
354-
+ Signum
355-
+ Trigonometry
356-
+ RealAngle
357-
+ HasBoolMask
358-
+ Clone,
359-
T::Mask: LazySelect<T>,
360-
Wp: WhitePoint<T>,
352+
T: Real + Arithmetics + Powf + Sqrt + Abs + Signum + Trigonometry + RealAngle + Clone,
361353
{
362-
fn into_cam16(self, parameters: Parameters<Wp, T>) -> Cam16<Wp, T> {
354+
fn into_cam16(self, parameters: BakedParameters<Wp, T>) -> Cam16<Wp, T> {
363355
math::xyz_to_cam16(
364356
self.into_color_unclamped().with_white_point(),
365-
parameters.into_any_white_point(),
357+
parameters.inner,
366358
)
367359
.with_white_point()
368360
}
@@ -372,18 +364,16 @@ where
372364
pub trait FromCam16<Wp, T> {
373365
/// Convert `cam16` into `Self`, with `parameters` that describe the viewing
374366
/// conditions.
375-
fn from_cam16(cam16: PartialCam16<Wp, T>, parameters: Parameters<Wp, T>) -> Self;
367+
fn from_cam16(cam16: PartialCam16<Wp, T>, parameters: BakedParameters<Wp, T>) -> Self;
376368
}
377369

378370
impl<C, Wp, T> FromCam16<Wp, T> for C
379371
where
380372
T: Real
381373
+ One
382374
+ Zero
383-
+ Clamp
384375
+ Sqrt
385376
+ Powf
386-
+ Exp
387377
+ Abs
388378
+ Signum
389379
+ Arithmetics
@@ -392,27 +382,32 @@ where
392382
+ SignedAngle
393383
+ PartialCmp
394384
+ Clone,
395-
T::Mask: LazySelect<T> + LazySelect<Xyz<white_point::Any, T>>,
385+
T::Mask: LazySelect<Xyz<white_point::Any, T>>,
396386
Xyz<Wp, T>: IntoColorUnclamped<C>,
397-
Wp: WhitePoint<T>,
398387
{
399-
fn from_cam16(cam16: PartialCam16<Wp, T>, parameters: Parameters<Wp, T>) -> Self {
400-
math::cam16_to_xyz(cam16.with_white_point(), parameters.into_any_white_point())
388+
fn from_cam16(cam16: PartialCam16<Wp, T>, parameters: BakedParameters<Wp, T>) -> Self {
389+
math::cam16_to_xyz(cam16.with_white_point(), parameters.inner)
401390
.with_white_point()
402391
.into_color_unclamped()
403392
}
404393
}
405394

406-
/// Parameters for CAM16.
395+
/// Parameters for CAM16 that describe the viewing conditions.
407396
///
408397
/// These parameters describe the viewing conditions for a more accurate color
409-
/// appearance metric. The default values are used in [`FromColor`],
398+
/// appearance metric. The CAM16 attributes and derived values are only really
399+
/// comparable if they were calculated with the same parameters. The parameters
400+
/// are, however, too dynamic to all be part of the type parameters of
401+
/// [`Cam16`].
402+
///
403+
/// The default values are used in [`FromColor`][crate::FromColor],
410404
/// [`IntoColor`][crate::IntoColor], etc.
411405
///
412-
/// See also Moroney (2000) [Usage Guidelines for CIECAM97s][moroney_2000] for more
413-
/// information and advice on how to customize these parameters.
406+
/// See also Moroney (2000) [Usage Guidelines for CIECAM97s][moroney_2000] for
407+
/// more information and advice on how to customize these parameters.
414408
///
415-
/// [moroney_2000]: https://www.imaging.org/common/uploaded%20files/pdfs/Papers/2000/PICS-0-81/1611.pdf
409+
/// [moroney_2000]:
410+
/// https://www.imaging.org/common/uploaded%20files/pdfs/Papers/2000/PICS-0-81/1611.pdf
416411
#[non_exhaustive]
417412
pub struct Parameters<Wp, T> {
418413
/// The reference white point. Defaults to `Wp` when it implements
@@ -455,6 +450,135 @@ where
455450
}
456451
}
457452

453+
impl<T> Parameters<white_point::Any, T> {
454+
/// Pre-calculate parameters with no statically known reference white point.
455+
///
456+
/// The default white point in this case is [`D65`], unless specified in
457+
/// `Self::white_point`.
458+
pub fn any_into(self) -> BakedParameters<white_point::Any, T>
459+
where
460+
T: Real
461+
+ One
462+
+ Zero
463+
+ Clamp
464+
+ PartialCmp
465+
+ Arithmetics
466+
+ Powf
467+
+ Sqrt
468+
+ Exp
469+
+ Abs
470+
+ Signum
471+
+ Clone,
472+
T::Mask: LazySelect<T>,
473+
{
474+
BakedParameters::any_from(self)
475+
}
476+
}
477+
478+
impl<Wp, T> Clone for Parameters<Wp, T>
479+
where
480+
T: Clone,
481+
{
482+
fn clone(&self) -> Self {
483+
Self {
484+
white_point: self.white_point.clone(),
485+
adapting_luminance: self.adapting_luminance.clone(),
486+
background_luminance: self.background_luminance.clone(),
487+
surround: self.surround.clone(),
488+
discounting: self.discounting.clone(),
489+
}
490+
}
491+
}
492+
493+
impl<Wp, T> Copy for Parameters<Wp, T> where T: Copy {}
494+
495+
/// Pre-calculated variables for CAM16, that only depend on the viewing
496+
/// conditions.
497+
///
498+
/// Derived from [`Parameters`], the `BakedParameters` can be used in
499+
/// [`FromCam16`] and [`IntoCam16`] to reduce the amount of repeated work
500+
/// required for converting multiple colors.
501+
pub struct BakedParameters<Wp, T> {
502+
inner: math::DependentParameters<T>,
503+
white_point: PhantomData<Wp>,
504+
}
505+
506+
impl<T> BakedParameters<white_point::Any, T> {
507+
/// Pre-calculate parameters with no statically known reference white point.
508+
///
509+
/// The default white point in this case is [`D65`], unless specified in
510+
/// `Parameters::white_point`.
511+
pub fn any_from(parameters: Parameters<white_point::Any, T>) -> Self
512+
where
513+
T: Real
514+
+ One
515+
+ Zero
516+
+ Clamp
517+
+ PartialCmp
518+
+ Arithmetics
519+
+ Powf
520+
+ Sqrt
521+
+ Exp
522+
+ Abs
523+
+ Signum
524+
+ Clone,
525+
T::Mask: LazySelect<T>,
526+
{
527+
Self {
528+
inner: math::prepare_parameters(parameters),
529+
white_point: PhantomData,
530+
}
531+
}
532+
}
533+
534+
impl<Wp, T> Clone for BakedParameters<Wp, T>
535+
where
536+
T: Clone,
537+
{
538+
fn clone(&self) -> Self {
539+
Self {
540+
inner: self.inner.clone(),
541+
white_point: PhantomData,
542+
}
543+
}
544+
}
545+
546+
impl<Wp, T> Copy for BakedParameters<Wp, T> where T: Copy {}
547+
548+
impl<Wp, T> From<Parameters<Wp, T>> for BakedParameters<Wp, T>
549+
where
550+
Wp: WhitePoint<T>,
551+
T: Real
552+
+ One
553+
+ Zero
554+
+ Clamp
555+
+ PartialCmp
556+
+ Arithmetics
557+
+ Powf
558+
+ Sqrt
559+
+ Exp
560+
+ Abs
561+
+ Signum
562+
+ Clone,
563+
T::Mask: LazySelect<T>,
564+
{
565+
fn from(value: Parameters<Wp, T>) -> Self {
566+
Self {
567+
inner: math::prepare_parameters(value.into_any_white_point()),
568+
white_point: PhantomData,
569+
}
570+
}
571+
}
572+
573+
impl<Wp, T> Default for BakedParameters<Wp, T>
574+
where
575+
Parameters<Wp, T>: Default + Into<BakedParameters<Wp, T>>,
576+
{
577+
fn default() -> Self {
578+
Parameters::default().into()
579+
}
580+
}
581+
458582
impl<Wp, T> Parameters<Wp, T>
459583
where
460584
T: Real,
@@ -481,6 +605,7 @@ where
481605
}
482606

483607
/// A description of the peripheral area.
608+
#[derive(Clone, Copy)]
484609
#[non_exhaustive]
485610
pub enum Surround<T> {
486611
/// Represents a dark room, such as a movie theatre. Corresponds to a
@@ -550,6 +675,20 @@ where
550675
}
551676
}
552677

678+
impl<Wp, T> Clone for WhitePointParameter<Wp, T>
679+
where
680+
T: Clone,
681+
{
682+
fn clone(&self) -> Self {
683+
match self {
684+
Self::Default => Self::Default,
685+
Self::Custom(white_point) => Self::Custom(white_point.clone()),
686+
}
687+
}
688+
}
689+
690+
impl<Wp, T> Copy for WhitePointParameter<Wp, T> where T: Copy {}
691+
553692
#[cfg(test)]
554693
mod test {
555694
use crate::{convert::IntoColor, Srgb};

0 commit comments

Comments
 (0)