1pub use self::ColorType::*;
2use crate::util::OneWayOwned;
3use std::fmt::{Debug, Display};
4
5pub type ColorIndex = u8;
6pub type ColorComponent = u8;
7pub type ColorInt = u32;
8pub type RGBInts = (ColorComponent, ColorComponent, ColorComponent);
9pub type ARGBInts = (
10 ColorComponent,
11 ColorComponent,
12 ColorComponent,
13 ColorComponent,
14);
15
16#[derive(Debug, Clone, PartialEq, PartialOrd)]
32pub enum ColorType<T = String>
33{
34 RGBString(T),
46 RGBInteger(ColorComponent, ColorComponent, ColorComponent),
48 ARGBInteger(
52 ColorComponent,
53 ColorComponent,
54 ColorComponent,
55 ColorComponent,
56 ),
57 VariableRGBInteger(Vec<RGBInts>),
60 VariableARGBInteger(Vec<ARGBInts>),
63 PaletteFracColor(f64),
73 PaletteCBColor(f64),
103 VariablePaletteColor(Vec<f64>),
107 SavedColorMap(T, Vec<f64>),
112 Index(ColorIndex),
114 VariableIndex(Vec<ColorIndex>),
121 Background,
123 Black,
125}
126
127impl<T: Display + Debug> ColorType<T>
128{
129 pub fn command(&self) -> String
131 {
132 match self
133 {
134 RGBString(s) => format!(r#"rgb "{}""#, from_string(s.to_string())),
135 RGBInteger(r, g, b) => format!(r#"rgb {}"#, from_argb(255, *r, *g, *b)),
136 ARGBInteger(a, r, g, b) => format!(r#"rgb {}"#, from_argb(*a, *r, *g, *b)),
137 VariableRGBInteger(_) => "rgb variable".into(),
138 VariableARGBInteger(_) => "rgb variable".into(),
139 PaletteFracColor(v) => format!("palette frac {v}"),
140 PaletteCBColor(v) => format!("palette cb {v}"),
141 VariablePaletteColor(_) => "palette z".into(),
142 SavedColorMap(s, _) => format!("palette {s}"),
143 VariableIndex(_) => "variable".into(),
144 Background => "bgnd".into(),
145 Index(n) => format!("{}", n),
146 Black => "black".into(),
147 }
148 }
149
150 pub fn data(&self) -> Vec<f64>
151 {
152 match self
153 {
154 VariableRGBInteger(items) => items
155 .iter()
156 .map(|(r, g, b)| from_argb(255, *r, *g, *b) as f64)
157 .collect(),
158 VariableARGBInteger(items) => items
159 .iter()
160 .map(|(a, r, g, b)| from_argb(*a, *r, *g, *b) as f64)
161 .collect(),
162 VariablePaletteColor(items) => items.clone(),
163 SavedColorMap(_, items) => items.clone(),
164 VariableIndex(items) => items.iter().map(|v| *v as f64).collect(),
165 c => panic!("data() called on non-variable color type: {:?}", *c),
166 }
167 }
168
169 pub fn is_variable(&self) -> bool
170 {
171 matches!(
172 self,
173 VariableRGBInteger(_)
174 | VariableARGBInteger(_)
175 | VariableIndex(_)
176 | VariablePaletteColor(_)
177 | SavedColorMap(_, _)
178 )
179 }
180
181 pub fn has_alpha(&self) -> bool
182 {
183 match self
184 {
185 RGBString(s) =>
186 {
187 let s = s.to_string();
188 s.starts_with("0x") && s.len() == 10 || s.starts_with("#") && s.len() == 9
189 }
190 ARGBInteger(_, _, _, _) | VariableARGBInteger(_) => true,
191 _ => false,
192 }
193 }
194}
195
196fn from_argb(a: ColorComponent, r: ColorComponent, g: ColorComponent, b: ColorComponent)
197 -> ColorInt
198{
199 ((255 - a as ColorInt) << 24)
200 + ((r as ColorInt) << 16)
201 + ((g as ColorInt) << 8)
202 + (b as ColorInt)
203}
204
205fn from_string(argb: String) -> String
206{
207 if let Some(trimmed_argb) = argb.strip_prefix("0x").or_else(|| argb.strip_prefix("#"))
208 {
209 if trimmed_argb.len() == 8
210 {
211 if let Ok(argb_int) = ColorInt::from_str_radix(trimmed_argb, 16)
212 {
213 let a = 255 - ((argb_int >> 24) & 0xff);
214 let argb_int = (a << 24) + (argb_int & 0xffffff);
215 format!("#{:08x}", argb_int)
216 }
217 else
218 {
219 argb
221 }
222 }
223 else
224 {
225 argb
226 }
227 }
228 else
229 {
230 argb
231 }
232}
233
234fn float_color_to_int(v: f64) -> Result<u8, String>
235{
236 if !(0.0..=1.0).contains(&v)
237 {
238 Err(format!(
239 "Float value must be greater than zero and less than one. Actual value: {}",
240 v
241 ))
242 }
243 else
244 {
245 Ok(((v * 255.0).round()) as u8)
246 }
247}
248
249fn floats_to_rgb(r: f64, g: f64, b: f64) -> Result<RGBInts, String>
261{
262 Ok((
263 float_color_to_int(r)?,
264 float_color_to_int(g)?,
265 float_color_to_int(b)?,
266 ))
267}
268
269fn floats_to_argb(a: f64, r: f64, g: f64, b: f64) -> Result<ARGBInts, String>
282{
283 Ok((
284 float_color_to_int(a)?,
285 float_color_to_int(r)?,
286 float_color_to_int(g)?,
287 float_color_to_int(b)?,
288 ))
289}
290
291impl<'l> From<&'l str> for ColorType<String>
292{
293 fn from(value: &'l str) -> Self
295 {
296 ColorType::RGBString(String::from(value))
297 }
298}
299
300impl<'l> From<String> for ColorType<String>
301{
302 fn from(value: String) -> Self
304 {
305 ColorType::RGBString(value)
306 }
307}
308
309impl<'l> From<&'l str> for ColorType<&'l str>
310{
311 fn from(value: &'l str) -> Self
313 {
314 ColorType::RGBString(value)
315 }
316}
317
318impl<T> From<ARGBInts> for ColorType<T>
319{
320 fn from(value: ARGBInts) -> Self
322 {
323 ColorType::ARGBInteger(value.0, value.1, value.2, value.3)
324 }
325}
326
327impl<T> From<RGBInts> for ColorType<T>
328{
329 fn from(value: RGBInts) -> Self
331 {
332 ColorType::RGBInteger(value.0, value.1, value.2)
333 }
334}
335
336impl<T> TryFrom<(f64, f64, f64)> for ColorType<T>
337{
338 type Error = String;
339 fn try_from(value: (f64, f64, f64)) -> Result<Self, Self::Error>
342 {
343 let ints = floats_to_rgb(value.0, value.1, value.2)?;
344 Ok(ColorType::RGBInteger(ints.0, ints.1, ints.2))
345 }
346}
347
348impl<T> TryFrom<(f64, f64, f64, f64)> for ColorType<T>
349{
350 type Error = String;
351 fn try_from(value: (f64, f64, f64, f64)) -> Result<Self, Self::Error>
354 {
355 let ints = floats_to_argb(value.0, value.1, value.2, value.3)?;
356 Ok(ColorType::ARGBInteger(ints.0, ints.1, ints.2, ints.3))
357 }
358}
359
360impl<T> From<Vec<RGBInts>> for ColorType<T>
361{
362 fn from(value: Vec<RGBInts>) -> Self
364 {
365 ColorType::VariableRGBInteger(value)
366 }
367}
368
369impl<T> From<Vec<ARGBInts>> for ColorType<T>
370{
371 fn from(value: Vec<ARGBInts>) -> Self
373 {
374 ColorType::VariableARGBInteger(value)
375 }
376}
377
378impl<T> From<ColorIndex> for ColorType<T>
379{
380 fn from(value: ColorIndex) -> Self
382 {
383 ColorType::Index(value)
384 }
385}
386
387impl<T> From<Vec<ColorIndex>> for ColorType<T>
388{
389 fn from(value: Vec<ColorIndex>) -> Self
391 {
392 ColorType::VariableIndex(value)
393 }
394}
395
396impl<T: Display> OneWayOwned for ColorType<T>
397{
398 type Output = ColorType<String>;
399
400 fn to_one_way_owned(&self) -> ColorType<String>
401 {
402 match self
403 {
404 RGBString(s) => RGBString(s.to_string()),
405 RGBInteger(r, g, b) => RGBInteger(*r, *g, *b),
406 VariableRGBInteger(d) => VariableRGBInteger(d.clone()),
407 PaletteFracColor(v) => PaletteFracColor(*v),
408 PaletteCBColor(v) => PaletteCBColor(*v),
409 VariablePaletteColor(d) => VariablePaletteColor(d.clone()),
410 SavedColorMap(s, d) => SavedColorMap(s.to_string(), d.clone()),
411 VariableIndex(d) => VariableIndex(d.clone()),
412 Background => Background,
413 Index(n) => Index(*n),
414 Black => Black,
415 ARGBInteger(a, r, g, b) => ARGBInteger(*a, *r, *g, *b),
416 VariableARGBInteger(d) => VariableARGBInteger(d.clone()),
417 }
418 }
419}
420
421impl ColorType<String>
422{
423 pub fn to_ref(&self) -> ColorType<&str>
424 {
425 match self
426 {
427 RGBString(s) => RGBString(s),
428 RGBInteger(r, g, b) => RGBInteger(*r, *g, *b),
429 VariableRGBInteger(d) => VariableRGBInteger(d.to_vec()),
430 VariableARGBInteger(d) => VariableARGBInteger(d.to_vec()),
431 PaletteFracColor(v) => PaletteFracColor(*v),
432 PaletteCBColor(v) => PaletteCBColor(*v),
433 VariablePaletteColor(d) => VariablePaletteColor(d.to_vec()),
434 SavedColorMap(s, d) => SavedColorMap(s, d.to_vec()),
435 VariableIndex(d) => VariableIndex(d.to_vec()),
436 Background => Background,
437 Index(n) => Index(*n),
438 Black => Black,
439 ARGBInteger(a, r, g, b) => ARGBInteger(*a, *r, *g, *b),
440 }
441 }
442}