Skip to main content

macroquad_ply/
color.rs

1//! Color types and helpers.
2
3pub use colors::*;
4
5/// A color represented by 4 floats: red, green, blue and alpha.
6#[repr(C)]
7#[derive(Clone, Copy, Debug, Default, PartialEq)]
8pub struct Color {
9    /// Red channel value from 0.0 to 1.0.
10    pub r: f32,
11    /// Green channel value from 0.0 to 1.0.
12    pub g: f32,
13    /// Blue channel value from 0.0 to 1.0.
14    pub b: f32,
15    /// Alpha channel value from 0.0 to 1.0.
16    pub a: f32,
17}
18
19/// Build a color from 4 components of 0..255 values.
20/// This was a temporary solution because [Color::from_rgba] was not a const fn due to
21/// [this issue](https://github.com/rust-lang/rust/issues/57241) waiting to be resolved.
22/// It is not needed anymore.
23#[macro_export]
24macro_rules! color_u8 {
25    ($r:expr, $g:expr, $b:expr, $a:expr) => {
26        Color::new(
27            $r as f32 / 255.,
28            $g as f32 / 255.,
29            $b as f32 / 255.,
30            $a as f32 / 255.,
31        )
32    };
33}
34
35#[test]
36fn color_from_bytes() {
37    assert_eq!(Color::new(1.0, 0.0, 0.0, 1.0), color_u8!(255, 0, 0, 255));
38    assert_eq!(
39        Color::new(1.0, 0.5, 0.0, 1.0),
40        color_u8!(255, 127.5, 0, 255)
41    );
42    assert_eq!(
43        Color::new(0.0, 1.0, 0.5, 1.0),
44        color_u8!(0, 255, 127.5, 255)
45    );
46}
47
48impl From<Color> for [u8; 4] {
49    fn from(val: Color) -> Self {
50        [
51            (val.r * 255.) as u8,
52            (val.g * 255.) as u8,
53            (val.b * 255.) as u8,
54            (val.a * 255.) as u8,
55        ]
56    }
57}
58
59impl From<[u8; 4]> for Color {
60    fn from(val: [u8; 4]) -> Self {
61        Color::new(
62            val[0] as f32 / 255.,
63            val[1] as f32 / 255.,
64            val[2] as f32 / 255.,
65            val[3] as f32 / 255.,
66        )
67    }
68}
69
70impl From<Color> for [f32; 4] {
71    fn from(val: Color) -> Self {
72        [val.r, val.g, val.b, val.a]
73    }
74}
75
76impl From<[f32; 4]> for Color {
77    fn from(colors: [f32; 4]) -> Color {
78        Color::new(colors[0], colors[1], colors[2], colors[3])
79    }
80}
81
82impl Color {
83    /// Creates a new `Color` with the given red, green, blue, and alpha components.
84    /// Values are expected to be between 0.0 and 1.0.
85    ///
86    /// # Example
87    ///
88    /// ```
89    /// use macroquad::prelude::*;
90    ///
91    /// let pink = Color::new(1.00, 0.43, 0.76, 1.00);
92    /// assert_eq!(pink.r, 1.00);
93    /// assert_eq!(pink.g, 0.43);
94    /// assert_eq!(pink.b, 0.76);
95    /// assert_eq!(pink.a, 1.00);
96    /// ```
97    ///
98    /// Note that values outside of this range are effectively clamped,
99    /// and do not generate an error or warning.
100    pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Color {
101        Color { r, g, b, a }
102    }
103
104    /// Build a color from 4 components between 0 and 255.
105    pub const fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Color {
106        Color::new(
107            r as f32 / 255.,
108            g as f32 / 255.,
109            b as f32 / 255.,
110            a as f32 / 255.,
111        )
112    }
113
114    /// Build a color from a hexadecimal u32.
115    ///
116    /// # Example
117    ///
118    /// ```
119    /// use macroquad::prelude::*;
120    ///
121    /// let light_blue = Color::from_hex(0x3CA7D5);
122    /// assert_eq!(light_blue.r, 0.23529412);
123    /// assert_eq!(light_blue.g, 0.654902);
124    /// assert_eq!(light_blue.b, 0.8352941);
125    /// assert_eq!(light_blue.a, 1.00);
126    /// ```
127    pub const fn from_hex(hex: u32) -> Color {
128        let bytes: [u8; 4] = hex.to_be_bytes();
129
130        Self::from_rgba(bytes[1], bytes[2], bytes[3], 255)
131    }
132
133    /// Create a vec4 of red, green, blue, and alpha components.
134    pub const fn to_vec(&self) -> glam::Vec4 {
135        glam::Vec4::new(self.r, self.g, self.b, self.a)
136    }
137
138    /// Create a color from a vec4 of red, green, blue, and alpha components.
139    pub const fn from_vec(vec: glam::Vec4) -> Self {
140        Self::new(vec.x, vec.y, vec.z, vec.w)
141    }
142
143    /// Create a copy of the current color, but with a different alpha value.
144    pub const fn with_alpha(&self, alpha: f32) -> Color {
145        Color::new(self.r, self.g, self.b, alpha)
146    }
147}
148
149pub mod colors {
150    //! Constants for some common colors.
151
152    use super::Color;
153
154    pub const LIGHTGRAY: Color = Color::new(0.78, 0.78, 0.78, 1.00);
155    pub const GRAY: Color = Color::new(0.51, 0.51, 0.51, 1.00);
156    pub const DARKGRAY: Color = Color::new(0.31, 0.31, 0.31, 1.00);
157    pub const YELLOW: Color = Color::new(0.99, 0.98, 0.00, 1.00);
158    pub const GOLD: Color = Color::new(1.00, 0.80, 0.00, 1.00);
159    pub const ORANGE: Color = Color::new(1.00, 0.63, 0.00, 1.00);
160    pub const PINK: Color = Color::new(1.00, 0.43, 0.76, 1.00);
161    pub const RED: Color = Color::new(0.90, 0.16, 0.22, 1.00);
162    pub const MAROON: Color = Color::new(0.75, 0.13, 0.22, 1.00);
163    pub const GREEN: Color = Color::new(0.00, 0.89, 0.19, 1.00);
164    pub const LIME: Color = Color::new(0.00, 0.62, 0.18, 1.00);
165    pub const DARKGREEN: Color = Color::new(0.00, 0.46, 0.17, 1.00);
166    pub const SKYBLUE: Color = Color::new(0.40, 0.75, 1.00, 1.00);
167    pub const BLUE: Color = Color::new(0.00, 0.47, 0.95, 1.00);
168    pub const DARKBLUE: Color = Color::new(0.00, 0.32, 0.67, 1.00);
169    pub const PURPLE: Color = Color::new(0.78, 0.48, 1.00, 1.00);
170    pub const VIOLET: Color = Color::new(0.53, 0.24, 0.75, 1.00);
171    pub const DARKPURPLE: Color = Color::new(0.44, 0.12, 0.49, 1.00);
172    pub const BEIGE: Color = Color::new(0.83, 0.69, 0.51, 1.00);
173    pub const BROWN: Color = Color::new(0.50, 0.42, 0.31, 1.00);
174    pub const DARKBROWN: Color = Color::new(0.30, 0.25, 0.18, 1.00);
175    pub const WHITE: Color = Color::new(1.00, 1.00, 1.00, 1.00);
176    pub const BLACK: Color = Color::new(0.00, 0.00, 0.00, 1.00);
177    pub const BLANK: Color = Color::new(0.00, 0.00, 0.00, 0.00);
178    pub const MAGENTA: Color = Color::new(1.00, 0.00, 1.00, 1.00);
179}
180
181#[rustfmt::skip]
182pub fn hsl_to_rgb(h: f32, s: f32, l: f32) -> Color {
183    let r;
184    let g;
185    let b;
186
187    if s == 0.0 {  r = l; g = l; b = l; }
188    else {
189        fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
190            if t < 0.0 { t += 1.0 }
191            if t > 1.0 { t -= 1.0 }
192            if t < 1.0 / 6.0 { return p + (q - p) * 6.0 * t; }
193            if t < 1.0 / 2.0 { return q; }
194            if t < 2.0 / 3.0 { return p + (q - p) * (2.0 / 3.0 - t) * 6.0; }
195            p
196        }
197
198        let q = if l < 0.5 {
199            l * (1.0 + s)
200        } else {
201            l + s - l * s
202        };
203        let p = 2.0 * l - q;
204        r = hue_to_rgb(p, q, h + 1.0 / 3.0);
205        g = hue_to_rgb(p, q, h);
206        b = hue_to_rgb(p, q, h - 1.0 / 3.0);
207    }
208
209    Color::new(r, g, b, 1.0)
210}
211
212pub fn rgb_to_hsl(color: Color) -> (f32, f32, f32) {
213    fn max(a: f32, b: f32) -> f32 {
214        if a > b {
215            a
216        } else {
217            b
218        }
219    }
220    fn min(a: f32, b: f32) -> f32 {
221        if a < b {
222            a
223        } else {
224            b
225        }
226    }
227
228    let Color { r, g, b, .. } = color;
229
230    let max = max(max(r, g), b);
231    let min = min(min(r, g), b);
232
233    // Luminosity is the average of the max and min rgb color intensities.
234    let l = (max + min) / 2.0;
235
236    // Saturation
237    let delta: f32 = max - min;
238    if delta == 0.0 {
239        // it's gray
240        return (0.0, 0.0, l);
241    }
242
243    // it's not gray
244    let s = if l < 0.5 {
245        delta / (max + min)
246    } else {
247        delta / (2.0 - max - min)
248    };
249
250    // Hue
251    let r2 = (((max - r) / 6.0) + (delta / 2.0)) / delta;
252    let g2 = (((max - g) / 6.0) + (delta / 2.0)) / delta;
253    let b2 = (((max - b) / 6.0) + (delta / 2.0)) / delta;
254
255    let mut h = match max {
256        x if x == r => b2 - g2,
257        x if x == g => (1.0 / 3.0) + r2 - b2,
258        _ => (2.0 / 3.0) + g2 - r2,
259    };
260
261    // Fix wraparounds
262    if h < 0 as f32 {
263        h += 1.0;
264    } else if h > 1 as f32 {
265        h -= 1.0;
266    }
267
268    (h, s, l)
269}