palette/convert/from_into_color.rs
1use crate::Clamp;
2
3#[cfg(feature = "alloc")]
4use crate::cast::{self, ArrayCast};
5
6use super::FromColorUnclamped;
7
8///A trait for converting one color from another, in a possibly lossy way.
9///
10/// `U: FromColor<T>` is implemented for every type `U: FromColorUnclamped<T> +
11/// Clamp`, as well as for `Vec<T>` and `Box<[T]>` where `T` and `U` have the
12/// same memory layout.
13///
14/// See [`FromColorUnclamped`](crate::convert::FromColorUnclamped) for a
15/// lossless version of this trait. See
16/// [`TryFromColor`](crate::convert::TryFromColor) for a trait that gives an
17/// error when the result is out of bounds.
18///
19/// # The Difference Between FromColor and From
20///
21/// The conversion traits, including `FromColor`, were added to gain even more
22/// flexibility than what `From` and the other standard library traits can give.
23/// There are a few subtle, but important, differences in their semantics:
24///
25/// * `FromColor` and `IntoColor` are allowed to be lossy, meaning converting `A
26/// -> B -> A` may result in a different value than the original. This applies
27/// to `A -> A` as well.
28/// * `From<Self>` and `Into<Self>` are blanket implemented, while
29/// `FromColor<Self>` and `IntoColor<Self>` have to be manually implemented.
30/// This allows additional flexibility, such as allowing implementing
31/// `FromColor<Rgb<S2, T>> for Rgb<S1, T>`.
32/// * Implementing `FromColorUnclamped`,
33/// [`IsWithinBounds`](crate::IsWithinBounds) and [`Clamp`] is enough to get
34/// all the other conversion traits, while `From` and `Into` would not be
35/// possible to blanket implement in the same way. This also reduces the work
36/// that needs to be done by macros.
37///
38/// See the [`convert`](crate::convert) module for how to implement
39/// `FromColorUnclamped` for custom colors.
40pub trait FromColor<T>: Sized {
41 /// Convert from T with values clamped to the color defined bounds.
42 ///
43 /// ```
44 /// use palette::{IsWithinBounds, FromColor, Lch, Srgb};
45 ///
46 /// let rgb = Srgb::from_color(Lch::new(50.0f32, 100.0, -175.0));
47 /// assert!(rgb.is_within_bounds());
48 /// ```
49 #[must_use]
50 fn from_color(t: T) -> Self;
51}
52
53impl<T, U> FromColor<T> for U
54where
55 U: FromColorUnclamped<T> + Clamp,
56{
57 #[inline]
58 fn from_color(t: T) -> Self {
59 Self::from_color_unclamped(t).clamp()
60 }
61}
62
63#[cfg(feature = "alloc")]
64impl<T, U> FromColor<alloc::vec::Vec<T>> for alloc::vec::Vec<U>
65where
66 T: ArrayCast,
67 U: ArrayCast<Array = T::Array> + FromColor<T>,
68{
69 /// Convert all colors in place, without reallocating.
70 ///
71 /// ```
72 /// use palette::{convert::FromColor, SaturateAssign, Srgb, Lch};
73 ///
74 /// let srgb = vec![Srgb::new(0.8f32, 1.0, 0.2), Srgb::new(0.9, 0.1, 0.3)];
75 /// let mut lch = Vec::<Lch>::from_color(srgb);
76 ///
77 /// lch.saturate_assign(0.1);
78 ///
79 /// let srgb = Vec::<Srgb>::from_color(lch);
80 /// ```
81 #[inline]
82 fn from_color(color: alloc::vec::Vec<T>) -> Self {
83 cast::map_vec_in_place(color, U::from_color)
84 }
85}
86
87#[cfg(feature = "alloc")]
88impl<T, U> FromColor<alloc::boxed::Box<[T]>> for alloc::boxed::Box<[U]>
89where
90 T: ArrayCast,
91 U: ArrayCast<Array = T::Array> + FromColor<T>,
92{
93 /// Convert all colors in place, without reallocating.
94 ///
95 /// ```
96 /// use palette::{convert::FromColor, SaturateAssign, Srgb, Lch};
97 ///
98 /// let srgb = vec![Srgb::new(0.8f32, 1.0, 0.2), Srgb::new(0.9, 0.1, 0.3)].into_boxed_slice();
99 /// let mut lch = Box::<[Lch]>::from_color(srgb);
100 ///
101 /// lch.saturate_assign(0.1);
102 ///
103 /// let srgb = Box::<[Srgb]>::from_color(lch);
104 /// ```
105 #[inline]
106 fn from_color(color: alloc::boxed::Box<[T]>) -> Self {
107 cast::map_slice_box_in_place(color, U::from_color)
108 }
109}
110
111/// A trait for converting a color into another, in a possibly lossy way.
112///
113/// `U: IntoColor<T>` is implemented for every type `T: FromColor<U>`.
114///
115/// See [`FromColor`](crate::convert::FromColor) for more details.
116pub trait IntoColor<T>: Sized {
117 /// Convert into T with values clamped to the color defined bounds
118 ///
119 /// ```
120 /// use palette::{IsWithinBounds, IntoColor, Lch, Srgb};
121 ///
122 /// let rgb: Srgb = Lch::new(50.0, 100.0, -175.0).into_color();
123 /// assert!(rgb.is_within_bounds());
124 /// ```
125 #[must_use]
126 fn into_color(self) -> T;
127}
128
129impl<T, U> IntoColor<U> for T
130where
131 U: FromColor<T>,
132{
133 #[inline]
134 fn into_color(self) -> U {
135 U::from_color(self)
136 }
137}