1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use crate::Clamp;

#[cfg(feature = "alloc")]
use crate::cast::{self, ArrayCast};

use super::FromColorUnclamped;

///A trait for converting one color from another, in a possibly lossy way.
///
/// `U: FromColor<T>` is implemented for every type `U: FromColorUnclamped<T> +
/// Clamp`, as well as for `Vec<T>` and `Box<[T]>` where `T` and `U` have the
/// same memory layout.
///
/// See [`FromColorUnclamped`](crate::convert::FromColorUnclamped) for a
/// lossless version of this trait. See
/// [`TryFromColor`](crate::convert::TryFromColor) for a trait that gives an
/// error when the result is out of bounds.
///
/// # The Difference Between FromColor and From
///
/// The conversion traits, including `FromColor`, were added to gain even more
/// flexibility than what `From` and the other standard library traits can give.
/// There are a few subtle, but important, differences in their semantics:
///
/// * `FromColor` and `IntoColor` are allowed to be lossy, meaning converting `A
///   -> B -> A` may result in a different value than the original. This applies
///   to `A -> A` as well.
/// * `From<Self>` and `Into<Self>` are blanket implemented, while
///   `FromColor<Self>` and `IntoColor<Self>` have to be manually implemented.
///   This allows additional flexibility, such as allowing implementing
///   `FromColor<Rgb<S2, T>> for Rgb<S1, T>`.
/// * Implementing `FromColorUnclamped`,
///   [`IsWithinBounds`](crate::IsWithinBounds) and [`Clamp`] is enough to get
///   all the other conversion traits, while `From` and `Into` would not be
///   possible to blanket implement in the same way. This also reduces the work
///   that needs to be done by macros.
///
/// See the [`convert`](crate::convert) module for how to implement
/// `FromColorUnclamped` for custom colors.
pub trait FromColor<T>: Sized {
    /// Convert from T with values clamped to the color defined bounds.
    ///
    /// ```
    /// use palette::{IsWithinBounds, FromColor, Lch, Srgb};
    ///
    /// let rgb = Srgb::from_color(Lch::new(50.0f32, 100.0, -175.0));
    /// assert!(rgb.is_within_bounds());
    /// ```
    #[must_use]
    fn from_color(t: T) -> Self;
}

impl<T, U> FromColor<T> for U
where
    U: FromColorUnclamped<T> + Clamp,
{
    #[inline]
    fn from_color(t: T) -> Self {
        Self::from_color_unclamped(t).clamp()
    }
}

#[cfg(feature = "alloc")]
impl<T, U> FromColor<alloc::vec::Vec<T>> for alloc::vec::Vec<U>
where
    T: ArrayCast,
    U: ArrayCast<Array = T::Array> + FromColor<T>,
{
    /// Convert all colors in place, without reallocating.
    ///
    /// ```
    /// use palette::{convert::FromColor, SaturateAssign, Srgb, Lch};
    ///
    /// let srgb = vec![Srgb::new(0.8f32, 1.0, 0.2), Srgb::new(0.9, 0.1, 0.3)];
    /// let mut lch = Vec::<Lch>::from_color(srgb);
    ///
    /// lch.saturate_assign(0.1);
    ///
    /// let srgb = Vec::<Srgb>::from_color(lch);
    /// ```
    #[inline]
    fn from_color(color: alloc::vec::Vec<T>) -> Self {
        cast::map_vec_in_place(color, U::from_color)
    }
}

#[cfg(feature = "alloc")]
impl<T, U> FromColor<alloc::boxed::Box<[T]>> for alloc::boxed::Box<[U]>
where
    T: ArrayCast,
    U: ArrayCast<Array = T::Array> + FromColor<T>,
{
    /// Convert all colors in place, without reallocating.
    ///
    /// ```
    /// use palette::{convert::FromColor, SaturateAssign, Srgb, Lch};
    ///
    /// let srgb = vec![Srgb::new(0.8f32, 1.0, 0.2), Srgb::new(0.9, 0.1, 0.3)].into_boxed_slice();
    /// let mut lch = Box::<[Lch]>::from_color(srgb);
    ///
    /// lch.saturate_assign(0.1);
    ///
    /// let srgb = Box::<[Srgb]>::from_color(lch);
    /// ```
    #[inline]
    fn from_color(color: alloc::boxed::Box<[T]>) -> Self {
        cast::map_slice_box_in_place(color, U::from_color)
    }
}

/// A trait for converting a color into another, in a possibly lossy way.
///
/// `U: IntoColor<T>` is implemented for every type `T: FromColor<U>`.
///
/// See [`FromColor`](crate::convert::FromColor) for more details.
pub trait IntoColor<T>: Sized {
    /// Convert into T with values clamped to the color defined bounds
    ///
    /// ```
    /// use palette::{IsWithinBounds, IntoColor, Lch, Srgb};
    ///
    /// let rgb: Srgb = Lch::new(50.0, 100.0, -175.0).into_color();
    /// assert!(rgb.is_within_bounds());
    /// ```
    #[must_use]
    fn into_color(self) -> T;
}

impl<T, U> IntoColor<U> for T
where
    U: FromColor<T>,
{
    #[inline]
    fn into_color(self) -> U {
        U::from_color(self)
    }
}