prisma/encoding/
encoded_color.rs

1//! Provides the `EncodedColor` type for storing colors with their encodings.
2
3use super::EncodableColor;
4use crate::channel::{AngularChannelScalar, PosNormalChannelScalar};
5use crate::color_space::{ColorSpace, SpacedColor, WithColorSpace};
6use crate::convert::{FromColor, FromHsi, FromYCbCr};
7use crate::encoding::encode::{ColorEncoding, LinearEncoding, TranscodableColor};
8use crate::hsi::{Hsi, HsiOutOfGamutMode};
9use crate::ycbcr::{YCbCr, YCbCrModel, YCbCrOutOfGamutMode};
10use crate::{Bounded, Broadcast, Color, Color3, Color4, FromTuple, Invert, Lerp, PolarColor};
11use angle::Angle;
12#[cfg(feature = "approx")]
13use approx;
14use num_traits;
15
16use std::fmt;
17use std::ops::{Deref, DerefMut};
18
19/// A color decorated with its encoding. This is the primary way to use encodings.
20///
21/// As most encodings are zero-sized structs except for `GammaEncoding`, there will be no size
22/// penalty for using `EncodedColor`.
23#[derive(Copy, Clone, Debug, PartialEq, Eq)]
24pub struct EncodedColor<C, E> {
25    color: C,
26    encoding: E,
27}
28
29/// A color with a linear encoding
30pub type LinearColor<C> = EncodedColor<C, LinearEncoding>;
31
32impl<C, E> EncodedColor<C, E>
33where
34    C: Color + EncodableColor,
35    E: ColorEncoding,
36{
37    /// Construct a new `EncodedColor` from a color and an encoding.
38    pub fn new(color: C, encoding: E) -> Self {
39        EncodedColor { color, encoding }
40    }
41}
42impl<C, E> EncodedColor<C, E>
43where
44    C: Color,
45    E: ColorEncoding,
46{
47    /// Decompose a `EncodedColor` into it's color and encoding objects
48    pub fn decompose(self) -> (C, E) {
49        (self.color, self.encoding)
50    }
51    /// Returns a reference to the color object
52    pub fn color(&self) -> &C {
53        &self.color
54    }
55    /// Returns a mutable reference to the color object
56    pub fn color_mut(&mut self) -> &mut C {
57        &mut self.color
58    }
59    /// Discard the encoding, returning the bare color object
60    pub fn strip_encoding(self) -> C {
61        self.color
62    }
63    /// Returns a reference to the encoding object
64    pub fn encoding(&self) -> &E {
65        &self.encoding
66    }
67}
68
69impl<C, E> EncodedColor<C, E>
70where
71    E: ColorEncoding,
72    C: TranscodableColor,
73{
74    /// Decode the color, making it linearly encoded
75    ///
76    /// Note: This only is implemented for Rgb. All other encoded colors must convert to Rgb first.
77    pub fn decode(self) -> EncodedColor<C, LinearEncoding> {
78        let decoded_color = self.color.decode_color(&self.encoding);
79        EncodedColor::new(decoded_color, LinearEncoding::new())
80    }
81
82    /// Change the encoding of the color
83    ///
84    /// Note: This only is implemented for Rgb. All other encoded colors must convert to Rgb first.
85    pub fn transcode<Encoder>(self, new_encoding: Encoder) -> EncodedColor<C, Encoder>
86    where
87        Encoder: ColorEncoding,
88    {
89        let decoded_color = self.decode();
90        decoded_color.encode(new_encoding)
91    }
92}
93impl<C> EncodedColor<C, LinearEncoding>
94where
95    C: TranscodableColor,
96{
97    /// Encode a linear RGB color with `encoding`
98    pub fn encode<Encoder>(self, encoding: Encoder) -> EncodedColor<C, Encoder>
99    where
100        Encoder: ColorEncoding,
101    {
102        self.color.encode_color(&encoding).encoded_as(encoding)
103    }
104}
105
106impl<C, E> EncodedColor<C, E>
107where
108    C: Color + Broadcast + EncodableColor,
109    E: ColorEncoding + PartialEq,
110{
111    /// Construct a new `EncodedColor` with all channels set to `value` and with `encoding`
112    pub fn broadcast(value: C::ChannelFormat, encoding: E) -> Self {
113        EncodedColor::new(C::broadcast(value), encoding)
114    }
115}
116
117impl<C, E> EncodedColor<C, E>
118where
119    C: Color + FromTuple + EncodableColor,
120    E: ColorEncoding + PartialEq,
121{
122    /// Construct a new `EncodedColor` from a tuple of channels and an encoding
123    pub fn from_tuple(values: C::ChannelsTuple, encoding: E) -> Self {
124        EncodedColor::new(C::from_tuple(values), encoding)
125    }
126}
127
128impl<T, C, E, S> WithColorSpace<T, C, E, S> for EncodedColor<C, E>
129where
130    C: EncodableColor,
131    S: ColorSpace<T>,
132    E: ColorEncoding,
133    T: num_traits::Float,
134{
135    fn with_color_space(self, space: S) -> SpacedColor<T, C, E, S> {
136        SpacedColor::new(self, space)
137    }
138}
139
140impl<C, E> Color for EncodedColor<C, E>
141where
142    C: Color + EncodableColor,
143    E: ColorEncoding + PartialEq,
144{
145    type Tag = C::Tag;
146    type ChannelsTuple = C::ChannelsTuple;
147
148    fn num_channels() -> u32 {
149        C::num_channels()
150    }
151    fn to_tuple(self) -> Self::ChannelsTuple {
152        self.color.to_tuple()
153    }
154}
155
156impl<C, E> Color3 for EncodedColor<C, E>
157where
158    C: Color3 + EncodableColor,
159    E: ColorEncoding + PartialEq,
160{
161}
162
163impl<C, E> Color4 for EncodedColor<C, E>
164where
165    C: Color4 + EncodableColor,
166    E: ColorEncoding + PartialEq,
167{
168}
169
170impl<C, E> PolarColor for EncodedColor<C, E>
171where
172    C: Color + EncodableColor + PolarColor,
173    E: ColorEncoding + PartialEq,
174{
175    type Angular = C::Angular;
176    type Cartesian = C::Cartesian;
177}
178
179impl<C, E> Lerp for EncodedColor<C, E>
180where
181    C: Color + Lerp + EncodableColor,
182    E: ColorEncoding + PartialEq,
183{
184    type Position = C::Position;
185
186    fn lerp(&self, right: &Self, pos: Self::Position) -> Self {
187        if self.encoding != right.encoding {
188            panic!("Tried to interpolate between two different color encodings")
189        }
190        EncodedColor::new(self.color.lerp(&right.color(), pos), self.encoding.clone())
191    }
192}
193
194impl<C, E> Invert for EncodedColor<C, E>
195where
196    C: Color + Invert + EncodableColor,
197    E: ColorEncoding + PartialEq,
198{
199    fn invert(self) -> Self {
200        EncodedColor::new(self.color.invert(), self.encoding)
201    }
202}
203
204impl<C, E> Bounded for EncodedColor<C, E>
205where
206    C: Color + Bounded + EncodableColor,
207    E: ColorEncoding + PartialEq,
208{
209    fn normalize(self) -> Self {
210        EncodedColor::new(self.color.normalize(), self.encoding)
211    }
212    fn is_normalized(&self) -> bool {
213        self.color.is_normalized()
214    }
215}
216
217impl<C, E> EncodableColor for EncodedColor<C, E>
218where
219    C: EncodableColor,
220    E: ColorEncoding + PartialEq,
221{
222}
223
224impl<C, E> Deref for EncodedColor<C, E>
225where
226    C: EncodableColor,
227    E: ColorEncoding,
228{
229    type Target = C;
230
231    fn deref(&self) -> &C {
232        &self.color
233    }
234}
235impl<C, E> DerefMut for EncodedColor<C, E>
236where
237    C: EncodableColor,
238    E: ColorEncoding,
239{
240    fn deref_mut(&mut self) -> &mut C {
241        &mut self.color
242    }
243}
244
245impl<C, E, C2> FromColor<EncodedColor<C2, E>> for EncodedColor<C, E>
246where
247    C: Color + FromColor<C2> + EncodableColor,
248    E: ColorEncoding,
249    C2: EncodableColor,
250{
251    fn from_color(from: &EncodedColor<C2, E>) -> Self {
252        EncodedColor::new(FromColor::from_color(from.color()), from.encoding.clone())
253    }
254}
255
256impl<C, E, T, A> FromHsi<EncodedColor<Hsi<T, A>, E>> for EncodedColor<C, E>
257where
258    C: Color + EncodableColor + FromHsi<Hsi<T, A>>,
259    E: ColorEncoding,
260    T: PosNormalChannelScalar + num_traits::Float,
261    A: AngularChannelScalar + Angle<Scalar = T>,
262{
263    fn from_hsi(from: &EncodedColor<Hsi<T, A>, E>, out_of_gamut_mode: HsiOutOfGamutMode) -> Self {
264        EncodedColor::new(
265            C::from_hsi(&from.color, out_of_gamut_mode),
266            from.encoding.clone(),
267        )
268    }
269}
270impl<C, E, T, M> FromYCbCr<EncodedColor<YCbCr<T, M>, E>> for EncodedColor<C, E>
271where
272    C: Color + EncodableColor + FromYCbCr<YCbCr<T, M>>,
273    E: ColorEncoding,
274    T: PosNormalChannelScalar + num_traits::Float,
275    M: YCbCrModel<T>,
276{
277    fn from_ycbcr(
278        from: &EncodedColor<YCbCr<T, M>, E>,
279        out_of_gamut_mode: YCbCrOutOfGamutMode,
280    ) -> Self {
281        EncodedColor::new(
282            C::from_ycbcr(&from.color, out_of_gamut_mode),
283            from.encoding.clone(),
284        )
285    }
286}
287
288#[cfg(feature = "approx")]
289impl<C, E> approx::AbsDiffEq for EncodedColor<C, E>
290where
291    C: Color + EncodableColor + approx::AbsDiffEq,
292    E: ColorEncoding + PartialEq,
293{
294    type Epsilon = C::Epsilon;
295
296    fn default_epsilon() -> Self::Epsilon {
297        C::default_epsilon()
298    }
299    fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
300        (self.encoding == other.encoding) && self.color.abs_diff_eq(&other.color, epsilon)
301    }
302}
303#[cfg(feature = "approx")]
304impl<C, E> approx::RelativeEq for EncodedColor<C, E>
305where
306    C: Color + EncodableColor + approx::RelativeEq,
307    E: ColorEncoding + PartialEq,
308{
309    fn default_max_relative() -> Self::Epsilon {
310        C::default_max_relative()
311    }
312    fn relative_eq(
313        &self,
314        other: &Self,
315        epsilon: Self::Epsilon,
316        max_relative: Self::Epsilon,
317    ) -> bool {
318        (self.encoding == other.encoding)
319            && self.color.relative_eq(&other.color, epsilon, max_relative)
320    }
321}
322
323#[cfg(feature = "approx")]
324impl<C, E> approx::UlpsEq for EncodedColor<C, E>
325where
326    C: Color + EncodableColor + approx::UlpsEq,
327    E: ColorEncoding + PartialEq,
328{
329    fn default_max_ulps() -> u32 {
330        C::default_max_ulps()
331    }
332    fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
333        (self.encoding == other.encoding) && self.color.ulps_eq(&other.color, epsilon, max_ulps)
334    }
335}
336
337impl<C, E> fmt::Display for EncodedColor<C, E>
338where
339    C: Color + EncodableColor + fmt::Display,
340    E: ColorEncoding + fmt::Display,
341{
342    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
343        write!(f, "{} as {}", self.color, self.encoding)
344    }
345}
346
347#[cfg(test)]
348mod tests {
349    use super::*;
350    use crate::test;
351    use crate::{Hsv, Rgb};
352    use angle::Deg;
353    use approx::*;
354
355    #[test]
356    fn test_encode_as() {
357        let c1 = Rgb::new(0.5, 0.5, 0.5);
358        let e1 = c1.clone().encoded_as(LinearEncoding {});
359
360        assert_eq!(&c1, e1.color());
361        assert_eq!(e1.encoding(), &LinearEncoding {});
362
363        let e2 = c1.clone().linear();
364
365        assert_eq!(e1, e2);
366
367        let c3 = Rgb::new(0.25, 0.5, 0.75).linear().invert();
368        assert_eq!(c3, Rgb::new(0.75, 0.5, 0.25).linear());
369    }
370
371    #[test]
372    fn test_deref() {
373        let mut e1 = Rgb::new(1.0, 0.0, 0.5).srgb_encoded();
374
375        assert_eq!(e1.red(), 1.0);
376        assert_eq!(e1.green(), 0.0);
377        assert_eq!(e1.blue(), 0.5);
378        assert_eq!(e1.clone().to_tuple(), (1.0, 0.0, 0.5));
379        assert_eq!(&*e1, e1.color());
380
381        *e1.blue_mut() = 0.33;
382        assert_eq!(e1.blue(), 0.33);
383
384        let e2 = Hsv::new(Deg(180.0), 0.5, 0.25).srgb_encoded();
385        assert_eq!(e2.hue(), e2.color().hue());
386        assert_eq!(e2.hue(), Deg(180.0));
387    }
388
389    #[test]
390    fn test_convert() {
391        for color in test::build_hs_test_data() {
392            let rgb = color.rgb.clone().linear();
393            let hsv = color.hsv.clone().linear();
394
395            assert_relative_eq!(
396                EncodedColor::<Hsv<_>, _>::from_color(&rgb),
397                hsv,
398                epsilon = 1e-3
399            );
400        }
401    }
402}