1use core::marker::PhantomData;
4
5use crate::encoding::{Encoded, Encoding, Linear};
6use crate::fixed::Q0_16;
7use crate::space::{ColorSpace, Perceptual};
8
9#[cfg(feature = "f32")]
10pub use crate::color_f32::ColorF32;
11
12pub struct Color<S: ColorSpace, E: Encoding> {
14 pub ch: [Q0_16; 3],
16 _pd: PhantomData<fn() -> (S, E)>,
17}
18
19impl<S: ColorSpace, E: Encoding> Copy for Color<S, E> {}
20
21impl<S: ColorSpace, E: Encoding> Clone for Color<S, E> {
22 fn clone(&self) -> Self {
23 *self
24 }
25}
26
27impl<S: ColorSpace, E: Encoding> PartialEq for Color<S, E> {
28 fn eq(&self, other: &Self) -> bool {
29 self.ch == other.ch
30 }
31}
32
33impl<S: ColorSpace, E: Encoding> Eq for Color<S, E> {}
34
35impl<S: ColorSpace, E: Encoding> core::fmt::Debug for Color<S, E> {
36 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
37 f.debug_struct("Color").field("ch", &self.ch).finish()
38 }
39}
40
41impl<S: ColorSpace, E: Encoding> Color<S, E> {
42 #[must_use]
44 pub const fn new(ch: [Q0_16; 3]) -> Self {
45 Self {
46 ch,
47 _pd: PhantomData,
48 }
49 }
50
51 #[cfg(feature = "f32")]
53 #[must_use]
54 pub const fn to_f32(self) -> crate::ColorF32<S, E> {
55 let [c0, c1, c2] = self.ch;
56 crate::ColorF32::new([c0.to_f32(), c1.to_f32(), c2.to_f32()])
57 }
58}
59
60impl<S: ColorSpace> Color<S, Linear> {
61 #[must_use]
63 pub const fn encode(self) -> Color<S, Encoded> {
64 Color::new(self.ch)
65 }
66
67 #[must_use]
71 pub const fn lerp(self, other: Self, t: Q0_16) -> Self {
72 let [a0, a1, a2] = self.ch;
73 let [b0, b1, b2] = other.ch;
74 Color::new([
75 crate::arith::lerp_q0_16(a0, b0, t),
76 crate::arith::lerp_q0_16(a1, b1, t),
77 crate::arith::lerp_q0_16(a2, b2, t),
78 ])
79 }
80}
81
82impl<S: ColorSpace> Color<S, Encoded> {
83 #[must_use]
85 pub const fn decode(self) -> Color<S, Linear> {
86 Color::new(self.ch)
87 }
88}
89
90impl<S: ColorSpace + Perceptual> Color<S, Encoded> {
91 #[must_use]
93 pub const fn lerp(self, other: Self, t: Q0_16) -> Self {
94 let [a0, a1, a2] = self.ch;
95 let [b0, b1, b2] = other.ch;
96 Color::new([
97 crate::arith::lerp_q0_16(a0, b0, t),
98 crate::arith::lerp_q0_16(a1, b1, t),
99 crate::arith::lerp_q0_16(a2, b2, t),
100 ])
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use crate::space::Srgb;
108
109 #[test]
110 fn constructs_linear_and_encoded_srgb() {
111 let lin = Color::<Srgb, Linear>::new(Q0_16::array_from_raw([1, 2, 3]));
112 let enc = Color::<Srgb, Encoded>::new(Q0_16::array_from_raw([4, 5, 6]));
113 assert_eq!(lin.ch, Q0_16::array_from_raw([1, 2, 3]));
114 assert_eq!(enc.ch, Q0_16::array_from_raw([4, 5, 6]));
115 let _ = lin.encode();
116 let _ = enc.decode();
117 }
118
119 fn assert_send_sync<T: Send + Sync>() {}
120
121 #[test]
122 fn color_matrix_gain_are_send_sync() {
123 assert_send_sync::<Color<Srgb, Linear>>();
124 assert_send_sync::<Color<Srgb, Encoded>>();
125 assert_send_sync::<crate::matrix::Matrix3<Srgb, Srgb>>();
126 assert_send_sync::<crate::gain::Gain<Srgb>>();
127 }
128}