devela/media/color/rgb/impls/
u8.rs

1// devela::media::color::rgb::impls::u8
2
3use crate::{Norm, Rgb8, Rgb16, Rgba8, Rgba16};
4#[cfg(doc)]
5use crate::{Rgb, Rgba};
6
7#[cfg(feature = "_float_f32")]
8use crate::{RgbF32, RgbaF32};
9#[cfg(feature = "_float_f64")]
10use crate::{RgbF64, RgbaF64};
11
12#[allow(missing_docs)]
13#[rustfmt::skip]
14impl Rgb8 {
15    /// New `Rgb<u8>`.
16    pub const fn new(r: u8, g: u8, b: u8) -> Rgb8 { Self { c: [r, g, b] } }
17    /// The red component.
18    pub const fn red(self) -> u8 { self.c[0] }
19    pub const fn r(self) -> u8 { self.c[0] }
20    /// The green component.
21    pub const fn green(self) -> u8 { self.c[1] }
22    pub const fn g(self) -> u8 { self.c[1] }
23    /// The blue component.
24    pub const fn blue(self) -> u8 { self.c[2] }
25    pub const fn b(self) -> u8 { self.c[2] }
26}
27
28/// # General conversions
29impl Rgb8 {
30    /// Create from an array.
31    pub const fn from_array(c: [u8; 3]) -> Rgb8 {
32        Rgb8 { c }
33    }
34    /// Convert to an array.
35    pub const fn as_array(self) -> [u8; 3] {
36        self.c
37    }
38    /// Create from a tuple.
39    pub const fn from_tuple(c: (u8, u8, u8)) -> Rgb8 {
40        Rgb8::new(c.0, c.1, c.2)
41    }
42    /// Convert to a tuple.
43    pub const fn to_tuple(self) -> (u8, u8, u8) {
44        (self.r(), self.g(), self.b())
45    }
46
47    /* Rgba8 */
48
49    /// Create from [`Rgba<u8>`].
50    ///
51    /// Loses the alpha channel.
52    pub const fn from_rgba8(c: Rgba8) -> Rgb8 {
53        Rgb8::new(c.r(), c.g(), c.b())
54    }
55    /// Convert to [`Rgba<u8>`].
56    ///
57    /// Adds the given `alpha` channel.
58    pub const fn to_rgba8(self, alpha: u8) -> Rgba8 {
59        Rgba8::new(self.r(), self.g(), self.b(), alpha)
60    }
61
62    /* packed u32 */
63
64    /// Create from a packed `u32` in `0xRRGGBB` format.
65    ///
66    /// Any bits above `0x00FF_FFFF` are ignored.
67    pub const fn from_rgb8_packed(packed: u32) -> Rgb8 {
68        Rgb8::from_array([
69            ((packed >> 16) & 0xFF) as u8,
70            ((packed >> 8) & 0xFF) as u8,
71            (packed & 0xFF) as u8,
72        ])
73    }
74    /// Convert to a packed `u32` in `0xRRGGBB` format.
75    pub const fn to_rgb8_packed(self) -> u32 {
76        ((self.r() as u32) << 16) | ((self.g() as u32) << 8) | (self.b() as u32)
77    }
78
79    /// Create from a packed `u32` in `0xRRGGBBAA` format, discarding alpha.
80    pub const fn from_rgba8_packed(packed: u32) -> Rgb8 {
81        Rgb8::from_array([
82            ((packed >> 24) & 0xFF) as u8,
83            ((packed >> 16) & 0xFF) as u8,
84            ((packed >> 8) & 0xFF) as u8,
85        ])
86    }
87    /// Convert to a packed `u32` in `0xRRGGBBAA` format, with the given `alpha`.
88    pub const fn to_rgba8_packed(self, alpha: u8) -> u32 {
89        ((self.r() as u32) << 24)
90            | ((self.g() as u32) << 16)
91            | ((self.b() as u32) << 8)
92            | (alpha as u32)
93    }
94
95    /* u16 */
96
97    /// Convert to `Rgba<u8>` by scaling each component proportionally.
98    pub fn from_rgb16(from: Rgb16) -> Rgb8 {
99        Rgb8::new(
100            ((from.c[0] + 128) / 257) as u8, // Rounding via +128
101            ((from.c[1] + 128) / 257) as u8,
102            ((from.c[2] + 128) / 257) as u8,
103        )
104    }
105    /// Create from `Rgb<u16>` by scaling each component proportionally.
106    pub fn to_rgb16(self) -> Rgb16 {
107        Rgb16::new(
108            (self.c[0] as u16) * 257, // 255 * 257 = 65535
109            (self.c[1] as u16) * 257,
110            (self.c[2] as u16) * 257,
111        )
112    }
113}
114#[rustfmt::skip]
115impl From<Rgba8> for Rgb8 { fn from(from: Rgba8) -> Rgb8 { Rgb8::from_rgba8(from) } }
116#[rustfmt::skip]
117impl From<Rgb16> for Rgb8 { fn from(from: Rgb16) -> Rgb8 { Rgb8::from_rgb16(from) } }
118
119/// # `f32` conversions
120#[cfg(feature = "_float_f32")]
121#[cfg_attr(nightly_doc, doc(cfg(feature = "_float_f32")))]
122impl Rgb8 {
123    /// Create from [`Rgb<f32>`].
124    pub const fn from_rgb_f32(c: RgbF32) -> Rgb8 {
125        Rgb8::new(Norm::f32_to_u8(c.r()), Norm::f32_to_u8(c.g()), Norm::f32_to_u8(c.b()))
126    }
127    /// Convert to [`Rgb<f32>`].
128    pub const fn to_rgb_f32(self) -> RgbF32 {
129        RgbF32::new(Norm::u8_to_f32(self.r()), Norm::u8_to_f32(self.g()), Norm::u8_to_f32(self.b()))
130    }
131
132    /// Create from [`Rgba<f32>`].
133    ///
134    /// Loses the alpha channel.
135    pub const fn from_rgba_f32(c: RgbaF32) -> Rgb8 {
136        Rgb8::new(Norm::f32_to_u8(c.r()), Norm::f32_to_u8(c.g()), Norm::f32_to_u8(c.b()))
137    }
138    /// Convert to [`Rgba<f32>`].
139    ///
140    /// Adds the given `alpha` channel.
141    pub const fn to_rgba_f32(self, alpha: u8) -> RgbaF32 {
142        RgbaF32::new(
143            Norm::u8_to_f32(self.r()),
144            Norm::u8_to_f32(self.g()),
145            Norm::u8_to_f32(self.b()),
146            Norm::u8_to_f32(alpha),
147        )
148    }
149}
150
151/// # `f64` conversions
152#[cfg(feature = "_float_f64")]
153#[cfg_attr(nightly_doc, doc(cfg(feature = "_float_f64")))]
154impl Rgb8 {
155    /// Create from [`Rgb<f64>`].
156    pub const fn from_rgb_f64(c: RgbF64) -> Rgb8 {
157        Rgb8::new(Norm::f64_to_u8(c.r()), Norm::f64_to_u8(c.g()), Norm::f64_to_u8(c.b()))
158    }
159    /// Convert to [`Rgb<f64>`].
160    pub const fn to_rgb_f64(self) -> RgbF64 {
161        RgbF64::new(Norm::u8_to_f64(self.r()), Norm::u8_to_f64(self.g()), Norm::u8_to_f64(self.b()))
162    }
163
164    /// Create from [`Rgba<f64>`].
165    ///
166    /// Loses the alpha channel.
167    pub const fn from_rgba_f64(c: RgbaF64) -> Rgb8 {
168        Rgb8::new(Norm::f64_to_u8(c.r()), Norm::f64_to_u8(c.g()), Norm::f64_to_u8(c.b()))
169    }
170    /// Convert to [`Rgba<f64>`].
171    ///
172    /// Adds the given `alpha` channel.
173    pub const fn to_rgba_f64(self, alpha: u8) -> RgbaF64 {
174        RgbaF64::new(
175            Norm::u8_to_f64(self.r()),
176            Norm::u8_to_f64(self.g()),
177            Norm::u8_to_f64(self.b()),
178            Norm::u8_to_f64(alpha),
179        )
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use super::*;
186
187    const C8: Rgb8 = Rgb8::new(10, 20, 30);
188    const CA8: Rgba8 = Rgba8::new(10, 20, 30, 40);
189    const C16: Rgb16 = Rgb16::new(2570, 5140, 7710);
190    const H8: u32 = 0x_0A_14_1E;
191    const HA8: u32 = 0x_0A_14_1E_28;
192
193    #[test]
194    fn general_conversions() {
195        // array/tuple
196        assert_eq![Rgb8::from_array([10, 20, 30]), C8];
197        assert_eq![C8.as_array(), [10, 20, 30]];
198        assert_eq![Rgb8::from_tuple((10, 20, 30)), C8];
199        assert_eq![C8.to_tuple(), (10, 20, 30)];
200        // rgba
201        assert_eq![Rgb8::from_rgba8(CA8), C8];
202        assert_eq![C8.to_rgba8(40), CA8];
203        // packed rgb
204        assert_eq![Rgb8::from_rgb8_packed(H8), C8];
205        assert_eq![C8.to_rgb8_packed(), H8];
206        // packed rgba
207        assert_eq![Rgb8::from_rgba8_packed(HA8), C8];
208        assert_eq![C8.to_rgba8_packed(40), HA8];
209        // u16
210        assert_eq![Rgb8::from_rgb16(C16), C8];
211        assert_eq![C8.to_rgb16(), C16];
212    }
213
214    #[test]
215    #[cfg(feature = "_float_f32")]
216    fn f32_conversions() {
217        let f = RgbF32::new(0.039215688, 0.078431375, 0.11764706);
218        let fa = RgbaF32::new(0.039215688, 0.078431375, 0.11764706, 0.15686275);
219        assert_eq![Rgb8::from_rgb_f32(f), C8];
220        assert_eq![C8.to_rgb_f32(), f];
221        assert_eq![Rgb8::from_rgba_f32(fa), C8];
222        assert_eq![C8.to_rgba_f32(40), fa];
223    }
224
225    #[test]
226    #[cfg(feature = "_float_f64")]
227    fn f64_conversions() {
228        let f = RgbF64::new(0.0392156862745098, 0.0784313725490196, 0.11764705882352941);
229        let fa = RgbaF64::new(
230            0.0392156862745098,
231            0.0784313725490196,
232            0.11764705882352941,
233            0.1568627450980392,
234        );
235        assert_eq![Rgb8::from_rgb_f64(f), C8];
236        assert_eq![C8.to_rgb_f64(), f];
237        assert_eq![Rgb8::from_rgba_f64(fa), C8];
238        assert_eq![C8.to_rgba_f64(40), fa];
239    }
240}