Skip to main content

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 round;
25mod srgb;
26#[cfg(test)]
27mod tests;
28mod wcag;
29mod xyzd50;
30mod xyzd65;
31
32pub use a98_rgb::A98Rgb;
33pub use channels::{Channel, PolarLayout, ToAlpha};
34pub use color_space::ColorSpace;
35pub use display_p3::DisplayP3;
36pub use distance::ColorDistance;
37pub use gamut::Gamut;
38pub use hex::Hex;
39pub use hsb::Hsv;
40pub use hsl::Hsl;
41pub use hwb::Hwb;
42pub use lab::Lab;
43pub use lch::Lch;
44pub use linear_rgb::LinearRgb;
45pub use mix::{ColorMix, ColorMixPolar, HueInterpolation, mix_channels};
46pub use named::{Named, ToNamedError};
47pub use oklab::Oklab;
48pub use oklch::Oklch;
49pub use prophoto_rgb::ProphotoRgb;
50pub use rec2020::Rec2020;
51pub use round::PerceptualRound;
52pub use srgb::Srgb;
53pub use wcag::{WcagColorContrast, WcagLevel};
54pub use xyzd50::XyzD50;
55pub use xyzd65::XyzD65;
56
57#[derive(Debug, Clone, Copy, PartialEq)]
58pub enum Color {
59	A98Rgb(A98Rgb),
60	DisplayP3(DisplayP3),
61	Hsv(Hsv),
62	Hsl(Hsl),
63	Hex(Hex),
64	Hwb(Hwb),
65	Lab(Lab),
66	Lch(Lch),
67	LinearRgb(LinearRgb),
68	Named(Named),
69	Oklab(Oklab),
70	Oklch(Oklch),
71	ProphotoRgb(ProphotoRgb),
72	Rec2020(Rec2020),
73	Srgb(Srgb),
74	XyzD50(XyzD50),
75	XyzD65(XyzD65),
76}
77
78impl fmt::Display for Color {
79	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80		match self {
81			Self::A98Rgb(a) => fmt::Display::fmt(a, f),
82			Self::DisplayP3(d) => fmt::Display::fmt(d, f),
83			Self::Hex(h) => fmt::Display::fmt(h, f),
84			Self::Hsv(h) => fmt::Display::fmt(h, f),
85			Self::Hsl(h) => fmt::Display::fmt(h, f),
86			Self::Hwb(h) => fmt::Display::fmt(h, f),
87			Self::Lab(l) => fmt::Display::fmt(l, f),
88			Self::Lch(l) => fmt::Display::fmt(l, f),
89			Self::LinearRgb(l) => fmt::Display::fmt(l, f),
90			Self::Named(n) => fmt::Display::fmt(n, f),
91			Self::Oklab(o) => fmt::Display::fmt(o, f),
92			Self::Oklch(o) => fmt::Display::fmt(o, f),
93			Self::ProphotoRgb(p) => fmt::Display::fmt(p, f),
94			Self::Rec2020(r) => fmt::Display::fmt(r, f),
95			Self::Srgb(s) => fmt::Display::fmt(s, f),
96			Self::XyzD50(x) => fmt::Display::fmt(x, f),
97			Self::XyzD65(x) => fmt::Display::fmt(x, f),
98		}
99	}
100}
101
102impl Color {
103	/// Returns a copy of this colour with a new alpha value (0.0–100.0).
104	///
105	/// For `Named` colours (which are always fully opaque), setting a non-100 alpha converts to `Srgb`. For `Hex`,
106	/// the colour is round-tripped through `Srgb` to apply the new alpha.
107	pub fn with_alpha(self, alpha: f32) -> Self {
108		match self {
109			Color::A98Rgb(mut c) => {
110				c.alpha = alpha;
111				Color::A98Rgb(c)
112			}
113			Color::DisplayP3(mut c) => {
114				c.alpha = alpha;
115				Color::DisplayP3(c)
116			}
117			Color::Hsv(mut c) => {
118				c.alpha = alpha;
119				Color::Hsv(c)
120			}
121			Color::Hsl(mut c) => {
122				c.alpha = alpha;
123				Color::Hsl(c)
124			}
125			Color::Hex(h) => {
126				let mut srgb: Srgb = h.into();
127				srgb.alpha = alpha;
128				Color::Srgb(srgb)
129			}
130			Color::Hwb(mut c) => {
131				c.alpha = alpha;
132				Color::Hwb(c)
133			}
134			Color::Lab(mut c) => {
135				c.alpha = alpha;
136				Color::Lab(c)
137			}
138			Color::Lch(mut c) => {
139				c.alpha = alpha;
140				Color::Lch(c)
141			}
142			Color::LinearRgb(mut c) => {
143				c.alpha = alpha;
144				Color::LinearRgb(c)
145			}
146			Color::Named(n) => {
147				let mut srgb: Srgb = n.into();
148				srgb.alpha = alpha;
149				Color::Srgb(srgb)
150			}
151			Color::Oklab(mut c) => {
152				c.alpha = alpha;
153				Color::Oklab(c)
154			}
155			Color::Oklch(mut c) => {
156				c.alpha = alpha;
157				Color::Oklch(c)
158			}
159			Color::ProphotoRgb(mut c) => {
160				c.alpha = alpha;
161				Color::ProphotoRgb(c)
162			}
163			Color::Rec2020(mut c) => {
164				c.alpha = alpha;
165				Color::Rec2020(c)
166			}
167			Color::Srgb(mut c) => {
168				c.alpha = alpha;
169				Color::Srgb(c)
170			}
171			Color::XyzD50(mut c) => {
172				c.alpha = alpha;
173				Color::XyzD50(c)
174			}
175			Color::XyzD65(mut c) => {
176				c.alpha = alpha;
177				Color::XyzD65(c)
178			}
179		}
180	}
181}
182
183impl ToAlpha for Color {
184	fn to_alpha(&self) -> f32 {
185		match self {
186			Color::A98Rgb(a) => a.to_alpha(),
187			Color::DisplayP3(d) => d.to_alpha(),
188			Color::Hex(h) => h.to_alpha(),
189			Color::Hsv(h) => h.to_alpha(),
190			Color::Hsl(h) => h.to_alpha(),
191			Color::Hwb(h) => h.to_alpha(),
192			Color::Lab(l) => l.to_alpha(),
193			Color::Lch(l) => l.to_alpha(),
194			Color::LinearRgb(l) => l.to_alpha(),
195			Color::Named(n) => n.to_alpha(),
196			Color::Oklab(o) => o.to_alpha(),
197			Color::Oklch(o) => o.to_alpha(),
198			Color::ProphotoRgb(p) => p.to_alpha(),
199			Color::Rec2020(r) => r.to_alpha(),
200			Color::Srgb(s) => s.to_alpha(),
201			Color::XyzD50(x) => x.to_alpha(),
202			Color::XyzD65(x) => x.to_alpha(),
203		}
204	}
205}
206
207impl From<A98Rgb> for Color {
208	fn from(c: A98Rgb) -> Self {
209		Color::A98Rgb(c)
210	}
211}
212impl From<DisplayP3> for Color {
213	fn from(c: DisplayP3) -> Self {
214		Color::DisplayP3(c)
215	}
216}
217impl From<Hsl> for Color {
218	fn from(c: Hsl) -> Self {
219		Color::Hsl(c)
220	}
221}
222impl From<Hwb> for Color {
223	fn from(c: Hwb) -> Self {
224		Color::Hwb(c)
225	}
226}
227impl From<Lab> for Color {
228	fn from(c: Lab) -> Self {
229		Color::Lab(c)
230	}
231}
232impl From<Lch> for Color {
233	fn from(c: Lch) -> Self {
234		Color::Lch(c)
235	}
236}
237impl From<LinearRgb> for Color {
238	fn from(c: LinearRgb) -> Self {
239		Color::LinearRgb(c)
240	}
241}
242impl From<Oklab> for Color {
243	fn from(c: Oklab) -> Self {
244		Color::Oklab(c)
245	}
246}
247impl From<Oklch> for Color {
248	fn from(c: Oklch) -> Self {
249		Color::Oklch(c)
250	}
251}
252impl From<ProphotoRgb> for Color {
253	fn from(c: ProphotoRgb) -> Self {
254		Color::ProphotoRgb(c)
255	}
256}
257impl From<Rec2020> for Color {
258	fn from(c: Rec2020) -> Self {
259		Color::Rec2020(c)
260	}
261}
262impl From<Srgb> for Color {
263	fn from(c: Srgb) -> Self {
264		Color::Srgb(c)
265	}
266}
267impl From<XyzD50> for Color {
268	fn from(c: XyzD50) -> Self {
269		Color::XyzD50(c)
270	}
271}
272impl From<XyzD65> for Color {
273	fn from(c: XyzD65) -> Self {
274		Color::XyzD65(c)
275	}
276}
277
278impl From<Color> for XyzD65 {
279	fn from(value: Color) -> Self {
280		match value {
281			Color::A98Rgb(a) => a.into(),
282			Color::DisplayP3(d) => d.into(),
283			Color::Hex(h) => h.into(),
284			Color::Hsv(h) => h.into(),
285			Color::Hsl(h) => h.into(),
286			Color::Hwb(h) => h.into(),
287			Color::Lab(l) => l.into(),
288			Color::Lch(l) => l.into(),
289			Color::LinearRgb(l) => l.into(),
290			Color::Named(n) => n.into(),
291			Color::Oklab(o) => o.into(),
292			Color::Oklch(o) => o.into(),
293			Color::ProphotoRgb(p) => p.into(),
294			Color::Rec2020(r) => r.into(),
295			Color::Srgb(s) => s.into(),
296			Color::XyzD50(x) => x.into(),
297			Color::XyzD65(x) => x,
298		}
299	}
300}
301
302pub const COLOR_EPSILON: f64 = 0.0072;
303
304pub fn round_dp(f: f64, d: u32) -> f64 {
305	let factor = 10u32.pow(d) as f64;
306	(f * factor).round() / factor
307}