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}