pixelpwnr_render/
color.rs

1use std::fmt;
2use std::num::ParseIntError;
3
4/// The default alpha channel value, if not specified. (0xFF = opaque)
5const DEFAULT_ALPHA: u8 = 0xFF;
6
7#[derive(Debug, Clone, Copy)]
8pub enum ParseColorError {
9    /// 6 or 8 characters are required
10    /// Value is the actual amount
11    InvalidCharCount(usize),
12    /// An invalid character was encountered
13    InvalidChar(u8),
14}
15
16/// Struct representing a color value.
17///
18/// This color uses 4 channels, for red, green, blue and alpha.
19/// Each channel may be a value from 0 to 255.
20///
21/// Internally, this struct stores the color channels as a single u32 (DWORD)
22/// value, which is aligned to 4 bytes in memory. This allows atomic use when
23/// directly writing the value in most cases (but not all!).
24#[repr(align(4))]
25#[derive(PartialEq, Clone, Copy)]
26pub struct Color {
27    /// Defines the color with a byte for each of the 4 color channels.
28    ///
29    /// Bytes are ordered as RGBA, little endian.
30    value: u32,
31}
32
33impl Color {
34    /// Construct a new color, from a raw color value.
35    ///
36    /// This color value defines the value of all 4 color channels.
37    pub fn new(value: u32) -> Self {
38        Color { value }
39    }
40
41    /// Construct a new color, from RGB values.
42    ///
43    /// The alpha channel will be set to 0xFF.
44    pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
45        Color::from_rgba(r, g, b, DEFAULT_ALPHA)
46    }
47
48    /// Construct a new color, from RGBA values.
49    pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
50        Color::new(r as u32 | (g as u32) << 8 | (b as u32) << 16 | (a as u32) << 24)
51    }
52
53    /// Get the red value, in the range `[0, 255)`.
54    pub fn red(&self) -> u32 {
55        self.value & 0xFF
56    }
57
58    /// Get green green value, in the range `[0, 255)`.
59    pub fn green(&self) -> u32 {
60        (self.value & 0xFF00) >> 8
61    }
62
63    /// Get the blue value, in the range `[0, 255)`.
64    pub fn blue(&self) -> u32 {
65        (self.value & 0xFF0000) >> 16
66    }
67
68    /// Get the alpha value, in the range `[0, 255)`.
69    pub fn alpha(&self) -> u32 {
70        (self.value & 0xFF000000) >> 24
71    }
72
73    /// Construct a new color, from the given hexadecimal string.
74    ///
75    /// If parsing the hexadecimal string failed, an error is returned.
76    pub fn from_hex(value: &str) -> Result<Self, ParseIntError> {
77        // Parse the hexadecimal value
78        let mut raw = u32::from_str_radix(value, 16)?;
79
80        // Shift and add an alpha channel, if it wasn't set
81        if value.len() <= 6 {
82            raw = (raw << 8) | 0xFF;
83        }
84
85        // Construct and return the color
86
87        let color = Color::new(raw.to_be());
88
89        Ok(color)
90    }
91
92    /// Construct a new color, from the given slice.
93    /// The slice should represent hexadecimal characters as ASCII characters,
94    /// meaning that they should be between b'0' and b'9', between b'a' and b'f', or
95    /// between b'A' and b'F'
96    pub fn from_hex_raw(value: &[u8]) -> Result<Self, ParseColorError> {
97        let len = value.len();
98
99        /// This always returns a value 0 <= v <= 15
100        fn parse_char(input: u8) -> Result<u8, ParseColorError> {
101            if input >= b'a' && input <= b'f' {
102                Ok(input - b'a' + 10)
103            } else if input >= b'A' && input <= b'F' {
104                Ok(input - b'A' + 10)
105            } else if input >= b'0' && input <= b'9' {
106                Ok(input - b'0')
107            } else {
108                Err(ParseColorError::InvalidChar(input))
109            }
110        }
111
112        let build = || {
113            let mut raw_u32 = 0u32;
114            for char in value.iter() {
115                raw_u32 <<= 4;
116                raw_u32 |= parse_char(*char)? as u32;
117            }
118            Ok(raw_u32)
119        };
120
121        if len == 6 {
122            let mut value = build()?;
123            // No Alpha byte
124            value = (value << 8) | 0xFF;
125            Ok(Color {
126                value: value.to_be(),
127            })
128        } else if len == 8 {
129            let value = build()?;
130            Ok(Color {
131                value: value.to_be(),
132            })
133        } else {
134            Err(ParseColorError::InvalidCharCount(len))
135        }
136    }
137
138    /// Get the hexadecimal value of the color.
139    #[allow(dead_code)]
140    pub fn hex(&self) -> String {
141        format!("{:06X}", self.value.to_be() >> 8)
142    }
143
144    /// A black color, with the default alpha.
145    pub fn black() -> Self {
146        Color::from_rgb(0, 0, 0)
147    }
148
149    /// Get the raw color value, as single u32.
150    pub fn to_raw(&self) -> u32 {
151        self.value
152    }
153
154    /// Blend this color with another
155    ///
156    /// Self should be the current value, and `other` should be the incoming value
157    pub fn blend(&mut self, other: Color) {
158        // Self = destination = ptr
159        // Other = source = rgba
160
161        let mut r = other.red();
162        let mut g = other.green();
163        let mut b = other.blue();
164        let mut a = other.alpha();
165
166        if a == 0 {
167            return;
168        } else if a < u8::MAX as u32 {
169            let na = u8::MAX as u32 - a;
170            r = ((a * r) + (na * self.red())) / 0xFF;
171            g = ((a * g) + (na * self.green())) / 0xFF;
172            b = ((a * b) + (na * self.blue())) / 0xFF;
173            a = a + self.alpha();
174        }
175        self.value = r & 0xFF | (g & 0xFF) << 8 | (b & 0xFF) << 16 | (a & 0xFF) << 24;
176    }
177}
178
179impl fmt::Debug for Color {
180    /// Nicely format the color in a human readable RGB(A) format.
181    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
182        // Only debug the alpha channel if it isn't the default value
183        if self.alpha() == 0 {
184            write!(
185                f,
186                "ColorRGB({:X}, {:X}, {:X})",
187                self.red(),
188                self.green(),
189                self.blue()
190            )
191        } else {
192            write!(
193                f,
194                "ColorRGBA({:X}, {:X}, {:X}, {:X})",
195                self.red(),
196                self.green(),
197                self.blue(),
198                self.alpha()
199            )
200        }
201    }
202}
203
204#[test]
205fn from_hex_raw() {
206    macro_rules! test {
207        ($in: literal, $out: expr, $print: literal) => {
208            let color_raw = Color::from_hex_raw($in.as_bytes()).unwrap();
209            let color = Color::from_hex($in).unwrap();
210            assert_eq!(color, color_raw);
211            assert_eq!(Color::new($out), color_raw);
212            assert_eq!(format!("{:?}", color_raw), $print);
213        };
214    }
215
216    test!("ABCDEFBA", 0xBAEFCDAB, "ColorRGBA(AB, CD, EF, BA)");
217    test!("AABBCC", 0xFFCCBBAA, "ColorRGBA(AA, BB, CC, FF)");
218    test!("ABCDEF00", 0x00EFCDAB, "ColorRGB(AB, CD, EF)");
219}