pixelpwnr_render/
color.rs1use std::fmt;
2use std::num::ParseIntError;
3
4const DEFAULT_ALPHA: u8 = 0xFF;
6
7#[derive(Debug, Clone, Copy)]
8pub enum ParseColorError {
9 InvalidCharCount(usize),
12 InvalidChar(u8),
14}
15
16#[repr(align(4))]
25#[derive(PartialEq, Clone, Copy)]
26pub struct Color {
27 value: u32,
31}
32
33impl Color {
34 pub fn new(value: u32) -> Self {
38 Color { value }
39 }
40
41 pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
45 Color::from_rgba(r, g, b, DEFAULT_ALPHA)
46 }
47
48 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 pub fn red(&self) -> u32 {
55 self.value & 0xFF
56 }
57
58 pub fn green(&self) -> u32 {
60 (self.value & 0xFF00) >> 8
61 }
62
63 pub fn blue(&self) -> u32 {
65 (self.value & 0xFF0000) >> 16
66 }
67
68 pub fn alpha(&self) -> u32 {
70 (self.value & 0xFF000000) >> 24
71 }
72
73 pub fn from_hex(value: &str) -> Result<Self, ParseIntError> {
77 let mut raw = u32::from_str_radix(value, 16)?;
79
80 if value.len() <= 6 {
82 raw = (raw << 8) | 0xFF;
83 }
84
85 let color = Color::new(raw.to_be());
88
89 Ok(color)
90 }
91
92 pub fn from_hex_raw(value: &[u8]) -> Result<Self, ParseColorError> {
97 let len = value.len();
98
99 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 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 #[allow(dead_code)]
140 pub fn hex(&self) -> String {
141 format!("{:06X}", self.value.to_be() >> 8)
142 }
143
144 pub fn black() -> Self {
146 Color::from_rgb(0, 0, 0)
147 }
148
149 pub fn to_raw(&self) -> u32 {
151 self.value
152 }
153
154 pub fn blend(&mut self, other: Color) {
158 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 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
182 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}