chromashift/
lib.rs

1#![deny(warnings)]
2
3use core::fmt;
4mod a98_rgb;
5mod channels;
6mod color_space;
7mod conversion;
8mod display_p3;
9mod distance;
10mod gamut;
11mod hex;
12mod hsb;
13mod hsl;
14mod hwb;
15mod lab;
16mod lch;
17mod linear_rgb;
18mod mix;
19mod named;
20mod oklab;
21mod oklch;
22mod prophoto_rgb;
23mod rec2020;
24mod srgb;
25#[cfg(test)]
26mod tests;
27mod wcag;
28mod xyzd50;
29mod xyzd65;
30
31pub use a98_rgb::A98Rgb;
32pub use channels::ToAlpha;
33pub use color_space::ColorSpace;
34pub use display_p3::DisplayP3;
35pub use distance::ColorDistance;
36pub use gamut::Gamut;
37pub use hex::Hex;
38pub use hsb::Hsv;
39pub use hsl::Hsl;
40pub use hwb::Hwb;
41pub use lab::Lab;
42pub use lch::Lch;
43pub use linear_rgb::LinearRgb;
44pub use mix::{ColorMix, ColorMixPolar, HueInterpolation};
45pub use named::{Named, ToNamedError};
46pub use oklab::Oklab;
47pub use oklch::Oklch;
48pub use prophoto_rgb::ProphotoRgb;
49pub use rec2020::Rec2020;
50pub use srgb::Srgb;
51pub use wcag::{WcagColorContrast, WcagLevel};
52pub use xyzd50::XyzD50;
53pub use xyzd65::XyzD65;
54
55#[derive(Debug, Clone, Copy, PartialEq)]
56pub enum Color {
57	A98Rgb(A98Rgb),
58	DisplayP3(DisplayP3),
59	Hsv(Hsv),
60	Hsl(Hsl),
61	Hex(Hex),
62	Hwb(Hwb),
63	Lab(Lab),
64	Lch(Lch),
65	LinearRgb(LinearRgb),
66	Named(Named),
67	Oklab(Oklab),
68	Oklch(Oklch),
69	ProphotoRgb(ProphotoRgb),
70	Rec2020(Rec2020),
71	Srgb(Srgb),
72	XyzD50(XyzD50),
73	XyzD65(XyzD65),
74}
75
76impl fmt::Display for Color {
77	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78		match self {
79			Self::A98Rgb(a) => fmt::Display::fmt(a, f),
80			Self::DisplayP3(d) => fmt::Display::fmt(d, f),
81			Self::Hex(h) => fmt::Display::fmt(h, f),
82			Self::Hsv(h) => fmt::Display::fmt(h, f),
83			Self::Hsl(h) => fmt::Display::fmt(h, f),
84			Self::Hwb(h) => fmt::Display::fmt(h, f),
85			Self::Lab(l) => fmt::Display::fmt(l, f),
86			Self::Lch(l) => fmt::Display::fmt(l, f),
87			Self::LinearRgb(l) => fmt::Display::fmt(l, f),
88			Self::Named(n) => fmt::Display::fmt(n, f),
89			Self::Oklab(o) => fmt::Display::fmt(o, f),
90			Self::Oklch(o) => fmt::Display::fmt(o, f),
91			Self::ProphotoRgb(p) => fmt::Display::fmt(p, f),
92			Self::Rec2020(r) => fmt::Display::fmt(r, f),
93			Self::Srgb(s) => fmt::Display::fmt(s, f),
94			Self::XyzD50(x) => fmt::Display::fmt(x, f),
95			Self::XyzD65(x) => fmt::Display::fmt(x, f),
96		}
97	}
98}
99
100impl Color {
101	/// Returns a copy of this colour with a new alpha value (0.0–100.0).
102	///
103	/// For `Named` colours (which are always fully opaque), setting a non-100 alpha converts to `Srgb`. For `Hex`,
104	/// the colour is round-tripped through `Srgb` to apply the new alpha.
105	pub fn with_alpha(self, alpha: f32) -> Self {
106		match self {
107			Color::A98Rgb(mut c) => {
108				c.alpha = alpha;
109				Color::A98Rgb(c)
110			}
111			Color::DisplayP3(mut c) => {
112				c.alpha = alpha;
113				Color::DisplayP3(c)
114			}
115			Color::Hsv(mut c) => {
116				c.alpha = alpha;
117				Color::Hsv(c)
118			}
119			Color::Hsl(mut c) => {
120				c.alpha = alpha;
121				Color::Hsl(c)
122			}
123			Color::Hex(h) => {
124				let mut srgb: Srgb = h.into();
125				srgb.alpha = alpha;
126				Color::Srgb(srgb)
127			}
128			Color::Hwb(mut c) => {
129				c.alpha = alpha;
130				Color::Hwb(c)
131			}
132			Color::Lab(mut c) => {
133				c.alpha = alpha;
134				Color::Lab(c)
135			}
136			Color::Lch(mut c) => {
137				c.alpha = alpha;
138				Color::Lch(c)
139			}
140			Color::LinearRgb(mut c) => {
141				c.alpha = alpha;
142				Color::LinearRgb(c)
143			}
144			Color::Named(n) => {
145				let mut srgb: Srgb = n.into();
146				srgb.alpha = alpha;
147				Color::Srgb(srgb)
148			}
149			Color::Oklab(mut c) => {
150				c.alpha = alpha;
151				Color::Oklab(c)
152			}
153			Color::Oklch(mut c) => {
154				c.alpha = alpha;
155				Color::Oklch(c)
156			}
157			Color::ProphotoRgb(mut c) => {
158				c.alpha = alpha;
159				Color::ProphotoRgb(c)
160			}
161			Color::Rec2020(mut c) => {
162				c.alpha = alpha;
163				Color::Rec2020(c)
164			}
165			Color::Srgb(mut c) => {
166				c.alpha = alpha;
167				Color::Srgb(c)
168			}
169			Color::XyzD50(mut c) => {
170				c.alpha = alpha;
171				Color::XyzD50(c)
172			}
173			Color::XyzD65(mut c) => {
174				c.alpha = alpha;
175				Color::XyzD65(c)
176			}
177		}
178	}
179}
180
181impl ToAlpha for Color {
182	fn to_alpha(&self) -> f32 {
183		match self {
184			Color::A98Rgb(a) => a.to_alpha(),
185			Color::DisplayP3(d) => d.to_alpha(),
186			Color::Hex(h) => h.to_alpha(),
187			Color::Hsv(h) => h.to_alpha(),
188			Color::Hsl(h) => h.to_alpha(),
189			Color::Hwb(h) => h.to_alpha(),
190			Color::Lab(l) => l.to_alpha(),
191			Color::Lch(l) => l.to_alpha(),
192			Color::LinearRgb(l) => l.to_alpha(),
193			Color::Named(n) => n.to_alpha(),
194			Color::Oklab(o) => o.to_alpha(),
195			Color::Oklch(o) => o.to_alpha(),
196			Color::ProphotoRgb(p) => p.to_alpha(),
197			Color::Rec2020(r) => r.to_alpha(),
198			Color::Srgb(s) => s.to_alpha(),
199			Color::XyzD50(x) => x.to_alpha(),
200			Color::XyzD65(x) => x.to_alpha(),
201		}
202	}
203}
204
205impl From<Color> for XyzD65 {
206	fn from(value: Color) -> Self {
207		match value {
208			Color::A98Rgb(a) => a.into(),
209			Color::DisplayP3(d) => d.into(),
210			Color::Hex(h) => h.into(),
211			Color::Hsv(h) => h.into(),
212			Color::Hsl(h) => h.into(),
213			Color::Hwb(h) => h.into(),
214			Color::Lab(l) => l.into(),
215			Color::Lch(l) => l.into(),
216			Color::LinearRgb(l) => l.into(),
217			Color::Named(n) => n.into(),
218			Color::Oklab(o) => o.into(),
219			Color::Oklch(o) => o.into(),
220			Color::ProphotoRgb(p) => p.into(),
221			Color::Rec2020(r) => r.into(),
222			Color::Srgb(s) => s.into(),
223			Color::XyzD50(x) => x.into(),
224			Color::XyzD65(x) => x,
225		}
226	}
227}
228
229pub const COLOR_EPSILON: f64 = 0.0072;
230
231pub(crate) fn round_dp(f: f64, d: u32) -> f64 {
232	let factor = 10u32.pow(d) as f64;
233	(f * factor).round() / factor
234}