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 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}