embedded_graphics/mock_display/
color_mapping.rs1use embedded_graphics_core::pixelcolor::{
2 Bgr555, Bgr565, Bgr888, BinaryColor, Gray2, Gray4, Gray8, GrayColor, Rgb555, Rgb565, Rgb888,
3 RgbColor, WebColors,
4};
5
6pub trait ColorMapping: Into<Rgb888> {
10 const NONE_COLOR: Rgb888 = Rgb888::new(128, 128, 128);
15
16 fn char_to_color(c: char) -> Self;
18
19 fn color_to_char(color: Self) -> char;
21}
22
23impl ColorMapping for BinaryColor {
24 fn char_to_color(c: char) -> Self {
25 match c {
26 '.' => BinaryColor::Off,
27 '#' => BinaryColor::On,
28 _ => panic!("Invalid char in pattern: '{}'", c),
29 }
30 }
31
32 fn color_to_char(color: Self) -> char {
33 match color {
34 BinaryColor::Off => '.',
35 BinaryColor::On => '#',
36 }
37 }
38}
39
40macro_rules! impl_gray_color_mapping {
41 ($type:ident, $radix:expr) => {
42 impl ColorMapping for $type {
43 const NONE_COLOR: Rgb888 = Rgb888::CSS_STEEL_BLUE;
44
45 fn char_to_color(c: char) -> Self {
46 if let Some(digit) = c.to_digit($radix) {
47 Self::new(digit as u8)
48 } else {
49 panic!("invalid char in pattern: '{}'", c)
50 }
51 }
52
53 fn color_to_char(color: Self) -> char {
54 core::char::from_digit(color.luma() as u32, $radix)
55 .unwrap()
56 .to_ascii_uppercase()
57 }
58 }
59 };
60}
61
62impl_gray_color_mapping!(Gray2, 4);
63impl_gray_color_mapping!(Gray4, 16);
64
65impl ColorMapping for Gray8 {
66 const NONE_COLOR: Rgb888 = Rgb888::CSS_STEEL_BLUE;
67
68 fn char_to_color(c: char) -> Self {
69 if let Some(digit) = c.to_digit(16) {
70 Self::new(digit as u8 * 0x11)
71 } else {
72 panic!("invalid char in pattern: '{}'", c);
73 }
74 }
75
76 fn color_to_char(color: Self) -> char {
77 let luma = color.luma();
78 let lower = luma & 0xF;
79 let upper = luma >> 4;
80
81 if lower != upper {
82 '?'
83 } else {
84 core::char::from_digit(lower as u32, 16)
85 .unwrap()
86 .to_ascii_uppercase()
87 }
88 }
89}
90
91macro_rules! impl_rgb_color_mapping {
92 ($type:ident) => {
93 impl ColorMapping for $type {
94 fn char_to_color(c: char) -> Self {
95 match c {
96 'K' => Self::BLACK,
97 'R' => Self::RED,
98 'G' => Self::GREEN,
99 'B' => Self::BLUE,
100 'Y' => Self::YELLOW,
101 'M' => Self::MAGENTA,
102 'C' => Self::CYAN,
103 'W' => Self::WHITE,
104 _ => panic!("Invalid char in pattern: '{}'", c),
105 }
106 }
107
108 fn color_to_char(color: Self) -> char {
109 match color {
110 Self::BLACK => 'K',
111 Self::RED => 'R',
112 Self::GREEN => 'G',
113 Self::BLUE => 'B',
114 Self::YELLOW => 'Y',
115 Self::MAGENTA => 'M',
116 Self::CYAN => 'C',
117 Self::WHITE => 'W',
118 _ => '?',
119 }
120 }
121 }
122 };
123}
124
125impl_rgb_color_mapping!(Rgb555);
126impl_rgb_color_mapping!(Bgr555);
127impl_rgb_color_mapping!(Rgb565);
128impl_rgb_color_mapping!(Bgr565);
129impl_rgb_color_mapping!(Rgb888);
130impl_rgb_color_mapping!(Bgr888);
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn gray2_mapping() {
138 for luma in 0..4 {
139 let color = Gray2::new(luma);
140
141 assert_eq!(color, Gray2::char_to_color(Gray2::color_to_char(color)));
142 }
143 }
144
145 #[test]
146 fn gray4_mapping() {
147 for luma in 0..16 {
148 let color = Gray4::new(luma);
149
150 assert_eq!(color, Gray4::char_to_color(Gray4::color_to_char(color)));
151 }
152 }
153
154 #[test]
155 fn gray8_mapping() {
156 for luma in 0..16 {
157 let color = Gray8::new(luma * 0x11);
158
159 assert_eq!(color, Gray8::char_to_color(Gray8::color_to_char(color)));
160 }
161 }
162
163 #[test]
164 #[should_panic(expected = "invalid char in pattern: '4'")]
165 fn invalid_gray2_char_4() {
166 Gray2::char_to_color('4');
167 }
168
169 #[test]
170 #[should_panic(expected = "invalid char in pattern: 'A'")]
171 fn invalid_gray2_char_a() {
172 Gray2::char_to_color('A');
173 }
174
175 #[test]
176 #[should_panic(expected = "invalid char in pattern: 'G'")]
177 fn invalid_gray4_char_g() {
178 Gray2::char_to_color('G');
179 }
180
181 #[test]
182 #[should_panic(expected = "invalid char in pattern: 'G'")]
183 fn invalid_gray8_char_g() {
184 Gray8::char_to_color('G');
185 }
186}