color/
chromaticity.rs

1// Copyright 2024 the Color Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use crate::{matdiagmatmul, matmatmul, matvecmul};
5
6/// CIE `xy` chromaticity, specifying a color in the XYZ color space, but not its luminosity.
7///
8/// An absolute color can be specified by adding a luminosity coordinate `Y` as in `xyY`. An `XYZ`
9/// color can be calculated from `xyY` as follows.
10///
11/// ```text
12/// X = Y/y * x
13/// Y = Y
14/// Z = Y/y * (1 - x - y)
15/// ```
16#[derive(Clone, Copy, Debug, PartialEq)]
17pub struct Chromaticity {
18    /// The x-coordinate of the CIE `xy` chromaticity.
19    pub x: f32,
20
21    /// The y-coordinate of the CIE `xy` chromaticity.
22    pub y: f32,
23}
24
25impl Chromaticity {
26    /// The CIE D65 white point under the standard 2° observer.
27    ///
28    /// This is a common white point for color spaces targeting monitors.
29    ///
30    /// The white point's chromaticities are truncated to four digits here, as specified by the
31    /// CSS Color 4 specification, and following most color spaces using this white point.
32    pub const D65: Self = Self {
33        x: 0.3127,
34        y: 0.3290,
35    };
36
37    /// The CIE D50 white point under the standard 2° observer.
38    ///
39    /// The white point's chromaticities are truncated to four digits here, as specified by the
40    /// CSS Color 4 specification, and following most color spaces using this white point.
41    pub const D50: Self = Self {
42        x: 0.3457,
43        y: 0.3585,
44    };
45
46    /// The [ACES white point][aceswp].
47    ///
48    /// This is the reference white of [ACEScg](crate::AcesCg) and [ACES2065-1](crate::Aces2065_1).
49    /// The white point is near the D60 white point under the standard 2° observer.
50    ///
51    /// [aceswp]: https://docs.acescentral.com/tb/white-point
52    pub const ACES: Self = Self {
53        x: 0.32168,
54        y: 0.33767,
55    };
56
57    /// Convert the `xy` chromaticities to XYZ, assuming `xyY` with `Y=1`.
58    pub(crate) const fn to_xyz(self) -> [f32; 3] {
59        let y_recip = 1. / self.y;
60        [self.x * y_recip, 1., (1. - self.x - self.y) * y_recip]
61    }
62
63    /// Calculate the 3x3 linear Bradford chromatic adaptation matrix from linear sRGB space.
64    ///
65    /// This calculates the matrix going from a reference white of `self` to a reference white of
66    /// `to`.
67    pub(crate) const fn linear_srgb_chromatic_adaptation_matrix(self, to: Self) -> [[f32; 3]; 3] {
68        let bradford_source = matvecmul(&Self::XYZ_TO_BRADFORD, self.to_xyz());
69        let bradford_dest = matvecmul(&Self::XYZ_TO_BRADFORD, to.to_xyz());
70
71        matmatmul(
72            &matdiagmatmul(
73                &Self::BRADFORD_TO_SRGB,
74                [
75                    bradford_dest[0] / bradford_source[0],
76                    bradford_dest[1] / bradford_source[1],
77                    bradford_dest[2] / bradford_source[2],
78                ],
79            ),
80            &Self::SRGB_TO_BRADFORD,
81        )
82    }
83
84    /// `XYZ_to_Bradford * lin_sRGB_to_XYZ`
85    const SRGB_TO_BRADFORD: [[f32; 3]; 3] = [
86        [
87            1_298_421_353. / 3_072_037_500.,
88            172_510_403. / 351_090_000.,
89            32_024_671. / 1_170_300_000.,
90        ],
91        [
92            85_542_113. / 1_536_018_750.,
93            7_089_448_151. / 7_372_890_000.,
94            244_246_729. / 10_532_700_000.,
95        ],
96        [
97            131_355_661. / 6_144_075_000.,
98            71_798_777. / 819_210_000.,
99            3_443_292_119. / 3_510_900_000.,
100        ],
101    ];
102
103    /// `XYZ_to_lin_sRGB * Bradford_to_XYZ`
104    const BRADFORD_TO_SRGB: [[f32; 3]; 3] = [
105        [
106            3_597_831_250_055_000. / 1_417_335_035_684_489.,
107            -1_833_298_161_702_000. / 1_417_335_035_684_489.,
108            -57_038_163_791_000. / 1_417_335_035_684_489.,
109        ],
110        [
111            -4_593_417_841_453_000. / 31_461_687_363_220_151.,
112            35_130_825_086_032_200. / 31_461_687_363_220_151.,
113            -702_492_905_752_400. / 31_461_687_363_220_151.,
114        ],
115        [
116            -191_861_334_350_000. / 4_536_975_728_019_583.,
117            -324_802_409_790_000. / 4_536_975_728_019_583.,
118            4_639_090_845_380_000. / 4_536_975_728_019_583.,
119        ],
120    ];
121
122    const XYZ_TO_BRADFORD: [[f32; 3]; 3] = [
123        [0.8951, 0.2664, -0.1614],
124        [-0.7502, 1.7135, 0.0367],
125        [0.0389, -0.0685, 1.0296],
126    ];
127}