palette/cast/packed.rs
1use core::marker::PhantomData;
2
3use crate::cast::UintCast;
4
5use super::ArrayCast;
6
7/// A color packed into a compact format, such as an unsigned integer.
8///
9/// `Packed` implements [ArrayCast](crate::cast::ArrayCast) and
10/// [UintCast](crate::cast::UintCast) so it can easily be constructed from
11/// slices, arrays and unsigned integers.
12///
13/// ```
14/// // `PackedArgb` is an alias for `Packed<rgb::channels::Argb, P = u32>`.
15/// use palette::{rgb::PackedArgb, cast::UintsAs};
16///
17/// let raw = [0x7F0080u32, 0x60BBCC];
18/// let colors: &[PackedArgb] = raw.uints_as();
19///
20/// assert_eq!(colors.len(), 2);
21/// assert_eq!(colors[0].color, 0x7F0080);
22/// assert_eq!(colors[1].color, 0x60BBCC);
23/// ```
24///
25/// ## Packed Integer Type Represented in `u32`.
26///
27/// A common example of a packed format is when an RGBA color is encoded as a
28/// hexadecimal number (such as `0x7F0080` from above). Two hexadecimal digits
29/// (8-bits) express each value of the Red, Green, Blue, and Alpha components in
30/// the RGBA color.
31///
32/// Note that conversion from float to integer component types in Palette rounds
33/// to nearest even: an `Rgb` component of `0.5` will convert to `0x80`/`128`,
34/// not `0x7F`/`127`.
35///
36/// ```
37/// use approx::assert_relative_eq;
38/// use palette::{Srgb, Srgba};
39/// use palette::rgb::{PackedArgb, PackedRgba};
40///
41/// let packed: PackedArgb = Srgb::new(0.5, 0.0, 0.5).into_format().into();
42/// assert_eq!(0xFF80_0080, packed.color);
43///
44/// let unpacked: Srgba<u8> = PackedRgba::from(0xFFFF_FF80u32).into();
45/// assert_relative_eq!(
46/// Srgba::new(1.0, 1.0, 1.0, 0.5),
47/// unpacked.into_format(),
48/// epsilon = 0.01
49/// );
50///
51/// // By default, `Packed` uses `Argb` order for creating `Rgb` colors to make
52/// // entering 6-digit hex numbers more convenient
53/// let rgb = Srgb::from(0xFF8000);
54/// assert_eq!(Srgb::new(0xFF, 0x80, 0x00), rgb);
55///
56/// let rgba = Srgba::from(0xFF80007F);
57/// assert_eq!(Srgba::new(0xFF, 0x80, 0x00, 0x7F), rgba);
58/// ```
59///
60/// When an `Rgb` type is packed, the alpha value will be `0xFF` in the
61/// corresponding `u32`. Converting from a packed color type back to an `Rgb`
62/// type will disregard the alpha value.
63#[derive(Debug, PartialEq, Eq)]
64#[repr(transparent)]
65pub struct Packed<O, P> {
66 /// The color packed into a type `P`, such as `u32` or `[u8; 4]`.
67 pub color: P,
68
69 /// The channel order for the color components in the packed data. See
70 /// [`ComponentOrder`].
71 pub channel_order: PhantomData<O>,
72}
73
74impl<O, P> Packed<O, P> {
75 /// Transform a color value into a packed memory representation.
76 #[inline]
77 pub fn pack<C>(color: C) -> Self
78 where
79 O: ComponentOrder<C, P>,
80 {
81 Packed {
82 color: O::pack(color),
83 channel_order: PhantomData,
84 }
85 }
86
87 /// Transform a packed color into a regular color value.
88 #[inline]
89 pub fn unpack<C>(self) -> C
90 where
91 O: ComponentOrder<C, P>,
92 {
93 O::unpack(self.color)
94 }
95}
96
97impl<O, P> Copy for Packed<O, P> where P: Copy {}
98
99impl<O, P> Clone for Packed<O, P>
100where
101 P: Clone,
102{
103 #[inline]
104 fn clone(&self) -> Self {
105 Self {
106 color: self.color.clone(),
107 channel_order: PhantomData,
108 }
109 }
110}
111
112// Safety:
113//
114// `Packed` is a transparent wrapper around `[u8; N]`, which fulfills the
115// requirements of `ArrayCast`.
116unsafe impl<O, T, const N: usize> ArrayCast for Packed<O, [T; N]> {
117 type Array = [T; N];
118}
119
120// Safety:
121//
122// `Packed` is a transparent wrapper around `u8`, which fulfills the
123// requirements of `UintCast`.
124unsafe impl<O> UintCast for Packed<O, u8> {
125 type Uint = u8;
126}
127
128// Safety:
129//
130// `Packed` is a transparent wrapper around `u16`, which fulfills the
131// requirements of `UintCast`.
132unsafe impl<O> UintCast for Packed<O, u16> {
133 type Uint = u16;
134}
135
136// Safety:
137//
138// `Packed` is a transparent wrapper around `u32`, which fulfills the
139// requirements of `UintCast`.
140unsafe impl<O> UintCast for Packed<O, u32> {
141 type Uint = u32;
142}
143
144// Safety:
145//
146// `Packed` is a transparent wrapper around `u64`, which fulfills the
147// requirements of `UintCast`.
148unsafe impl<O> UintCast for Packed<O, u64> {
149 type Uint = u64;
150}
151
152// Safety:
153//
154// `Packed` is a transparent wrapper around `u128`, which fulfills the
155// requirements of `UintCast`.
156unsafe impl<O> UintCast for Packed<O, u128> {
157 type Uint = u128;
158}
159
160impl_array_casts!([O, T, const N: usize] Packed<O, [T; N]>, [T; N]);
161impl_uint_casts_self!(Packed<O, P>, P, where Packed<O, P>: UintCast<Uint = P>);
162impl_uint_casts_other!([O] Packed<O, u8>, u8);
163impl_uint_casts_other!([O] Packed<O, u16>, u16);
164impl_uint_casts_other!([O] Packed<O, u32>, u32);
165impl_uint_casts_other!([O] Packed<O, u64>, u64);
166impl_uint_casts_other!([O] Packed<O, u128>, u128);
167
168#[cfg(feature = "bytemuck")]
169unsafe impl<O, P> bytemuck::Zeroable for Packed<O, P> where P: bytemuck::Zeroable {}
170#[cfg(feature = "bytemuck")]
171unsafe impl<O: 'static, P> bytemuck::Pod for Packed<O, P> where P: bytemuck::Pod {}
172
173/// Packs and unpacks color types with some component order.
174///
175/// As an example, RGBA channels may be ordered as `ABGR`, `ARGB`, `BGRA`, or
176/// `RGBA`.
177pub trait ComponentOrder<C, P> {
178 /// Combine the components of a color into the packed format.
179 fn pack(color: C) -> P;
180
181 /// Split the packed color into its separate components.
182 fn unpack(packed: P) -> C;
183}
184
185impl<C, T> ComponentOrder<C, u8> for T
186where
187 T: ComponentOrder<C, [u8; 1]>,
188{
189 #[inline]
190 fn pack(color: C) -> u8 {
191 let [packed] = T::pack(color);
192 packed
193 }
194
195 #[inline]
196 fn unpack(packed: u8) -> C {
197 T::unpack([packed])
198 }
199}
200
201impl<C, T> ComponentOrder<C, u16> for T
202where
203 T: ComponentOrder<C, [u8; 2]>,
204{
205 #[inline]
206 fn pack(color: C) -> u16 {
207 u16::from_be_bytes(T::pack(color))
208 }
209
210 #[inline]
211 fn unpack(packed: u16) -> C {
212 T::unpack(packed.to_be_bytes())
213 }
214}
215
216impl<C, T> ComponentOrder<C, u32> for T
217where
218 T: ComponentOrder<C, [u8; 4]>,
219{
220 #[inline]
221 fn pack(color: C) -> u32 {
222 u32::from_be_bytes(T::pack(color))
223 }
224
225 #[inline]
226 fn unpack(packed: u32) -> C {
227 T::unpack(packed.to_be_bytes())
228 }
229}
230
231impl<C, T> ComponentOrder<C, u64> for T
232where
233 T: ComponentOrder<C, [u8; 8]>,
234{
235 #[inline]
236 fn pack(color: C) -> u64 {
237 u64::from_be_bytes(T::pack(color))
238 }
239
240 #[inline]
241 fn unpack(packed: u64) -> C {
242 T::unpack(packed.to_be_bytes())
243 }
244}
245
246impl<C, T> ComponentOrder<C, u128> for T
247where
248 T: ComponentOrder<C, [u8; 16]>,
249{
250 #[inline]
251 fn pack(color: C) -> u128 {
252 u128::from_be_bytes(T::pack(color))
253 }
254
255 #[inline]
256 fn unpack(packed: u128) -> C {
257 T::unpack(packed.to_be_bytes())
258 }
259}