palette/convert/
from_into_color_mut.rs

1use core::{
2    marker::PhantomData,
3    ops::{Deref, DerefMut},
4};
5
6use crate::cast::{self, ArrayCast};
7
8use super::{FromColor, FromColorUnclampedMut, FromColorUnclampedMutGuard, IntoColor};
9
10/// Temporarily convert colors in place.
11///
12/// It allows colors to be converted without using more additional memory than
13/// what is necessary for the conversion, itself. The conversion will however
14/// have to be reverted at some point, since the memory space is borrowed and
15/// has to be restored to its original format. This is enforced by a scope guard
16/// that does the opposite conversion when it's dropped.
17///
18/// See also [`IntoColorMut`] and [`FromColorUnclampedMut`].
19///
20/// ```
21/// use palette::{FromColorMut, ShiftHueAssign, Srgb, Hsv};
22///
23/// let mut rgb = [
24///     Srgb::new(1.0, 0.0, 0.0),
25///     Srgb::new(0.0, 1.0, 0.0),
26///     Srgb::new(0.0, 0.0, 1.0),
27/// ];
28///
29/// {
30///     let mut hsv = <[Hsv]>::from_color_mut(&mut rgb);
31///
32///     // All of the colors in `rgb` have been converted to `Hsv`:
33///     assert_eq!(
34///         *hsv,
35///         [
36///             Hsv::new(0.0, 1.0, 1.0),
37///             Hsv::new(120.0, 1.0, 1.0),
38///             Hsv::new(240.0, 1.0, 1.0),
39///         ]
40///     );
41///
42///     hsv.shift_hue_assign(60.0);
43///
44/// } // The guard is dropped here and the colors are restored to `Srgb`.
45///
46/// // Notice how the colors in `rgb` have changed:
47/// assert_eq!(
48///     rgb,
49///     [
50///         Srgb::new(1.0, 1.0, 0.0),
51///         Srgb::new(0.0, 1.0, 1.0),
52///         Srgb::new(1.0, 0.0, 1.0),
53///     ]
54/// );
55/// ```
56///
57/// The scope guard, [`FromColorMutGuard`], has a few extra methods that can
58/// make multiple conversion steps more efficient. One of those is
59/// [`FromColorMutGuard::then_into_color_mut`], which works like
60/// [`IntoColorMut::into_color_mut`], but does not add an extra step when
61/// restoring to the original color type. This example will convert `Srgb → Hsv
62/// → Hsl → Srgb` instead of `Srgb → Hsv → Hsl → Hsv → Srgb`:
63///
64/// ```
65/// use palette::{FromColorMut, ShiftHueAssign, LightenAssign, Srgb, Hsv, Hsl};
66///
67/// let mut rgb = [
68///     Srgb::new(1.0, 0.0, 0.0),
69///     Srgb::new(0.0, 1.0, 0.0),
70///     Srgb::new(0.0, 0.0, 1.0),
71/// ];
72///
73/// {
74///     let mut hsv = <[Hsv]>::from_color_mut(&mut rgb);
75///     hsv.shift_hue_assign(60.0);
76///
77///     let mut hsl = hsv.then_into_color_mut::<[Hsl]>();
78///     hsl.lighten_assign(0.5);
79///
80/// } // `then_into_color_mut` makes the guard restore directly to `Srgb` here.
81///
82/// // Notice how the colors in `rgb` have changed:
83/// assert_eq!(
84///     rgb,
85///     [
86///         Srgb::new(1.0, 1.0, 0.5),
87///         Srgb::new(0.5, 1.0, 1.0),
88///         Srgb::new(1.0, 0.5, 1.0),
89///     ]
90/// );
91/// ```
92///
93/// # Note
94///
95/// The reused memory space could end up with unexpected values if the
96/// conversion panics or if the scope guard's `drop` function doesn't run. The
97/// default implementations of `FromColorMut` uses [`ArrayCast`], which is only
98/// implemented for color types that can safely accept and recover from any
99/// value. Other color types will have to provide their own implementations that
100/// can handle this case.
101pub trait FromColorMut<T>
102where
103    T: ?Sized + FromColorMut<Self>,
104{
105    /// Temporarily convert from another color type in place.
106    ///
107    /// This reuses the memory space, and the returned scope guard will restore
108    /// the converted colors to their original type when it's dropped.
109    #[must_use]
110    fn from_color_mut(color: &mut T) -> FromColorMutGuard<Self, T>;
111}
112
113impl<T, U> FromColorMut<U> for T
114where
115    T: FromColor<U> + ArrayCast + Clone,
116    U: FromColor<T> + ArrayCast<Array = T::Array> + Clone,
117{
118    #[inline]
119    fn from_color_mut(color: &mut U) -> FromColorMutGuard<Self, U> {
120        let color_clone = color.clone();
121
122        let result: &mut T = cast::from_array_mut(cast::into_array_mut(color));
123
124        *result = color_clone.into_color();
125
126        FromColorMutGuard {
127            current: Some(result),
128            original: PhantomData,
129        }
130    }
131}
132
133impl<T, U> FromColorMut<[U]> for [T]
134where
135    T: FromColorMut<U> + ArrayCast + ?Sized,
136    U: FromColorMut<T> + ArrayCast<Array = T::Array> + ?Sized,
137{
138    #[inline]
139    fn from_color_mut(colors: &mut [U]) -> FromColorMutGuard<Self, [U]> {
140        for color in &mut *colors {
141            // Forgetting the guard leaves the colors in the converted state.
142            core::mem::forget(T::from_color_mut(color));
143        }
144
145        FromColorMutGuard {
146            current: Some(cast::from_array_slice_mut(cast::into_array_slice_mut(
147                colors,
148            ))),
149            original: PhantomData,
150        }
151    }
152}
153
154/// Temporarily convert colors in place. The `Into` counterpart to
155/// [`FromColorMut`].
156///
157/// See [`FromColorMut`] for more details and examples.
158///
159/// ```
160/// use palette::{IntoColorMut, ShiftHueAssign, Srgb, Hsv};
161///
162/// let mut rgb = [
163///     Srgb::new(1.0, 0.0, 0.0),
164///     Srgb::new(0.0, 1.0, 0.0),
165///     Srgb::new(0.0, 0.0, 1.0),
166/// ];
167///
168/// {
169///     let hsv: &mut [Hsv] = &mut rgb.into_color_mut(); // The guard is coerced into a slice.
170///
171///     // All of the colors in `rgb` have been converted to `Hsv`:
172///     assert_eq!(
173///         hsv,
174///         [
175///             Hsv::new(0.0, 1.0, 1.0),
176///             Hsv::new(120.0, 1.0, 1.0),
177///             Hsv::new(240.0, 1.0, 1.0),
178///         ]
179///     );
180///
181///     hsv.shift_hue_assign(60.0);
182///
183/// } // The guard is dropped here and the colors are restored to `Srgb`.
184///
185/// // Notice how the colors in `rgb` have changed:
186/// assert_eq!(
187///     rgb,
188///     [
189///         Srgb::new(1.0, 1.0, 0.0),
190///         Srgb::new(0.0, 1.0, 1.0),
191///         Srgb::new(1.0, 0.0, 1.0),
192///     ]
193/// );
194/// ```
195pub trait IntoColorMut<T>: FromColorMut<T>
196where
197    T: ?Sized + FromColorMut<Self>,
198{
199    /// Temporarily convert to another color type in place.
200    ///
201    /// This reuses the memory space, and the returned scope guard will restore
202    /// the converted colors to their original type when it's dropped.
203    #[allow(clippy::wrong_self_convention)]
204    #[must_use]
205    fn into_color_mut(&mut self) -> FromColorMutGuard<T, Self>;
206}
207
208impl<T, U> IntoColorMut<T> for U
209where
210    T: FromColorMut<U> + ?Sized,
211    U: FromColorMut<T> + ?Sized,
212{
213    #[inline]
214    fn into_color_mut(&mut self) -> FromColorMutGuard<T, Self> {
215        T::from_color_mut(self)
216    }
217}
218
219/// A scope guard that restores the guarded colors to their original type when
220/// dropped.
221#[repr(transparent)]
222pub struct FromColorMutGuard<'a, T, U>
223where
224    T: FromColorMut<U> + ?Sized,
225    U: FromColorMut<T> + ?Sized,
226{
227    // `Option` lets us move out without triggering `Drop`.
228    pub(super) current: Option<&'a mut T>,
229    pub(super) original: PhantomData<&'a mut U>,
230}
231
232impl<'a, T, U> FromColorMutGuard<'a, T, U>
233where
234    T: FromColorMut<U> + ?Sized,
235    U: FromColorMut<T> + ?Sized,
236{
237    /// Convert the colors to another type and replace this guard.
238    ///
239    /// The colors will not be converted back to the current color type before
240    /// being restored, as opposed to when `into_color_mut` is called. Instead,
241    /// they are restored directly to their original type.
242    #[must_use]
243    #[inline]
244    pub fn then_into_color_mut<C>(mut self) -> FromColorMutGuard<'a, C, U>
245    where
246        T: FromColorMut<C>,
247        C: FromColorMut<U> + FromColorMut<T> + ?Sized,
248        U: FromColorMut<C>,
249    {
250        FromColorMutGuard {
251            current: self
252                .current
253                .take()
254                .map(C::from_color_mut)
255                .and_then(|mut guard| guard.current.take()),
256            original: PhantomData,
257        }
258    }
259
260    /// Convert the colors to another type, without clamping, and replace this
261    /// guard.
262    ///
263    /// The colors will not be converted back to the current color type before
264    /// being restored, as opposed to when `into_color_unclamped_mut` is called.
265    /// Instead, they are restored directly to their original type.
266    #[must_use]
267    #[inline]
268    pub fn then_into_color_unclamped_mut<C>(mut self) -> FromColorUnclampedMutGuard<'a, C, U>
269    where
270        T: FromColorUnclampedMut<C>,
271        C: FromColorUnclampedMut<U> + FromColorUnclampedMut<T> + ?Sized,
272        U: FromColorUnclampedMut<C>,
273    {
274        FromColorUnclampedMutGuard {
275            current: self
276                .current
277                .take()
278                .map(C::from_color_unclamped_mut)
279                .and_then(|mut guard| guard.current.take()),
280            original: PhantomData,
281        }
282    }
283
284    /// Replace this guard with a guard that does not clamp the colors after restoring.
285    #[must_use]
286    #[inline]
287    pub fn into_unclamped_guard(mut self) -> FromColorUnclampedMutGuard<'a, T, U>
288    where
289        T: FromColorUnclampedMut<U>,
290        U: FromColorUnclampedMut<T>,
291    {
292        FromColorUnclampedMutGuard {
293            current: self.current.take(),
294            original: PhantomData,
295        }
296    }
297
298    /// Immediately restore the colors to their original type.
299    ///
300    /// This happens automatically when the guard is dropped, but there may be
301    /// situations where it's better or more convenient to call `restore`
302    /// directly.
303    #[inline]
304    pub fn restore(mut self) -> &'a mut U {
305        let restored = self
306            .current
307            .take()
308            .map(U::from_color_mut)
309            .and_then(|mut guard| guard.current.take());
310
311        if let Some(restored) = restored {
312            restored
313        } else {
314            unreachable!()
315        }
316    }
317}
318
319impl<'a, T, U> Deref for FromColorMutGuard<'a, T, U>
320where
321    T: FromColorMut<U> + ?Sized,
322    U: FromColorMut<T> + ?Sized,
323{
324    type Target = T;
325
326    #[inline]
327    fn deref(&self) -> &Self::Target {
328        if let Some(current) = self.current.as_ref() {
329            current
330        } else {
331            unreachable!()
332        }
333    }
334}
335
336impl<'a, T, U> DerefMut for FromColorMutGuard<'a, T, U>
337where
338    T: FromColorMut<U> + ?Sized,
339    U: FromColorMut<T> + ?Sized,
340{
341    #[inline]
342    fn deref_mut(&mut self) -> &mut Self::Target {
343        if let Some(current) = self.current.as_mut() {
344            current
345        } else {
346            unreachable!()
347        }
348    }
349}
350
351impl<'a, T, U> Drop for FromColorMutGuard<'a, T, U>
352where
353    T: FromColorMut<U> + ?Sized,
354    U: FromColorMut<T> + ?Sized,
355{
356    #[inline]
357    fn drop(&mut self) {
358        // Forgetting the guard leaves the colors in the converted state.
359        core::mem::forget(self.current.take().map(U::from_color_mut));
360    }
361}