1use crate::color::{
4 brightness::{Channel, ChannelVector},
5 ApproxBrightness,
6 BadCmyColor,
7 BadGrayColor,
8 BasicColor,
9 Brightness,
10};
11use crossterm::style::Color as CrosstermColor;
12use std::{convert::TryFrom, fmt, ops::Not};
13
14#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct CmyColor {
18 code: u8,
20}
21
22impl CmyColor {
23 pub const BASE: u8 = 6;
25
26 pub fn try_new(
29 cyan: u8,
30 magenta: u8,
31 yellow: u8,
32 ) -> Result<Self, BadCmyColor> {
33 if cyan >= Self::BASE || magenta >= Self::BASE || yellow >= Self::BASE {
34 Err(BadCmyColor { cyan, magenta, yellow })
35 } else {
36 Ok(Self {
37 code: cyan * Self::BASE.pow(2) + magenta * Self::BASE + yellow,
38 })
39 }
40 }
41
42 pub fn new(cyan: u8, magenta: u8, yellow: u8) -> Self {
47 Self::try_new(cyan, magenta, yellow).expect("Bad Cmy Color")
48 }
49
50 pub const fn cyan(self) -> u8 {
52 self.code / Self::BASE / Self::BASE % Self::BASE
53 }
54
55 pub const fn magenta(self) -> u8 {
57 self.code / Self::BASE % Self::BASE
58 }
59
60 pub const fn yellow(self) -> u8 {
62 self.code % Self::BASE
63 }
64
65 pub const fn code(self) -> u8 {
67 self.code
68 }
69
70 pub fn set_cyan(self, cyan: u8) -> Self {
75 Self::new(cyan, self.magenta(), self.yellow())
76 }
77
78 pub fn set_magenta(self, magenta: u8) -> Self {
83 Self::new(self.cyan(), magenta, self.yellow())
84 }
85
86 pub fn set_yellow(self, yellow: u8) -> Self {
91 Self::new(self.cyan(), self.magenta(), yellow)
92 }
93
94 fn from_channels(channels: [Channel; 3]) -> Self {
96 Self::new(channels[0].value(), channels[1].value(), channels[2].value())
97 }
98
99 fn channels(self) -> [Channel; 3] {
101 [
102 Channel::new(self.cyan(), 30),
103 Channel::new(self.magenta(), 59),
104 Channel::new(self.yellow(), 11),
105 ]
106 }
107}
108
109impl fmt::Debug for CmyColor {
110 fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
111 fmtr.debug_struct("CmyColor")
112 .field("cyan", &self.cyan())
113 .field("magenta", &self.magenta())
114 .field("yellow", &self.yellow())
115 .finish()
116 }
117}
118
119impl Not for CmyColor {
120 type Output = Self;
121
122 fn not(self) -> Self::Output {
123 Self::new(
124 Self::BASE - self.cyan(),
125 Self::BASE - self.magenta(),
126 Self::BASE - self.yellow(),
127 )
128 }
129}
130
131impl ApproxBrightness for CmyColor {
132 fn approx_brightness(&self) -> Brightness {
133 let mut channels = self.channels();
134 let vector = ChannelVector::new(&mut channels, Self::BASE - 1);
135 vector.approx_brightness()
136 }
137
138 fn set_approx_brightness(&mut self, brightness: Brightness) {
139 let mut channels = self.channels();
140 let mut vector = ChannelVector::new(&mut channels, Self::BASE - 1);
141 vector.set_approx_brightness(brightness);
142 *self = Self::from_channels(channels);
143 }
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
148pub struct GrayColor {
149 brightness: u8,
151}
152
153impl GrayColor {
154 pub const MIN: Self = Self { brightness: 0 };
156 pub const HALF: Self = Self { brightness: 12 };
158 pub const MAX: Self = Self { brightness: 23 };
160
161 pub fn try_new(brightness: u8) -> Result<Self, BadGrayColor> {
164 if brightness > Self::MAX.brightness() {
165 Err(BadGrayColor { brightness })
166 } else {
167 Ok(Self { brightness })
168 }
169 }
170
171 pub fn new(brightness: u8) -> Self {
176 Self::try_new(brightness).expect("Bad gray color")
177 }
178
179 pub const fn brightness(self) -> u8 {
181 self.brightness
182 }
183}
184
185impl Not for GrayColor {
186 type Output = Self;
187
188 fn not(self) -> Self::Output {
189 Self::new(Self::MAX.brightness() + 1 - self.brightness)
190 }
191}
192
193impl ApproxBrightness for GrayColor {
194 fn approx_brightness(&self) -> Brightness {
195 let brightness = Brightness { level: u16::from(self.brightness) };
196 brightness.spread(u16::from(Self::MAX.brightness))
197 }
198
199 fn set_approx_brightness(&mut self, brightness: Brightness) {
200 let compressed = brightness.compress(u16::from(Self::MAX.brightness));
201 let res = u8::try_from(compressed.level);
202 self.brightness = res.expect("Color brightness bug");
203 }
204}
205
206#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
208pub enum Color8BitKind {
209 Basic(BasicColor),
211 Cmy(CmyColor),
213 Gray(GrayColor),
215}
216
217impl Not for Color8BitKind {
218 type Output = Self;
219
220 fn not(self) -> Self::Output {
221 match self {
222 Color8BitKind::Basic(color) => Color8BitKind::Basic(!color),
223 Color8BitKind::Cmy(color) => Color8BitKind::Cmy(!color),
224 Color8BitKind::Gray(color) => Color8BitKind::Gray(!color),
225 }
226 }
227}
228
229impl From<BasicColor> for Color8BitKind {
230 fn from(color: BasicColor) -> Self {
231 Color8BitKind::Basic(color)
232 }
233}
234
235impl From<CmyColor> for Color8BitKind {
236 fn from(color: CmyColor) -> Self {
237 Color8BitKind::Cmy(color)
238 }
239}
240
241impl From<GrayColor> for Color8BitKind {
242 fn from(color: GrayColor) -> Self {
243 Color8BitKind::Gray(color)
244 }
245}
246
247impl From<Color8Bit> for Color8BitKind {
248 fn from(color: Color8Bit) -> Self {
249 color.kind()
250 }
251}
252
253impl ApproxBrightness for Color8BitKind {
254 fn approx_brightness(&self) -> Brightness {
255 match self {
256 Color8BitKind::Basic(color) => color.approx_brightness(),
257 Color8BitKind::Cmy(color) => color.approx_brightness(),
258 Color8BitKind::Gray(color) => color.approx_brightness(),
259 }
260 }
261
262 fn set_approx_brightness(&mut self, brightness: Brightness) {
263 match self {
264 Color8BitKind::Basic(color) => color.set_approx_brightness(brightness),
265 Color8BitKind::Cmy(color) => color.set_approx_brightness(brightness),
266 Color8BitKind::Gray(color) => color.set_approx_brightness(brightness),
267 }
268 }
269}
270
271#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
273pub struct Color8Bit {
274 code: u8,
275}
276
277impl Color8Bit {
278 const BASIC_SIZE: u8 = 16;
280 const BASIC_CMY_SIZE: u8 =
282 Self::BASIC_SIZE + CmyColor::BASE * CmyColor::BASE * CmyColor::BASE;
283
284 pub const fn basic(color: BasicColor) -> Self {
286 Self { code: color as u8 }
287 }
288
289 pub const fn cmy(color: CmyColor) -> Self {
291 Self { code: color.code() + Self::BASIC_SIZE }
292 }
293
294 pub const fn gray(color: GrayColor) -> Self {
296 Self { code: color.brightness() + Self::BASIC_CMY_SIZE }
297 }
298
299 pub const fn code(self) -> u8 {
301 self.code
302 }
303
304 pub fn kind(self) -> Color8BitKind {
306 if self.code < 16 {
307 Color8BitKind::Basic(BasicColor::try_from(self.code).unwrap())
308 } else if self.code < Self::BASIC_CMY_SIZE {
309 Color8BitKind::Cmy(CmyColor { code: self.code - Self::BASIC_SIZE })
310 } else {
311 Color8BitKind::Gray(GrayColor {
312 brightness: self.code - Self::BASIC_CMY_SIZE,
313 })
314 }
315 }
316
317 pub(crate) fn to_crossterm(self) -> CrosstermColor {
319 CrosstermColor::AnsiValue(self.code())
320 }
321}
322
323impl fmt::Debug for Color8Bit {
324 fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
325 fmtr.debug_struct("Color8Bit").field("kind", &self.kind()).finish()
326 }
327}
328
329impl Not for Color8Bit {
330 type Output = Self;
331
332 fn not(self) -> Self::Output {
333 Self::from(!self.kind())
334 }
335}
336
337impl From<BasicColor> for Color8Bit {
338 fn from(color: BasicColor) -> Self {
339 Self::basic(color)
340 }
341}
342
343impl From<CmyColor> for Color8Bit {
344 fn from(color: CmyColor) -> Self {
345 Self::cmy(color)
346 }
347}
348
349impl From<GrayColor> for Color8Bit {
350 fn from(color: GrayColor) -> Self {
351 Self::gray(color)
352 }
353}
354
355impl From<Color8BitKind> for Color8Bit {
356 fn from(kind: Color8BitKind) -> Self {
357 match kind {
358 Color8BitKind::Basic(color) => Self::from(color),
359 Color8BitKind::Cmy(color) => Self::from(color),
360 Color8BitKind::Gray(color) => Self::from(color),
361 }
362 }
363}
364
365impl ApproxBrightness for Color8Bit {
366 fn approx_brightness(&self) -> Brightness {
367 self.kind().approx_brightness()
368 }
369
370 fn set_approx_brightness(&mut self, brightness: Brightness) {
371 *self = Self::from(self.kind().with_approx_brightness(brightness));
372 }
373}