1use core::fmt;
2mod a98_rgb;
3mod channels;
4mod color_space;
5mod conversion;
6mod display_p3;
7mod distance;
8mod gamut;
9mod hex;
10mod hsb;
11mod hsl;
12mod hwb;
13mod lab;
14mod lch;
15mod linear_rgb;
16mod mix;
17mod named;
18mod oklab;
19mod oklch;
20mod prophoto_rgb;
21mod rec2020;
22mod round;
23mod srgb;
24#[cfg(test)]
25mod tests;
26mod wcag;
27mod xyzd50;
28mod xyzd65;
29
30pub use a98_rgb::A98Rgb;
31pub use channels::{Channel, PolarLayout, ToAlpha};
32pub use color_space::ColorSpace;
33pub use display_p3::DisplayP3;
34pub use distance::ColorDistance;
35pub use gamut::Gamut;
36pub use hex::Hex;
37pub use hsb::Hsv;
38pub use hsl::Hsl;
39pub use hwb::Hwb;
40pub use lab::Lab;
41pub use lch::Lch;
42pub use linear_rgb::LinearRgb;
43pub use mix::{ColorMix, ColorMixPolar, HueInterpolation, mix_channels};
44pub use named::{Named, ToNamedError};
45pub use oklab::Oklab;
46pub use oklch::Oklch;
47pub use prophoto_rgb::ProphotoRgb;
48pub use rec2020::Rec2020;
49pub use round::PerceptualRound;
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 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<A98Rgb> for Color {
206 fn from(c: A98Rgb) -> Self {
207 Color::A98Rgb(c)
208 }
209}
210impl From<DisplayP3> for Color {
211 fn from(c: DisplayP3) -> Self {
212 Color::DisplayP3(c)
213 }
214}
215impl From<Hsl> for Color {
216 fn from(c: Hsl) -> Self {
217 Color::Hsl(c)
218 }
219}
220impl From<Hwb> for Color {
221 fn from(c: Hwb) -> Self {
222 Color::Hwb(c)
223 }
224}
225impl From<Lab> for Color {
226 fn from(c: Lab) -> Self {
227 Color::Lab(c)
228 }
229}
230impl From<Lch> for Color {
231 fn from(c: Lch) -> Self {
232 Color::Lch(c)
233 }
234}
235impl From<LinearRgb> for Color {
236 fn from(c: LinearRgb) -> Self {
237 Color::LinearRgb(c)
238 }
239}
240impl From<Oklab> for Color {
241 fn from(c: Oklab) -> Self {
242 Color::Oklab(c)
243 }
244}
245impl From<Oklch> for Color {
246 fn from(c: Oklch) -> Self {
247 Color::Oklch(c)
248 }
249}
250impl From<ProphotoRgb> for Color {
251 fn from(c: ProphotoRgb) -> Self {
252 Color::ProphotoRgb(c)
253 }
254}
255impl From<Rec2020> for Color {
256 fn from(c: Rec2020) -> Self {
257 Color::Rec2020(c)
258 }
259}
260impl From<Srgb> for Color {
261 fn from(c: Srgb) -> Self {
262 Color::Srgb(c)
263 }
264}
265impl From<XyzD50> for Color {
266 fn from(c: XyzD50) -> Self {
267 Color::XyzD50(c)
268 }
269}
270impl From<XyzD65> for Color {
271 fn from(c: XyzD65) -> Self {
272 Color::XyzD65(c)
273 }
274}
275
276impl From<Color> for XyzD65 {
277 fn from(value: Color) -> Self {
278 match value {
279 Color::A98Rgb(a) => a.into(),
280 Color::DisplayP3(d) => d.into(),
281 Color::Hex(h) => h.into(),
282 Color::Hsv(h) => h.into(),
283 Color::Hsl(h) => h.into(),
284 Color::Hwb(h) => h.into(),
285 Color::Lab(l) => l.into(),
286 Color::Lch(l) => l.into(),
287 Color::LinearRgb(l) => l.into(),
288 Color::Named(n) => n.into(),
289 Color::Oklab(o) => o.into(),
290 Color::Oklch(o) => o.into(),
291 Color::ProphotoRgb(p) => p.into(),
292 Color::Rec2020(r) => r.into(),
293 Color::Srgb(s) => s.into(),
294 Color::XyzD50(x) => x.into(),
295 Color::XyzD65(x) => x,
296 }
297 }
298}
299
300pub const COLOR_EPSILON: f64 = 0.0072;
301
302pub fn round_dp(f: f64, d: u32) -> f64 {
303 let factor = 10u32.pow(d) as f64;
304 (f * factor).round() / factor
305}