pub use self::ColorType::*;
use crate::util::OneWayOwned;
use std::fmt::{Debug, Display};
pub type ColorIndex = u8;
pub type ColorComponent = u8;
pub type ColorInt = u32;
pub type RGBInts = (ColorComponent, ColorComponent, ColorComponent);
pub type ARGBInts = (
ColorComponent,
ColorComponent,
ColorComponent,
ColorComponent,
);
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ColorType<T = String>
{
RGBString(T),
RGBInteger(ColorComponent, ColorComponent, ColorComponent),
ARGBInteger(
ColorComponent,
ColorComponent,
ColorComponent,
ColorComponent,
),
VariableRGBInteger(Vec<RGBInts>),
VariableARGBInteger(Vec<ARGBInts>),
PaletteFracColor(f64),
PaletteCBColor(f64),
VariablePaletteColor(Vec<f64>),
SavedColorMap(T, Vec<f64>),
Index(ColorIndex),
VariableIndex(Vec<ColorIndex>),
Background,
Black,
}
impl<T: Display + Debug> ColorType<T>
{
pub fn command(&self) -> String
{
match self
{
RGBString(s) => format!(r#"rgb "{}""#, from_string(s.to_string())),
RGBInteger(r, g, b) => format!(r#"rgb {}"#, from_argb(255, *r, *g, *b)),
ARGBInteger(a, r, g, b) => format!(r#"rgb {}"#, from_argb(*a, *r, *g, *b)),
VariableRGBInteger(_) => "rgb variable".into(),
VariableARGBInteger(_) => "rgb variable".into(),
PaletteFracColor(v) => format!("palette frac {v}"),
PaletteCBColor(v) => format!("palette cb {v}"),
VariablePaletteColor(_) => "palette z".into(),
SavedColorMap(s, _) => format!("palette {s}"),
VariableIndex(_) => "variable".into(),
Background => "bgnd".into(),
Index(n) => format!("{}", n),
Black => "black".into(),
}
}
pub fn data(&self) -> Vec<f64>
{
match self
{
VariableRGBInteger(items) => items
.iter()
.map(|(r, g, b)| from_argb(255, *r, *g, *b) as f64)
.collect(),
VariableARGBInteger(items) => items
.iter()
.map(|(a, r, g, b)| from_argb(*a, *r, *g, *b) as f64)
.collect(),
VariablePaletteColor(items) => items.clone(),
SavedColorMap(_, items) => items.clone(),
VariableIndex(items) => items.iter().map(|v| *v as f64).collect(),
c => panic!("data() called on non-variable color type: {:?}", *c),
}
}
pub fn is_variable(&self) -> bool
{
matches!(
self,
VariableRGBInteger(_)
| VariableARGBInteger(_)
| VariableIndex(_)
| VariablePaletteColor(_)
| SavedColorMap(_, _)
)
}
pub fn has_alpha(&self) -> bool
{
match self
{
RGBString(s) =>
{
let s = s.to_string();
s.starts_with("0x") && s.len() == 10 || s.starts_with("#") && s.len() == 9
}
ARGBInteger(_, _, _, _) | VariableARGBInteger(_) => true,
_ => false,
}
}
}
fn from_argb(a: ColorComponent, r: ColorComponent, g: ColorComponent, b: ColorComponent)
-> ColorInt
{
((255 - a as ColorInt) << 24)
+ ((r as ColorInt) << 16)
+ ((g as ColorInt) << 8)
+ (b as ColorInt)
}
fn from_string(argb: String) -> String
{
if let Some(trimmed_argb) = argb.strip_prefix("0x").or_else(|| argb.strip_prefix("#"))
{
if trimmed_argb.len() == 8
{
if let Ok(argb_int) = ColorInt::from_str_radix(trimmed_argb, 16)
{
let a = 255 - ((argb_int >> 24) & 0xff);
let argb_int = (a << 24) + (argb_int & 0xffffff);
format!("#{:08x}", argb_int)
}
else
{
argb
}
}
else
{
argb
}
}
else
{
argb
}
}
fn float_color_to_int(v: f64) -> Result<u8, String>
{
if !(0.0..=1.0).contains(&v)
{
Err(format!(
"Float value must be greater than zero and less than one. Actual value: {}",
v
))
}
else
{
Ok(((v * 255.0).round()) as u8)
}
}
fn floats_to_rgb(r: f64, g: f64, b: f64) -> Result<RGBInts, String>
{
Ok((
float_color_to_int(r)?,
float_color_to_int(g)?,
float_color_to_int(b)?,
))
}
fn floats_to_argb(a: f64, r: f64, g: f64, b: f64) -> Result<ARGBInts, String>
{
Ok((
float_color_to_int(a)?,
float_color_to_int(r)?,
float_color_to_int(g)?,
float_color_to_int(b)?,
))
}
impl<'l> From<&'l str> for ColorType<String>
{
fn from(value: &'l str) -> Self
{
ColorType::RGBString(String::from(value))
}
}
impl<'l> From<String> for ColorType<String>
{
fn from(value: String) -> Self
{
ColorType::RGBString(value)
}
}
impl<'l> From<&'l str> for ColorType<&'l str>
{
fn from(value: &'l str) -> Self
{
ColorType::RGBString(value)
}
}
impl<T> From<ARGBInts> for ColorType<T>
{
fn from(value: ARGBInts) -> Self
{
ColorType::ARGBInteger(value.0, value.1, value.2, value.3)
}
}
impl<T> From<RGBInts> for ColorType<T>
{
fn from(value: RGBInts) -> Self
{
ColorType::RGBInteger(value.0, value.1, value.2)
}
}
impl<T> TryFrom<(f64, f64, f64)> for ColorType<T>
{
type Error = String;
fn try_from(value: (f64, f64, f64)) -> Result<Self, Self::Error>
{
let ints = floats_to_rgb(value.0, value.1, value.2)?;
Ok(ColorType::RGBInteger(ints.0, ints.1, ints.2))
}
}
impl<T> TryFrom<(f64, f64, f64, f64)> for ColorType<T>
{
type Error = String;
fn try_from(value: (f64, f64, f64, f64)) -> Result<Self, Self::Error>
{
let ints = floats_to_argb(value.0, value.1, value.2, value.3)?;
Ok(ColorType::ARGBInteger(ints.0, ints.1, ints.2, ints.3))
}
}
impl<T> From<Vec<RGBInts>> for ColorType<T>
{
fn from(value: Vec<RGBInts>) -> Self
{
ColorType::VariableRGBInteger(value)
}
}
impl<T> From<Vec<ARGBInts>> for ColorType<T>
{
fn from(value: Vec<ARGBInts>) -> Self
{
ColorType::VariableARGBInteger(value)
}
}
impl<T> From<ColorIndex> for ColorType<T>
{
fn from(value: ColorIndex) -> Self
{
ColorType::Index(value)
}
}
impl<T> From<Vec<ColorIndex>> for ColorType<T>
{
fn from(value: Vec<ColorIndex>) -> Self
{
ColorType::VariableIndex(value)
}
}
impl<T: Display> OneWayOwned for ColorType<T>
{
type Output = ColorType<String>;
fn to_one_way_owned(&self) -> ColorType<String>
{
match self
{
RGBString(s) => RGBString(s.to_string()),
RGBInteger(r, g, b) => RGBInteger(*r, *g, *b),
VariableRGBInteger(d) => VariableRGBInteger(d.clone()),
PaletteFracColor(v) => PaletteFracColor(*v),
PaletteCBColor(v) => PaletteCBColor(*v),
VariablePaletteColor(d) => VariablePaletteColor(d.clone()),
SavedColorMap(s, d) => SavedColorMap(s.to_string(), d.clone()),
VariableIndex(d) => VariableIndex(d.clone()),
Background => Background,
Index(n) => Index(*n),
Black => Black,
ARGBInteger(a, r, g, b) => ARGBInteger(*a, *r, *g, *b),
VariableARGBInteger(d) => VariableARGBInteger(d.clone()),
}
}
}
impl ColorType<String>
{
pub fn to_ref(&self) -> ColorType<&str>
{
match self
{
RGBString(s) => RGBString(s),
RGBInteger(r, g, b) => RGBInteger(*r, *g, *b),
VariableRGBInteger(d) => VariableRGBInteger(d.to_vec()),
VariableARGBInteger(d) => VariableARGBInteger(d.to_vec()),
PaletteFracColor(v) => PaletteFracColor(*v),
PaletteCBColor(v) => PaletteCBColor(*v),
VariablePaletteColor(d) => VariablePaletteColor(d.to_vec()),
SavedColorMap(s, d) => SavedColorMap(s, d.to_vec()),
VariableIndex(d) => VariableIndex(d.to_vec()),
Background => Background,
Index(n) => Index(*n),
Black => Black,
ARGBInteger(a, r, g, b) => ARGBInteger(*a, *r, *g, *b),
}
}
}