1use crate::{
7 A98Rgb, Aces2065_1, AcesCg, Chromaticity, ColorSpace, ColorSpaceLayout, DisplayP3, Hsl, Hwb,
8 Lab, Lch, LinearSrgb, Missing, Oklab, Oklch, ProphotoRgb, Rec2020, Srgb, XyzD50, XyzD65,
9};
10
11#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
23#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
24#[non_exhaustive]
25#[repr(u8)]
26pub enum ColorSpaceTag {
27 Srgb = 0,
29 LinearSrgb = 1,
31 Lab = 2,
33 Lch = 3,
35 Hsl = 4,
37 Hwb = 5,
39 Oklab = 6,
41 Oklch = 7,
43 DisplayP3 = 8,
45 A98Rgb = 9,
47 ProphotoRgb = 10,
49 Rec2020 = 11,
51 Aces2065_1 = 15,
53 AcesCg = 12,
55 XyzD50 = 13,
57 XyzD65 = 14,
59 }
63
64impl ColorSpaceTag {
65 pub(crate) fn layout(self) -> ColorSpaceLayout {
66 match self {
67 Self::Lch | Self::Oklch => ColorSpaceLayout::HueThird,
68 Self::Hsl | Self::Hwb => ColorSpaceLayout::HueFirst,
69 _ => ColorSpaceLayout::Rectangular,
70 }
71 }
72
73 pub(crate) fn same_analogous(self, other: Self) -> bool {
81 use ColorSpaceTag::*;
82 matches!(
83 (self, other),
84 (
85 Srgb | LinearSrgb
86 | DisplayP3
87 | A98Rgb
88 | ProphotoRgb
89 | Rec2020
90 | Aces2065_1
91 | AcesCg
92 | XyzD50
93 | XyzD65,
94 Srgb | LinearSrgb
95 | DisplayP3
96 | A98Rgb
97 | ProphotoRgb
98 | Rec2020
99 | Aces2065_1
100 | AcesCg
101 | XyzD50
102 | XyzD65
103 ) | (Lab | Oklab, Lab | Oklab)
104 | (Lch | Oklch, Lch | Oklch)
105 )
106 }
107
108 pub(crate) fn l_missing(self, missing: Missing) -> bool {
109 use ColorSpaceTag::*;
110 match self {
111 Lab | Lch | Oklab | Oklch => missing.contains(0),
112 Hsl => missing.contains(2),
113 _ => false,
114 }
115 }
116
117 pub(crate) fn set_l_missing(self, missing: &mut Missing, components: &mut [f32; 4]) {
118 use ColorSpaceTag::*;
119 match self {
120 Lab | Lch | Oklab | Oklch => {
121 missing.insert(0);
122 components[0] = 0.0;
123 }
124 Hsl => {
125 missing.insert(2);
126 components[2] = 0.0;
127 }
128 _ => (),
129 }
130 }
131
132 pub(crate) fn c_missing(self, missing: Missing) -> bool {
133 use ColorSpaceTag::*;
134 match self {
135 Lab | Lch | Oklab | Oklch | Hsl => missing.contains(1),
136 _ => false,
137 }
138 }
139
140 pub(crate) fn set_c_missing(self, missing: &mut Missing, components: &mut [f32; 4]) {
141 use ColorSpaceTag::*;
142 match self {
143 Lab | Lch | Oklab | Oklch | Hsl => {
144 missing.insert(1);
145 components[1] = 0.0;
146 }
147 _ => (),
148 }
149 }
150
151 pub(crate) fn h_missing(self, missing: Missing) -> bool {
152 self.layout()
153 .hue_channel()
154 .is_some_and(|ix| missing.contains(ix))
155 }
156
157 pub(crate) fn set_h_missing(self, missing: &mut Missing, components: &mut [f32; 4]) {
158 if let Some(ix) = self.layout().hue_channel() {
159 missing.insert(ix);
160 components[ix] = 0.0;
161 }
162 }
163
164 pub fn from_linear_srgb(self, rgb: [f32; 3]) -> [f32; 3] {
168 match self {
169 Self::Srgb => Srgb::from_linear_srgb(rgb),
170 Self::LinearSrgb => rgb,
171 Self::Lab => Lab::from_linear_srgb(rgb),
172 Self::Lch => Lch::from_linear_srgb(rgb),
173 Self::Oklab => Oklab::from_linear_srgb(rgb),
174 Self::Oklch => Oklch::from_linear_srgb(rgb),
175 Self::DisplayP3 => DisplayP3::from_linear_srgb(rgb),
176 Self::A98Rgb => A98Rgb::from_linear_srgb(rgb),
177 Self::ProphotoRgb => ProphotoRgb::from_linear_srgb(rgb),
178 Self::Rec2020 => Rec2020::from_linear_srgb(rgb),
179 Self::Aces2065_1 => Aces2065_1::from_linear_srgb(rgb),
180 Self::AcesCg => AcesCg::from_linear_srgb(rgb),
181 Self::XyzD50 => XyzD50::from_linear_srgb(rgb),
182 Self::XyzD65 => XyzD65::from_linear_srgb(rgb),
183 Self::Hsl => Hsl::from_linear_srgb(rgb),
184 Self::Hwb => Hwb::from_linear_srgb(rgb),
185 }
186 }
187
188 pub fn to_linear_srgb(self, src: [f32; 3]) -> [f32; 3] {
192 match self {
193 Self::Srgb => Srgb::to_linear_srgb(src),
194 Self::LinearSrgb => src,
195 Self::Lab => Lab::to_linear_srgb(src),
196 Self::Lch => Lch::to_linear_srgb(src),
197 Self::Oklab => Oklab::to_linear_srgb(src),
198 Self::Oklch => Oklch::to_linear_srgb(src),
199 Self::DisplayP3 => DisplayP3::to_linear_srgb(src),
200 Self::A98Rgb => A98Rgb::to_linear_srgb(src),
201 Self::ProphotoRgb => ProphotoRgb::to_linear_srgb(src),
202 Self::Rec2020 => Rec2020::to_linear_srgb(src),
203 Self::Aces2065_1 => Aces2065_1::to_linear_srgb(src),
204 Self::AcesCg => AcesCg::to_linear_srgb(src),
205 Self::XyzD50 => XyzD50::to_linear_srgb(src),
206 Self::XyzD65 => XyzD65::to_linear_srgb(src),
207 Self::Hsl => Hsl::to_linear_srgb(src),
208 Self::Hwb => Hwb::to_linear_srgb(src),
209 }
210 }
211
212 pub fn convert(self, target: Self, src: [f32; 3]) -> [f32; 3] {
216 match (self, target) {
217 _ if self == target => src,
218 (Self::Oklab, Self::Oklch) | (Self::Lab, Self::Lch) => Oklab::convert::<Oklch>(src),
219 (Self::Oklch, Self::Oklab) | (Self::Lch, Self::Lab) => Oklch::convert::<Oklab>(src),
220 (Self::Srgb, Self::Hsl) => Srgb::convert::<Hsl>(src),
221 (Self::Hsl, Self::Srgb) => Hsl::convert::<Srgb>(src),
222 (Self::Srgb, Self::Hwb) => Srgb::convert::<Hwb>(src),
223 (Self::Hwb, Self::Srgb) => Hwb::convert::<Srgb>(src),
224 (Self::Hsl, Self::Hwb) => Hsl::convert::<Hwb>(src),
225 (Self::Hwb, Self::Hsl) => Hwb::convert::<Hsl>(src),
226 _ => target.from_linear_srgb(self.to_linear_srgb(src)),
227 }
228 }
229
230 pub fn from_linear_srgb_absolute(self, rgb: [f32; 3]) -> [f32; 3] {
237 match self {
238 Self::Srgb => Srgb::from_linear_srgb_absolute(rgb),
239 Self::LinearSrgb => rgb,
240 Self::Lab => Lab::from_linear_srgb_absolute(rgb),
241 Self::Lch => Lch::from_linear_srgb_absolute(rgb),
242 Self::Oklab => Oklab::from_linear_srgb_absolute(rgb),
243 Self::Oklch => Oklch::from_linear_srgb_absolute(rgb),
244 Self::DisplayP3 => DisplayP3::from_linear_srgb_absolute(rgb),
245 Self::A98Rgb => A98Rgb::from_linear_srgb_absolute(rgb),
246 Self::ProphotoRgb => ProphotoRgb::from_linear_srgb_absolute(rgb),
247 Self::Rec2020 => Rec2020::from_linear_srgb_absolute(rgb),
248 Self::Aces2065_1 => Aces2065_1::from_linear_srgb_absolute(rgb),
249 Self::AcesCg => AcesCg::from_linear_srgb_absolute(rgb),
250 Self::XyzD50 => XyzD50::from_linear_srgb_absolute(rgb),
251 Self::XyzD65 => XyzD65::from_linear_srgb_absolute(rgb),
252 Self::Hsl => Hsl::from_linear_srgb_absolute(rgb),
253 Self::Hwb => Hwb::from_linear_srgb_absolute(rgb),
254 }
255 }
256
257 pub fn to_linear_srgb_absolute(self, src: [f32; 3]) -> [f32; 3] {
264 match self {
265 Self::Srgb => Srgb::to_linear_srgb_absolute(src),
266 Self::LinearSrgb => src,
267 Self::Lab => Lab::to_linear_srgb_absolute(src),
268 Self::Lch => Lch::to_linear_srgb_absolute(src),
269 Self::Oklab => Oklab::to_linear_srgb_absolute(src),
270 Self::Oklch => Oklch::to_linear_srgb_absolute(src),
271 Self::DisplayP3 => DisplayP3::to_linear_srgb_absolute(src),
272 Self::A98Rgb => A98Rgb::to_linear_srgb_absolute(src),
273 Self::ProphotoRgb => ProphotoRgb::to_linear_srgb_absolute(src),
274 Self::Rec2020 => Rec2020::to_linear_srgb_absolute(src),
275 Self::Aces2065_1 => Aces2065_1::to_linear_srgb_absolute(src),
276 Self::AcesCg => AcesCg::to_linear_srgb_absolute(src),
277 Self::XyzD50 => XyzD50::to_linear_srgb_absolute(src),
278 Self::XyzD65 => XyzD65::to_linear_srgb_absolute(src),
279 Self::Hsl => Hsl::to_linear_srgb_absolute(src),
280 Self::Hwb => Hwb::to_linear_srgb_absolute(src),
281 }
282 }
283
284 pub fn convert_absolute(self, target: Self, src: [f32; 3]) -> [f32; 3] {
292 match (self, target) {
293 _ if self == target => src,
294 (Self::Oklab, Self::Oklch) | (Self::Lab, Self::Lch) => {
295 Oklab::convert_absolute::<Oklch>(src)
296 }
297 (Self::Oklch, Self::Oklab) | (Self::Lch, Self::Lab) => {
298 Oklch::convert_absolute::<Oklab>(src)
299 }
300 (Self::Srgb, Self::Hsl) => Srgb::convert_absolute::<Hsl>(src),
301 (Self::Hsl, Self::Srgb) => Hsl::convert_absolute::<Srgb>(src),
302 (Self::Srgb, Self::Hwb) => Srgb::convert_absolute::<Hwb>(src),
303 (Self::Hwb, Self::Srgb) => Hwb::convert_absolute::<Srgb>(src),
304 (Self::Hsl, Self::Hwb) => Hsl::convert_absolute::<Hwb>(src),
305 (Self::Hwb, Self::Hsl) => Hwb::convert_absolute::<Hsl>(src),
306 _ => target.from_linear_srgb_absolute(self.to_linear_srgb_absolute(src)),
307 }
308 }
309
310 pub fn chromatically_adapt(
318 self,
319 src: [f32; 3],
320 from: Chromaticity,
321 to: Chromaticity,
322 ) -> [f32; 3] {
323 match self {
324 Self::Srgb => Srgb::chromatically_adapt(src, from, to),
325 Self::LinearSrgb => LinearSrgb::chromatically_adapt(src, from, to),
326 Self::Lab => Lab::chromatically_adapt(src, from, to),
327 Self::Lch => Lch::chromatically_adapt(src, from, to),
328 Self::Oklab => Oklab::chromatically_adapt(src, from, to),
329 Self::Oklch => Oklch::chromatically_adapt(src, from, to),
330 Self::DisplayP3 => DisplayP3::chromatically_adapt(src, from, to),
331 Self::A98Rgb => A98Rgb::chromatically_adapt(src, from, to),
332 Self::ProphotoRgb => ProphotoRgb::chromatically_adapt(src, from, to),
333 Self::Rec2020 => Rec2020::chromatically_adapt(src, from, to),
334 Self::Aces2065_1 => Aces2065_1::chromatically_adapt(src, from, to),
335 Self::AcesCg => AcesCg::chromatically_adapt(src, from, to),
336 Self::XyzD50 => XyzD50::chromatically_adapt(src, from, to),
337 Self::XyzD65 => XyzD65::chromatically_adapt(src, from, to),
338 Self::Hsl => Hsl::chromatically_adapt(src, from, to),
339 Self::Hwb => Hwb::chromatically_adapt(src, from, to),
340 }
341 }
342
343 pub fn scale_chroma(self, src: [f32; 3], scale: f32) -> [f32; 3] {
347 match self {
348 Self::LinearSrgb => LinearSrgb::scale_chroma(src, scale),
349 Self::Oklab | Self::Lab => Oklab::scale_chroma(src, scale),
350 Self::Oklch | Self::Lch | Self::Hsl => Oklch::scale_chroma(src, scale),
351 _ => {
352 let rgb = self.to_linear_srgb(src);
353 let scaled = LinearSrgb::scale_chroma(rgb, scale);
354 self.from_linear_srgb(scaled)
355 }
356 }
357 }
358
359 pub fn clip(self, src: [f32; 3]) -> [f32; 3] {
363 match self {
364 Self::Srgb => Srgb::clip(src),
365 Self::LinearSrgb => LinearSrgb::clip(src),
366 Self::Lab => Lab::clip(src),
367 Self::Lch => Lch::clip(src),
368 Self::Oklab => Oklab::clip(src),
369 Self::Oklch => Oklch::clip(src),
370 Self::DisplayP3 => DisplayP3::clip(src),
371 Self::A98Rgb => A98Rgb::clip(src),
372 Self::ProphotoRgb => ProphotoRgb::clip(src),
373 Self::Rec2020 => Rec2020::clip(src),
374 Self::Aces2065_1 => Aces2065_1::clip(src),
375 Self::AcesCg => AcesCg::clip(src),
376 Self::XyzD50 => XyzD50::clip(src),
377 Self::XyzD65 => XyzD65::clip(src),
378 Self::Hsl => Hsl::clip(src),
379 Self::Hwb => Hwb::clip(src),
380 }
381 }
382}