1#[cfg(feature = "graphics")]
7use embedded_graphics_core::pixelcolor::BinaryColor;
8#[cfg(feature = "graphics")]
9use embedded_graphics_core::pixelcolor::PixelColor;
10
11#[derive(Debug, PartialEq, Eq)]
13pub struct OutOfColorRangeParseError(u8);
14impl core::fmt::Display for OutOfColorRangeParseError {
15 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
16 write!(f, "Outside of possible Color Range: {}", self.0)
17 }
18}
19
20impl OutOfColorRangeParseError {
21 fn _new(size: u8) -> OutOfColorRangeParseError {
22 OutOfColorRangeParseError(size)
23 }
24}
25
26#[derive(Clone, Copy, PartialEq, Eq, Debug)]
29pub enum Color {
30 Black,
32 White,
34}
35
36#[derive(Clone, Copy, PartialEq, Eq, Debug)]
38pub enum TriColor {
39 Black,
41 White,
43 Chromatic,
45}
46
47#[derive(Clone, Copy, PartialEq, Eq, Debug)]
49pub enum OctColor {
50 Black = 0x00,
52 White = 0x01,
54 Green = 0x02,
56 Blue = 0x03,
58 Red = 0x04,
60 Yellow = 0x05,
62 Orange = 0x06,
64 HiZ = 0x07,
66}
67
68pub trait ColorType {
70 const BITS_PER_PIXEL_PER_BUFFER: usize;
73
74 const BUFFER_COUNT: usize;
77
78 fn bitmask(&self, bwrbit: bool, pos: u32) -> (u8, u16);
89}
90
91impl ColorType for Color {
92 const BITS_PER_PIXEL_PER_BUFFER: usize = 1;
93 const BUFFER_COUNT: usize = 1;
94 fn bitmask(&self, _bwrbit: bool, pos: u32) -> (u8, u16) {
95 let bit = 0x80 >> (pos % 8);
96 match self {
97 Color::Black => (!bit, 0u16),
98 Color::White => (!bit, bit as u16),
99 }
100 }
101}
102
103impl ColorType for TriColor {
104 const BITS_PER_PIXEL_PER_BUFFER: usize = 1;
105 const BUFFER_COUNT: usize = 2;
106 fn bitmask(&self, bwrbit: bool, pos: u32) -> (u8, u16) {
107 let bit = 0x80 >> (pos % 8);
108 match self {
109 TriColor::Black => (!bit, 0u16),
110 TriColor::White => (!bit, bit as u16),
111 TriColor::Chromatic => (
112 !bit,
113 if bwrbit {
114 (bit as u16) << 8
115 } else {
116 (bit as u16) << 8 | bit as u16
117 },
118 ),
119 }
120 }
121}
122
123impl ColorType for OctColor {
124 const BITS_PER_PIXEL_PER_BUFFER: usize = 4;
125 const BUFFER_COUNT: usize = 1;
126 fn bitmask(&self, _bwrbit: bool, pos: u32) -> (u8, u16) {
127 let mask = !(0xF0 >> ((pos % 2) * 4));
128 let bits = self.get_nibble() as u16;
129 (mask, if pos % 2 == 1 { bits } else { bits << 4 })
130 }
131}
132
133#[cfg(feature = "graphics")]
134impl From<BinaryColor> for OctColor {
135 fn from(b: BinaryColor) -> OctColor {
136 match b {
137 BinaryColor::On => OctColor::Black,
138 BinaryColor::Off => OctColor::White,
139 }
140 }
141}
142
143#[cfg(feature = "graphics")]
144impl From<OctColor> for embedded_graphics_core::pixelcolor::Rgb888 {
145 fn from(b: OctColor) -> Self {
146 let (r, g, b) = b.rgb();
147 Self::new(r, g, b)
148 }
149}
150
151#[cfg(feature = "graphics")]
152impl From<embedded_graphics_core::pixelcolor::Rgb888> for OctColor {
153 fn from(p: embedded_graphics_core::pixelcolor::Rgb888) -> OctColor {
154 use embedded_graphics_core::prelude::RgbColor;
155 let colors = [
156 OctColor::Black,
157 OctColor::White,
158 OctColor::Green,
159 OctColor::Blue,
160 OctColor::Red,
161 OctColor::Yellow,
162 OctColor::Orange,
163 OctColor::HiZ,
164 ];
165 if let Some(found) = colors.iter().find(|c| c.rgb() == (p.r(), p.g(), p.b())) {
167 return *found;
168 }
169
170 *colors
172 .iter()
173 .map(|c| (c, c.rgb()))
174 .map(|(c, (r, g, b))| {
175 let dist = (i32::from(r) - i32::from(p.r())).pow(2)
176 + (i32::from(g) - i32::from(p.g())).pow(2)
177 + (i32::from(b) - i32::from(p.b())).pow(2);
178 (c, dist)
179 })
180 .min_by_key(|(_c, dist)| *dist)
181 .map(|(c, _)| c)
182 .unwrap_or(&OctColor::White)
183 }
184}
185
186#[cfg(feature = "graphics")]
187impl From<embedded_graphics_core::pixelcolor::raw::RawU4> for OctColor {
188 fn from(b: embedded_graphics_core::pixelcolor::raw::RawU4) -> Self {
189 use embedded_graphics_core::prelude::RawData;
190 OctColor::from_nibble(b.into_inner()).unwrap()
191 }
192}
193
194#[cfg(feature = "graphics")]
195impl PixelColor for OctColor {
196 type Raw = embedded_graphics_core::pixelcolor::raw::RawU4;
197}
198
199impl OctColor {
200 pub fn get_nibble(self) -> u8 {
202 self as u8
203 }
204 pub fn colors_byte(a: OctColor, b: OctColor) -> u8 {
206 a.get_nibble() << 4 | b.get_nibble()
207 }
208
209 pub fn from_nibble(nibble: u8) -> Result<OctColor, OutOfColorRangeParseError> {
211 match nibble & 0xf {
212 0x00 => Ok(OctColor::Black),
213 0x01 => Ok(OctColor::White),
214 0x02 => Ok(OctColor::Green),
215 0x03 => Ok(OctColor::Blue),
216 0x04 => Ok(OctColor::Red),
217 0x05 => Ok(OctColor::Yellow),
218 0x06 => Ok(OctColor::Orange),
219 0x07 => Ok(OctColor::HiZ),
220 e => Err(OutOfColorRangeParseError(e)),
221 }
222 }
223 pub fn split_byte(byte: u8) -> Result<(OctColor, OctColor), OutOfColorRangeParseError> {
225 let low = OctColor::from_nibble(byte & 0xf)?;
226 let high = OctColor::from_nibble((byte >> 4) & 0xf)?;
227 Ok((high, low))
228 }
229 pub fn rgb(self) -> (u8, u8, u8) {
231 match self {
232 OctColor::White => (0xff, 0xff, 0xff),
233 OctColor::Black => (0x00, 0x00, 0x00),
234 OctColor::Green => (0x00, 0xff, 0x00),
235 OctColor::Blue => (0x00, 0x00, 0xff),
236 OctColor::Red => (0xff, 0x00, 0x00),
237 OctColor::Yellow => (0xff, 0xff, 0x00),
238 OctColor::Orange => (0xff, 0x80, 0x00),
239 OctColor::HiZ => (0x80, 0x80, 0x80), }
241 }
242}
243impl Color {
246 pub fn get_bit_value(self) -> u8 {
248 match self {
249 Color::White => 1u8,
250 Color::Black => 0u8,
251 }
252 }
253
254 pub fn get_byte_value(self) -> u8 {
256 match self {
257 Color::White => 0xff,
258 Color::Black => 0x00,
259 }
260 }
261
262 fn from_u8(val: u8) -> Self {
264 match val {
265 0 => Color::Black,
266 1 => Color::White,
267 e => panic!(
268 "DisplayColor only parses 0 and 1 (Black and White) and not `{}`",
269 e
270 ),
271 }
272 }
273
274 pub fn inverse(self) -> Color {
278 match self {
279 Color::White => Color::Black,
280 Color::Black => Color::White,
281 }
282 }
283}
284
285impl From<u8> for Color {
286 fn from(value: u8) -> Self {
287 Color::from_u8(value)
288 }
289}
290
291#[cfg(feature = "graphics")]
292impl PixelColor for Color {
293 type Raw = ();
294}
295
296#[cfg(feature = "graphics")]
297impl From<BinaryColor> for Color {
298 fn from(b: BinaryColor) -> Color {
299 match b {
300 BinaryColor::On => Color::Black,
301 BinaryColor::Off => Color::White,
302 }
303 }
304}
305
306#[cfg(feature = "graphics")]
307impl From<embedded_graphics_core::pixelcolor::Rgb888> for Color {
308 fn from(rgb: embedded_graphics_core::pixelcolor::Rgb888) -> Self {
309 use embedded_graphics_core::pixelcolor::RgbColor;
310 if rgb == RgbColor::BLACK {
311 Color::Black
312 } else if rgb == RgbColor::WHITE {
313 Color::White
314 } else {
315 if (rgb.r() as u16 + rgb.g() as u16 + rgb.b() as u16) > 255 * 3 / 2 {
317 Color::White
318 } else {
319 Color::Black
320 }
321 }
322 }
323}
324
325#[cfg(feature = "graphics")]
326impl From<Color> for embedded_graphics_core::pixelcolor::Rgb888 {
327 fn from(color: Color) -> Self {
328 use embedded_graphics_core::pixelcolor::RgbColor;
329 match color {
330 Color::Black => embedded_graphics_core::pixelcolor::Rgb888::BLACK,
331 Color::White => embedded_graphics_core::pixelcolor::Rgb888::WHITE,
332 }
333 }
334}
335
336impl TriColor {
337 pub fn get_bit_value(self) -> u8 {
339 match self {
340 TriColor::White => 1u8,
341 TriColor::Black | TriColor::Chromatic => 0u8,
342 }
343 }
344
345 pub fn get_byte_value(self) -> u8 {
347 match self {
348 TriColor::White => 0xff,
349 TriColor::Black | TriColor::Chromatic => 0x00,
350 }
351 }
352}
353
354#[cfg(feature = "graphics")]
355impl PixelColor for TriColor {
356 type Raw = ();
357}
358
359#[cfg(feature = "graphics")]
360impl From<BinaryColor> for TriColor {
361 fn from(b: BinaryColor) -> TriColor {
362 match b {
363 BinaryColor::On => TriColor::Black,
364 BinaryColor::Off => TriColor::White,
365 }
366 }
367}
368#[cfg(feature = "graphics")]
369impl From<embedded_graphics_core::pixelcolor::Rgb888> for TriColor {
370 fn from(rgb: embedded_graphics_core::pixelcolor::Rgb888) -> Self {
371 use embedded_graphics_core::pixelcolor::RgbColor;
372 if rgb == RgbColor::BLACK {
373 TriColor::Black
374 } else if rgb == RgbColor::WHITE {
375 TriColor::White
376 } else {
377 TriColor::Chromatic
379 }
380 }
381}
382#[cfg(feature = "graphics")]
383impl From<TriColor> for embedded_graphics_core::pixelcolor::Rgb888 {
384 fn from(tri_color: TriColor) -> Self {
385 use embedded_graphics_core::pixelcolor::RgbColor;
386 match tri_color {
387 TriColor::Black => embedded_graphics_core::pixelcolor::Rgb888::BLACK,
388 TriColor::White => embedded_graphics_core::pixelcolor::Rgb888::WHITE,
389 TriColor::Chromatic => embedded_graphics_core::pixelcolor::Rgb888::new(255, 0, 0),
391 }
392 }
393}
394
395#[cfg(test)]
396mod tests {
397 use super::*;
398
399 #[test]
400 fn from_u8() {
401 assert_eq!(Color::Black, Color::from(0u8));
402 assert_eq!(Color::White, Color::from(1u8));
403 }
404
405 #[test]
407 fn from_u8_panic() {
408 for val in 2..=u8::MAX {
409 extern crate std;
410 let result = std::panic::catch_unwind(|| Color::from(val));
411 assert!(result.is_err());
412 }
413 }
414
415 #[test]
416 fn u8_conversion_black() {
417 assert_eq!(Color::from(Color::Black.get_bit_value()), Color::Black);
418 assert_eq!(Color::from(0u8).get_bit_value(), 0u8);
419 }
420
421 #[test]
422 fn u8_conversion_white() {
423 assert_eq!(Color::from(Color::White.get_bit_value()), Color::White);
424 assert_eq!(Color::from(1u8).get_bit_value(), 1u8);
425 }
426
427 #[test]
428 fn test_oct() {
429 let left = OctColor::Red;
430 let right = OctColor::Green;
431 assert_eq!(
432 OctColor::split_byte(OctColor::colors_byte(left, right)),
433 Ok((left, right))
434 );
435 }
436}