chromashift/chromaticity.rs
1use crate::Matrix3;
2
3/// A CIE 1931 xy chromaticity: a colour's hue and purity with its luminance factored out.
4///
5/// Used for the white point of an [`RgbSpace`](crate::RgbSpace).
6///
7/// ```
8/// # use chromashift::Chromaticity;
9/// let [x, y, z] = Chromaticity::D65.xyz();
10/// assert_eq!(y, 1.0);
11/// assert!((x - 0.9504559).abs() < 1e-6);
12/// ```
13#[derive(Debug, Clone, Copy, PartialEq)]
14pub struct Chromaticity {
15 pub x: f64,
16 pub y: f64,
17}
18
19impl Chromaticity {
20 /// The D65 white point, as used by CSS Color 4: <https://drafts.csswg.org/css-color-4/#d65>
21 pub const D65: Self = Self::new(0.3127, 0.3290);
22
23 /// The D50 white point, as used by CSS Color 4: <https://drafts.csswg.org/css-color-4/#d50>
24 pub const D50: Self = Self::new(0.3457, 0.3585);
25
26 const BRADFORD: Matrix3 =
27 Matrix3([[0.8951, 0.2664, -0.1614], [-0.7502, 1.7135, 0.0367], [0.0389, -0.0685, 1.0296]]);
28
29 pub const fn new(x: f64, y: f64) -> Self {
30 Self { x, y }
31 }
32
33 /// This chromaticity as CIE XYZ, normalised to `Y = 1`.
34 pub const fn xyz(self) -> [f64; 3] {
35 [self.x / self.y, 1.0, (1.0 - self.x - self.y) / self.y]
36 }
37
38 /// The Bradford chromatic adaptation matrix taking CIE XYZ relative to `self` to XYZ relative to `other`.
39 ///
40 /// <https://en.wikipedia.org/wiki/LMS_color_space#Bradford's_spectrally_sharpened_matrix_(LLAB,_CIECAM97s)>
41 ///
42 /// ```
43 /// # use chromashift::Chromaticity;
44 /// let m = Chromaticity::D65.adapt_to(Chromaticity::D50);
45 /// assert!((m.0[0][0] - 1.0479297925449969).abs() < 1e-12);
46 /// ```
47 pub const fn adapt_to(self, other: Self) -> Matrix3 {
48 if self.x == other.x && self.y == other.y {
49 return Matrix3::IDENTITY;
50 }
51 let source = Self::BRADFORD.transform(self.xyz());
52 let dest = Self::BRADFORD.transform(other.xyz());
53 let scale = Matrix3::diagonal([dest[0] / source[0], dest[1] / source[1], dest[2] / source[2]]);
54 Self::BRADFORD.then(&scale).then(&Self::BRADFORD.inverse())
55 }
56}