colorutils_rs/
oklch.rs

1/*
2 * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved.
3 * //
4 * // Use of this source code is governed by a BSD-style
5 * // license that can be found in the LICENSE file.
6 */
7use crate::{EuclideanDistance, Oklab, Rgb, TaxicabDistance, TransferFunction};
8use erydanos::{eatan2f, ehypotf, Cosine, Sine};
9use num_traits::Pow;
10use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
11
12/// Represents *Oklch* colorspace
13#[repr(C)]
14#[derive(Copy, Clone, PartialOrd, PartialEq)]
15pub struct Oklch {
16    /// Lightness
17    pub l: f32,
18    /// Chroma
19    pub c: f32,
20    /// Hue
21    pub h: f32,
22}
23
24impl Oklch {
25    /// Creates new instance
26    #[inline]
27    pub fn new(l: f32, c: f32, h: f32) -> Oklch {
28        Oklch { l, c, h }
29    }
30
31    /// Converts *Rgb* into *Oklch*
32    ///
33    /// # Arguments
34    /// `transfer_function` - Transfer function into linear colorspace and its inverse
35    #[inline]
36    pub fn from_rgb(rgb: Rgb<u8>, transfer_function: TransferFunction) -> Oklch {
37        let oklab = rgb.to_oklab(transfer_function);
38        Oklch::from_oklab(oklab)
39    }
40
41    /// Converts Linear [Rgb] into [Oklch]
42    ///
43    /// # Arguments
44    /// `transfer_function` - Transfer function into linear colorspace and its inverse
45    #[inline]
46    pub fn from_linear_rgb(rgb: Rgb<f32>) -> Oklch {
47        let oklab = Oklab::from_linear_rgb(rgb);
48        Oklch::from_oklab(oklab)
49    }
50
51    /// Converts [Oklch] into [Rgb]
52    ///
53    /// # Arguments
54    /// `transfer_function` - Transfer function into linear colorspace and its inverse
55    #[inline]
56    pub fn to_rgb(&self, transfer_function: TransferFunction) -> Rgb<u8> {
57        let oklab = self.to_oklab();
58        oklab.to_rgb(transfer_function)
59    }
60
61    /// Converts [Oklch] into linear [Rgb]
62    #[inline]
63    pub fn to_linear_rgb(&self) -> Rgb<f32> {
64        let oklab = self.to_oklab();
65        oklab.to_linear_rgb()
66    }
67
68    /// Converts *Oklab* to *Oklch*
69    #[inline]
70    pub fn from_oklab(oklab: Oklab) -> Oklch {
71        let chroma = ehypotf(oklab.b, oklab.a);
72        let hue = eatan2f(oklab.b, oklab.a);
73        Oklch::new(oklab.l, chroma, hue)
74    }
75
76    /// Converts *Oklch* to *Oklab*
77    #[inline]
78    pub fn to_oklab(&self) -> Oklab {
79        let l = self.l;
80        let a = self.c * self.h.ecos();
81        let b = self.c * self.h.esin();
82        Oklab::new(l, a, b)
83    }
84}
85
86impl EuclideanDistance for Oklch {
87    #[inline]
88    fn euclidean_distance(&self, other: Self) -> f32 {
89        let dl = self.l - other.l;
90        let dc = self.c - other.c;
91        let dh = self.h - other.h;
92        (dl * dl + dc * dc + dh * dh).sqrt()
93    }
94}
95
96impl TaxicabDistance for Oklch {
97    #[inline]
98    fn taxicab_distance(&self, other: Self) -> f32 {
99        let dl = self.l - other.l;
100        let dc = self.c - other.c;
101        let dh = self.h - other.h;
102        dl.abs() + dc.abs() + dh.abs()
103    }
104}
105
106impl Add<Oklch> for Oklch {
107    type Output = Oklch;
108
109    #[inline]
110    fn add(self, rhs: Self) -> Oklch {
111        Oklch::new(self.l + rhs.l, self.c + rhs.c, self.h + rhs.h)
112    }
113}
114
115impl Add<f32> for Oklch {
116    type Output = Oklch;
117
118    #[inline]
119    fn add(self, rhs: f32) -> Oklch {
120        Oklch::new(self.l + rhs, self.c + rhs, self.h + rhs)
121    }
122}
123
124impl AddAssign<Oklch> for Oklch {
125    #[inline]
126    fn add_assign(&mut self, rhs: Oklch) {
127        self.l += rhs.l;
128        self.c += rhs.c;
129        self.h += rhs.h;
130    }
131}
132
133impl AddAssign<f32> for Oklch {
134    #[inline]
135    fn add_assign(&mut self, rhs: f32) {
136        self.l += rhs;
137        self.c += rhs;
138        self.h += rhs;
139    }
140}
141
142impl Mul<f32> for Oklch {
143    type Output = Oklch;
144
145    #[inline]
146    fn mul(self, rhs: f32) -> Self::Output {
147        Oklch::new(self.l * rhs, self.c * rhs, self.h * rhs)
148    }
149}
150
151impl Mul<Oklch> for Oklch {
152    type Output = Oklch;
153
154    #[inline]
155    fn mul(self, rhs: Oklch) -> Self::Output {
156        Oklch::new(self.l * rhs.l, self.c * rhs.c, self.h * rhs.h)
157    }
158}
159
160impl MulAssign<f32> for Oklch {
161    #[inline]
162    fn mul_assign(&mut self, rhs: f32) {
163        self.l *= rhs;
164        self.c *= rhs;
165        self.h *= rhs;
166    }
167}
168
169impl MulAssign<Oklch> for Oklch {
170    #[inline]
171    fn mul_assign(&mut self, rhs: Oklch) {
172        self.l *= rhs.l;
173        self.c *= rhs.c;
174        self.h *= rhs.h;
175    }
176}
177
178impl Sub<f32> for Oklch {
179    type Output = Oklch;
180
181    #[inline]
182    fn sub(self, rhs: f32) -> Self::Output {
183        Oklch::new(self.l - rhs, self.c - rhs, self.h - rhs)
184    }
185}
186
187impl Sub<Oklch> for Oklch {
188    type Output = Oklch;
189
190    #[inline]
191    fn sub(self, rhs: Oklch) -> Self::Output {
192        Oklch::new(self.l - rhs.l, self.c - rhs.c, self.h - rhs.h)
193    }
194}
195
196impl SubAssign<f32> for Oklch {
197    #[inline]
198    fn sub_assign(&mut self, rhs: f32) {
199        self.l -= rhs;
200        self.c -= rhs;
201        self.h -= rhs;
202    }
203}
204
205impl SubAssign<Oklch> for Oklch {
206    #[inline]
207    fn sub_assign(&mut self, rhs: Oklch) {
208        self.l -= rhs.l;
209        self.c -= rhs.c;
210        self.h -= rhs.h;
211    }
212}
213
214impl Div<f32> for Oklch {
215    type Output = Oklch;
216
217    #[inline]
218    fn div(self, rhs: f32) -> Self::Output {
219        Oklch::new(self.l / rhs, self.c / rhs, self.h / rhs)
220    }
221}
222
223impl Div<Oklch> for Oklch {
224    type Output = Oklch;
225
226    #[inline]
227    fn div(self, rhs: Oklch) -> Self::Output {
228        Oklch::new(self.l / rhs.l, self.c / rhs.c, self.h / rhs.h)
229    }
230}
231
232impl DivAssign<f32> for Oklch {
233    #[inline]
234    fn div_assign(&mut self, rhs: f32) {
235        self.l /= rhs;
236        self.c /= rhs;
237        self.h /= rhs;
238    }
239}
240
241impl DivAssign<Oklch> for Oklch {
242    #[inline]
243    fn div_assign(&mut self, rhs: Oklch) {
244        self.l /= rhs.l;
245        self.c /= rhs.c;
246        self.h /= rhs.h;
247    }
248}
249
250impl Neg for Oklch {
251    type Output = Oklch;
252
253    fn neg(self) -> Self::Output {
254        Oklch::new(-self.l, -self.c, -self.h)
255    }
256}
257
258impl Pow<f32> for Oklch {
259    type Output = Oklch;
260
261    #[inline]
262    fn pow(self, rhs: f32) -> Self::Output {
263        Oklch::new(self.l.powf(rhs), self.c.powf(rhs), self.h.powf(rhs))
264    }
265}
266
267impl Pow<Oklch> for Oklch {
268    type Output = Oklch;
269
270    #[inline]
271    fn pow(self, rhs: Oklch) -> Self::Output {
272        Oklch::new(self.l.powf(rhs.l), self.c.powf(rhs.c), self.h.powf(rhs.h))
273    }
274}
275
276impl Oklch {
277    #[inline]
278    pub fn sqrt(&self) -> Oklch {
279        Oklch::new(
280            if self.l < 0. { 0. } else { self.l.sqrt() },
281            if self.c < 0. { 0. } else { self.c.sqrt() },
282            if self.h < 0. { 0. } else { self.h.sqrt() },
283        )
284    }
285
286    #[inline]
287    pub fn cbrt(&self) -> Oklch {
288        Oklch::new(self.l.cbrt(), self.c.cbrt(), self.h.cbrt())
289    }
290}