use std::str;
use std::error;
use std::fmt;
use std::num;
use std::str::FromStr;
use crate::color::named_colors::NAMED_COLORS;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: f32,
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f,
"Color(r: {}, g: {}, b: {}, a: {})",
self.r,
self.g,
self.b,
self.a)
}
}
#[derive(Debug)]
pub struct ColorParseError;
impl From<num::ParseIntError> for ColorParseError {
#[allow(unused_variables)]
fn from(err: num::ParseIntError) -> ColorParseError {
return ColorParseError;
}
}
impl From<num::ParseFloatError> for ColorParseError {
#[allow(unused_variables)]
fn from(err: num::ParseFloatError) -> ColorParseError {
return ColorParseError;
}
}
impl error::Error for ColorParseError {
fn description(&self) -> &str {
"Failed to parse color"
}
fn cause(&self) -> Option<&dyn error::Error> {
None
}
}
impl fmt::Display for ColorParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ColorParseError: Invalid format")
}
}
impl str::FromStr for Color {
type Err = ColorParseError;
fn from_str(s: &str) -> Result<Self, ColorParseError> {
let s = s.trim();
if s.is_empty() {
return Err(ColorParseError);
}
let mut string = s.replace(' ', "");
string.make_ascii_lowercase();
if let Some(&color) = NAMED_COLORS.get(&*string) {
return Ok(color);
}
if string.starts_with("#") {
let string_char_count = string.chars().count();
if string_char_count == 4 {
let (_, value_string) = string.split_at(1);
let iv = u64::from_str_radix(value_string, 16)?;
if !(iv <= 0xfff) {
return Err(ColorParseError);
}
return Ok(Color {
r: (((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8)) as u8,
g: ((iv & 0xf0) | ((iv & 0xf0) >> 4)) as u8,
b: ((iv & 0xf) | ((iv & 0xf) << 4)) as u8,
a: 1.0,
});
} else if string_char_count == 7 {
let (_, value_string) = string.split_at(1);
let iv = u64::from_str_radix(value_string, 16)?;
if !(iv <= 0xffffff) {
return Err(ColorParseError);
}
return Ok(Color {
r: ((iv & 0xff0000) >> 16) as u8,
g: ((iv & 0xff00) >> 8) as u8,
b: (iv & 0xff) as u8,
a: 1.0,
});
}
return Err(ColorParseError);
}
let op = string.find("(").ok_or(ColorParseError)?;
let ep = string.find(")").ok_or(ColorParseError)?;
if (ep + 1) != string.len() || ep < op {
return Err(ColorParseError);
}
let (fmt, right_string_half) = string.split_at(op);
if fmt.is_empty() {
return Err(ColorParseError);
}
let mut filtered_right_string_half = right_string_half.to_string();
filtered_right_string_half.remove(0);
filtered_right_string_half.pop();
let params: Vec<&str> = filtered_right_string_half.split(",").collect();
if params.len() < 3 || params.len() > 4 {
return Err(ColorParseError);
}
if fmt == "rgba" {
return parse_rgba(params);
} else if fmt == "rgb" {
return parse_rgb(params);
} else if fmt == "hsla" {
return parse_hsla(params);
} else if fmt == "hsl" {
return parse_hsl(params);
}
return Err(ColorParseError);
}
}
fn parse_rgba(mut rgba: Vec<&str>) -> Result<Color, ColorParseError> {
if rgba.len() != 4 {
return Err(ColorParseError);
}
let a_str = rgba.pop().ok_or(ColorParseError)?;
let a = parse_css_float(a_str)?;
let mut rgb_color = parse_rgb(rgba)?;
rgb_color = Color { a: a, ..rgb_color };
return Ok(rgb_color);
}
fn parse_rgb(mut rgb: Vec<&str>) -> Result<Color, ColorParseError> {
if rgb.len() != 3 {
return Err(ColorParseError);
}
let b_str = rgb.pop().ok_or(ColorParseError)?;
let g_str = rgb.pop().ok_or(ColorParseError)?;
let r_str = rgb.pop().ok_or(ColorParseError)?;
let r = parse_css_int(r_str)?;
let g = parse_css_int(g_str)?;
let b = parse_css_int(b_str)?;
return Ok(Color {
r: r,
g: g,
b: b,
a: 1.0,
});
}
fn parse_hsla(mut hsla: Vec<&str>) -> Result<Color, ColorParseError> {
if hsla.len() != 4 {
return Err(ColorParseError);
}
let a_str = hsla.pop().ok_or(ColorParseError)?;
let a = parse_css_float(a_str)?;
let mut rgb_color: Color = parse_hsl(hsla)?;
rgb_color = Color { a: a, ..rgb_color };
return Ok(rgb_color);
}
fn parse_hsl(mut hsl: Vec<&str>) -> Result<Color, ColorParseError> {
if hsl.len() != 3 {
return Err(ColorParseError);
}
let l_str = hsl.pop().ok_or(ColorParseError)?;
let s_str = hsl.pop().ok_or(ColorParseError)?;
let h_str = hsl.pop().ok_or(ColorParseError)?;
let mut h = f32::from_str(h_str)?;
h = (((h % 360.0) + 360.0) % 360.0) / 360.0;
let s = parse_css_float(s_str)?;
let l = parse_css_float(l_str)?;
let m2: f32;
if l <= 0.5 {
m2 = l * (s + 1.0)
} else {
m2 = l + s - l * s;
}
let m1 = l * 2.0 - m2;
let r = clamp_css_byte_from_float(css_hue_to_rgb(m1, m2, h + 1.0 / 3.0) * 255.0);
let g = clamp_css_byte_from_float(css_hue_to_rgb(m1, m2, h) * 255.0);
let b = clamp_css_byte_from_float(css_hue_to_rgb(m1, m2, h - 1.0 / 3.0) * 255.0);
return Ok(Color {
r: r,
g: g,
b: b,
a: 1.0,
});
}
fn parse_css_float(fv_str: &str) -> Result<f32, num::ParseFloatError> {
let fv: f32;
if fv_str.ends_with("%") {
let mut percentage_string = fv_str.to_string();
percentage_string.pop();
fv = f32::from_str(&percentage_string)?;
return Ok(clamp_css_float(fv / 100.0));
}
fv = f32::from_str(fv_str)?;
return Ok(clamp_css_float(fv));
}
fn parse_css_int(iv_or_percentage_str: &str) -> Result<u8, ColorParseError> {
if iv_or_percentage_str.ends_with("%") {
let mut percentage_string = iv_or_percentage_str.to_string();
percentage_string.pop();
let fv = f32::from_str(&percentage_string)?;
return Ok(clamp_css_byte_from_float(fv / 100.0 * 255.0));
}
let iv = u32::from_str(iv_or_percentage_str)?;
return Ok(clamp_css_byte(iv));
}
fn clamp_css_float(fv: f32) -> f32 {
if fv < 0.0 {
0.0
} else if fv > 1.0 {
1.0
} else {
fv
}
}
fn clamp_css_byte_from_float(mut fv: f32) -> u8 {
fv = fv.round();
if fv < 0.0 {
0
} else if fv > 255.0 {
255
} else {
fv as u8
}
}
fn clamp_css_byte(iv: u32) -> u8 {
if iv > 255 { 255 } else { iv as u8 }
}
fn css_hue_to_rgb(m1: f32, m2: f32, mut h: f32) -> f32 {
if h < 0.0 {
h += 1.0;
} else if h > 1.0 {
h -= 1.0;
}
if h * 6.0 < 1.0 {
return m1 + (m2 - m1) * h * 6.0;
}
if h * 2.0 < 1.0 {
return m2;
}
if h * 3.0 < 2.0 {
return m1 + (m2 - m1) * (2.0 / 3.0 - h) * 6.0;
}
return m1;
}