color/
rgba8.rs

1// Copyright 2024 the Color Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use crate::{AlphaColor, PremulColor, Srgb};
5
6/// A packed representation of sRGB colors.
7///
8/// Encoding sRGB with 8 bits per component is extremely common, as
9/// it is efficient and convenient, even if limited in accuracy and
10/// gamut.
11///
12/// This is not meant to be a general purpose color type and is
13/// intended for use with [`AlphaColor::to_rgba8`] and [`OpaqueColor::to_rgba8`].
14///
15/// For a pre-multiplied packed representation, see [`PremulRgba8`].
16///
17/// [`AlphaColor::to_rgba8`]: crate::AlphaColor::to_rgba8
18/// [`OpaqueColor::to_rgba8`]: crate::OpaqueColor::to_rgba8
19#[derive(Clone, Copy, PartialEq, Eq, Debug)]
20#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
21#[repr(C)]
22pub struct Rgba8 {
23    /// Red component.
24    pub r: u8,
25    /// Green component.
26    pub g: u8,
27    /// Blue component.
28    pub b: u8,
29    /// Alpha component.
30    ///
31    /// Alpha is interpreted as separated alpha.
32    pub a: u8,
33}
34
35impl Rgba8 {
36    /// Returns the color as a `[u8; 4]`.
37    ///
38    /// The color values will be in the order `[r, g, b, a]`.
39    #[must_use]
40    pub const fn to_u8_array(self) -> [u8; 4] {
41        [self.r, self.g, self.b, self.a]
42    }
43
44    /// Convert the `[u8; 4]` byte array into an `Rgba8` color.
45    ///
46    /// The color values must be given in the order `[r, g, b, a]`.
47    #[must_use]
48    pub const fn from_u8_array([r, g, b, a]: [u8; 4]) -> Self {
49        Self { r, g, b, a }
50    }
51
52    /// Returns the color as a little-endian packed value, with `r` the least significant byte and
53    /// `a` the most significant.
54    #[must_use]
55    pub const fn to_u32(self) -> u32 {
56        u32::from_ne_bytes(self.to_u8_array())
57    }
58
59    /// Interpret the little-endian packed value as a color, with `r` the least significant byte
60    /// and `a` the most significant.
61    #[must_use]
62    pub const fn from_u32(packed_bytes: u32) -> Self {
63        Self::from_u8_array(u32::to_ne_bytes(packed_bytes))
64    }
65}
66
67impl From<Rgba8> for AlphaColor<Srgb> {
68    fn from(value: Rgba8) -> Self {
69        Self::from_rgba8(value.r, value.g, value.b, value.a)
70    }
71}
72
73/// A packed representation of pre-multiplied sRGB colors.
74///
75/// Encoding sRGB with 8 bits per component is extremely common, as
76/// it is efficient and convenient, even if limited in accuracy and
77/// gamut.
78///
79/// This is not meant to be a general purpose color type and is
80/// intended for use with [`PremulColor::to_rgba8`].
81///
82/// For a non-pre-multiplied packed representation, see [`Rgba8`].
83///
84/// [`PremulColor::to_rgba8`]: crate::PremulColor::to_rgba8
85#[derive(Clone, Copy, PartialEq, Eq, Debug)]
86#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
87#[repr(C)]
88pub struct PremulRgba8 {
89    /// Red component.
90    pub r: u8,
91    /// Green component.
92    pub g: u8,
93    /// Blue component.
94    pub b: u8,
95    /// Alpha component.
96    pub a: u8,
97}
98
99impl PremulRgba8 {
100    /// Returns the color as a `[u8; 4]`.
101    ///
102    /// The color values will be in the order `[r, g, b, a]`.
103    #[must_use]
104    pub const fn to_u8_array(self) -> [u8; 4] {
105        [self.r, self.g, self.b, self.a]
106    }
107
108    /// Convert the `[u8; 4]` byte array into a `PremulRgba8` color.
109    ///
110    /// The color values must be given in the order `[r, g, b, a]`.
111    #[must_use]
112    pub const fn from_u8_array([r, g, b, a]: [u8; 4]) -> Self {
113        Self { r, g, b, a }
114    }
115
116    /// Returns the color as a little-endian packed value, with `r` the least significant byte and
117    /// `a` the most significant.
118    #[must_use]
119    pub const fn to_u32(self) -> u32 {
120        u32::from_ne_bytes(self.to_u8_array())
121    }
122
123    /// Interpret the little-endian packed value as a color, with `r` the least significant byte
124    /// and `a` the most significant.
125    #[must_use]
126    pub const fn from_u32(packed_bytes: u32) -> Self {
127        Self::from_u8_array(u32::to_ne_bytes(packed_bytes))
128    }
129}
130
131impl From<PremulRgba8> for PremulColor<Srgb> {
132    fn from(value: PremulRgba8) -> Self {
133        Self::from_rgba8(value.r, value.g, value.b, value.a)
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::{PremulRgba8, Rgba8};
140
141    #[test]
142    fn to_u32() {
143        let c = Rgba8 {
144            r: 1,
145            g: 2,
146            b: 3,
147            a: 4,
148        };
149        assert_eq!(0x04030201_u32.to_le(), c.to_u32());
150
151        let p = PremulRgba8 {
152            r: 0xaa,
153            g: 0xbb,
154            b: 0xcc,
155            a: 0xff,
156        };
157        assert_eq!(0xffccbbaa_u32.to_le(), p.to_u32());
158    }
159
160    #[test]
161    fn from_u32() {
162        let c = Rgba8 {
163            r: 1,
164            g: 2,
165            b: 3,
166            a: 4,
167        };
168        assert_eq!(Rgba8::from_u32(0x04030201_u32.to_le()), c);
169
170        let p = PremulRgba8 {
171            r: 0xaa,
172            g: 0xbb,
173            b: 0xcc,
174            a: 0xff,
175        };
176        assert_eq!(PremulRgba8::from_u32(0xffccbbaa_u32.to_le()), p);
177    }
178
179    #[test]
180    fn to_u8_array() {
181        let c = Rgba8 {
182            r: 1,
183            g: 2,
184            b: 3,
185            a: 4,
186        };
187        assert_eq!([1, 2, 3, 4], c.to_u8_array());
188
189        let p = PremulRgba8 {
190            r: 0xaa,
191            g: 0xbb,
192            b: 0xcc,
193            a: 0xff,
194        };
195        assert_eq!([0xaa, 0xbb, 0xcc, 0xff], p.to_u8_array());
196    }
197
198    #[test]
199    fn from_u8_array() {
200        let c = Rgba8 {
201            r: 1,
202            g: 2,
203            b: 3,
204            a: 4,
205        };
206        assert_eq!(Rgba8::from_u8_array([1, 2, 3, 4]), c);
207
208        let p = PremulRgba8 {
209            r: 0xaa,
210            g: 0xbb,
211            b: 0xcc,
212            a: 0xff,
213        };
214        assert_eq!(PremulRgba8::from_u8_array([0xaa, 0xbb, 0xcc, 0xff]), p);
215    }
216
217    #[test]
218    #[cfg(feature = "bytemuck")]
219    fn bytemuck_to_u32() {
220        let c = Rgba8 {
221            r: 1,
222            g: 2,
223            b: 3,
224            a: 4,
225        };
226        assert_eq!(c.to_u32(), bytemuck::cast(c));
227
228        let p = PremulRgba8 {
229            r: 0xaa,
230            g: 0xbb,
231            b: 0xcc,
232            a: 0xff,
233        };
234        assert_eq!(p.to_u32(), bytemuck::cast(p));
235    }
236
237    #[test]
238    #[cfg(feature = "bytemuck")]
239    fn bytemuck_from_u32() {
240        let c = 0x04030201_u32.to_le();
241        assert_eq!(Rgba8::from_u32(c), bytemuck::cast(c));
242
243        let p = 0xffccbbaa_u32.to_le();
244        assert_eq!(PremulRgba8::from_u32(p), bytemuck::cast(p));
245    }
246
247    #[test]
248    #[cfg(feature = "bytemuck")]
249    fn bytemuck_to_u8_array() {
250        let c = Rgba8 {
251            r: 1,
252            g: 2,
253            b: 3,
254            a: 4,
255        };
256        assert_eq!(c.to_u8_array(), bytemuck::cast::<_, [u8; 4]>(c));
257        assert_eq!(c.to_u8_array(), bytemuck::cast::<_, [u8; 4]>(c.to_u32()));
258
259        let p = PremulRgba8 {
260            r: 0xaa,
261            g: 0xbb,
262            b: 0xcc,
263            a: 0xff,
264        };
265        assert_eq!(p.to_u8_array(), bytemuck::cast::<_, [u8; 4]>(p));
266        assert_eq!(p.to_u8_array(), bytemuck::cast::<_, [u8; 4]>(p.to_u32()));
267    }
268
269    #[test]
270    #[cfg(feature = "bytemuck")]
271    fn bytemuck_from_u8_array() {
272        let c = [1, 2, 3, 4];
273        assert_eq!(Rgba8::from_u8_array(c), bytemuck::cast(c));
274
275        let p = [0xaa, 0xbb, 0xcc, 0xff];
276        assert_eq!(PremulRgba8::from_u8_array(p), bytemuck::cast(p));
277    }
278}