Skip to main content

chromashift/
xyz.rs

1use crate::{Alpha, Chromaticity, Matrix3, Number, ToAlpha};
2use core::fmt;
3
4macro_rules! xyz_color {
5	($(#[$meta:meta])* $ty:ident, $css:literal, $white:ident) => {
6		$(#[$meta])*
7		/// The components are:
8		/// - X - a number between 0.0 and 100.0
9		/// - Y - a number between 0.0 and 100.0
10		/// - Z - a number between 0.0 and 100.0
11		/// - Alpha - a number between 0.0 and 100.0
12		#[derive(Debug, Clone, Copy, PartialEq)]
13		pub struct $ty {
14			pub x: f64,
15			pub y: f64,
16			pub z: f64,
17			pub alpha: f32,
18		}
19
20		impl $ty {
21			/// The white point these tristimulus values are relative to.
22			pub const WHITE: Chromaticity = Chromaticity::$white;
23
24			pub fn new(x: f64, y: f64, z: f64, alpha: f32) -> Self {
25				Self { x, y, z, alpha: alpha.clamp(0.0, 100.0) }
26			}
27		}
28
29		impl ToAlpha for $ty {
30			fn to_alpha(&self) -> f32 {
31				self.alpha
32			}
33		}
34
35		impl fmt::Display for $ty {
36			fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37				let Self { x, y, z, alpha } = *self;
38				write!(f, concat!("color(", $css, " {} {} {}{})"), Number(x / 100.0), Number(y / 100.0), Number(z / 100.0), Alpha(alpha))
39			}
40		}
41	};
42}
43
44xyz_color!(
45	/// A colour expressed as X, Y and Z values in the CIE XYZ tristimulus colour space, with an explicit D50 white
46	/// point.
47	XyzD50,
48	"xyz-d50",
49	D50
50);
51
52xyz_color!(
53	/// A colour expressed as X, Y and Z values in the CIE XYZ tristimulus colour space, with an explicit D65 white
54	/// point.
55	XyzD65,
56	"xyz-d65",
57	D65
58);
59
60const D50_TO_D65: Matrix3 = XyzD50::WHITE.adapt_to(XyzD65::WHITE);
61const D65_TO_D50: Matrix3 = XyzD65::WHITE.adapt_to(XyzD50::WHITE);
62
63impl From<XyzD50> for XyzD65 {
64	fn from(value: XyzD50) -> Self {
65		let XyzD50 { x, y, z, alpha } = value;
66		let [x, y, z] = D50_TO_D65.transform([x, y, z]);
67		XyzD65::new(x, y, z, alpha)
68	}
69}
70
71impl From<XyzD65> for XyzD50 {
72	fn from(value: XyzD65) -> Self {
73		let XyzD65 { x, y, z, alpha } = value;
74		let [x, y, z] = D65_TO_D50.transform([x, y, z]);
75		XyzD50::new(x, y, z, alpha)
76	}
77}