diff --git a/src/builtin/color.rs b/src/builtin/color.rs index a80b075..d1ab180 100644 --- a/src/builtin/color.rs +++ b/src/builtin/color.rs @@ -150,4 +150,10 @@ pub(crate) fn register(f: &mut BTreeMap) { _ => todo!("non-color given to builtin function `alpha()`") } }); + decl!(f "invert", |args, _| { + match arg!(args, 0, "color") { + Value::Color(c) => Some(Value::Color(c.invert())), + _ => todo!("non-color given to builtin function `alpha()`") + } + }); } diff --git a/src/color/mod.rs b/src/color/mod.rs index 8d1e6a2..52cdfc4 100644 --- a/src/color/mod.rs +++ b/src/color/mod.rs @@ -10,15 +10,15 @@ mod name; #[derive(Debug, Clone, Eq, PartialEq)] pub(crate) struct Color { - red: u16, - green: u16, - blue: u16, + red: u8, + green: u8, + blue: u8, alpha: Number, repr: String, } impl Color { - pub fn new(red: u16, green: u16, blue: u16, alpha: u16, repr: String) -> Self { + pub fn new(red: u8, green: u8, blue: u8, alpha: u8, repr: String) -> Self { Color { red, green, @@ -28,15 +28,15 @@ impl Color { } } - pub const fn red(&self) -> u16 { + pub const fn red(&self) -> u8 { self.red } - pub const fn blue(&self) -> u16 { + pub const fn blue(&self) -> u8 { self.blue } - pub const fn green(&self) -> u16 { + pub const fn green(&self) -> u8 { self.green } @@ -122,7 +122,7 @@ impl Color { }; let val = (luminance.clone() * Number::from(255)) .to_integer() - .to_u16() + .to_u8() .unwrap(); let repr = repr(val, val, val, &alpha); return Color { @@ -175,8 +175,8 @@ impl Color { } * Number::from(255)) .round() .to_integer() - .to_u16() - .expect("expected channel to fit inside u16"); + .to_u8() + .expect("expected channel to fit inside u8"); }; } @@ -198,11 +198,11 @@ impl Color { macro_rules! clamp { ($channel:ident) => { let $channel = if $channel > Number::from(255) { - 255_u16 + 255_u8 } else if $channel < Number::from(0) { - 0_u16 + 0_u8 } else { - $channel.round().to_integer().to_u16().unwrap() + $channel.round().to_integer().to_u8().unwrap() }; }; } @@ -211,6 +211,14 @@ impl Color { clamp!(green); clamp!(blue); + let alpha = if alpha > Number::from(1) { + Number::from(1) + } else if alpha < Number::from(0) { + Number::from(0) + } else { + alpha + }; + let repr = repr(red, green, blue, &alpha); Color { red, @@ -220,10 +228,24 @@ impl Color { repr, } } + + pub fn invert(&self) -> Self { + let red = std::u8::MAX - self.red; + let green = std::u8::MAX - self.green; + let blue = std::u8::MAX - self.blue; + let repr = repr(red, green, blue, &self.alpha); + Color { + red, + green, + blue, + alpha: self.alpha.clone(), + repr, + } + } } /// Get the proper representation from RGBA values -fn repr(red: u16, green: u16, blue: u16, alpha: &Number) -> String { +fn repr(red: u8, green: u8, blue: u8, alpha: &Number) -> String { if alpha < &Number::from(1) { format!("rgba({}, {}, {}, {})", red, green, blue, alpha) } else if let Ok(c) = ColorName::try_from([red, green, blue]) { diff --git a/src/color/name.rs b/src/color/name.rs index 7b18b68..737d24d 100644 --- a/src/color/name.rs +++ b/src/color/name.rs @@ -469,10 +469,11 @@ impl TryFrom<&str> for ColorName { } } } -impl TryFrom<[u16; 3]> for ColorName { + +impl TryFrom<[u8; 3]> for ColorName { type Error = &'static str; - fn try_from(c: [u16; 3]) -> Result { + fn try_from(c: [u8; 3]) -> Result { match c { [0xF0, 0xF8, 0xFF] => Ok(Self::AliceBlue), [0xFA, 0xEB, 0xD7] => Ok(Self::AntiqueWhite), diff --git a/src/value/number.rs b/src/value/number.rs index 99bee31..7ba24be 100644 --- a/src/value/number.rs +++ b/src/value/number.rs @@ -61,6 +61,7 @@ macro_rules! from_integer { from_integer!(u16); from_integer!(usize); from_integer!(i32); +from_integer!(u8); impl Display for Number { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/src/value/parse.rs b/src/value/parse.rs index 1a5543b..e682bcc 100644 --- a/src/value/parse.rs +++ b/src/value/parse.rs @@ -20,32 +20,32 @@ fn parse_hex(s: String) -> Value { match s.len() { 3 => { let v = u16::from_str_radix(&s, 16).unwrap(); - let red = ((v & 0xf00) >> 8) * 0x11; - let green = ((v & 0x0f0) >> 4) * 0x11; - let blue = (v & 0x00f) * 0x11; + let red = (((v & 0xf00) >> 8) * 0x11) as u8; + let green = (((v & 0x0f0) >> 4) * 0x11) as u8; + let blue = ((v & 0x00f) * 0x11) as u8; Value::Color(Color::new(red, green, blue, 1, format!("#{}", s))) } 4 => { let v = u16::from_str_radix(&s, 16).unwrap(); - let red = ((v & 0xf000) >> 12) * 0x11; - let green = ((v & 0x0f00) >> 8) * 0x11; - let blue = ((v & 0x00f0) >> 4) * 0x11; - let alpha = (v & 0x000f) * 0x11; + let red = (((v & 0xf000) >> 12) * 0x11) as u8; + let green = (((v & 0x0f00) >> 8) * 0x11) as u8; + let blue = (((v & 0x00f0) >> 4) * 0x11) as u8; + let alpha = ((v & 0x000f) * 0x11) as u8; Value::Color(Color::new(red, green, blue, alpha, format!("#{}", s))) } 6 => { let v = u32::from_str_radix(&s, 16).unwrap(); - let red: u16 = ((v & 0x00ff_0000) >> 16) as u16; - let green: u16 = ((v & 0x0000_ff00) >> 8) as u16; - let blue: u16 = (v & 0x0000_00ff) as u16; + let red = ((v & 0x00ff_0000) >> 16) as u8; + let green = ((v & 0x0000_ff00) >> 8) as u8; + let blue = (v & 0x0000_00ff) as u8; Value::Color(Color::new(red, green, blue, 1, format!("#{}", s))) } 8 => { let v = u32::from_str_radix(&s, 16).unwrap(); - let red = ((v & 0xff00_0000) >> 24) as u16; - let green = ((v & 0x00ff_0000) >> 16) as u16; - let blue = ((v & 0x0000_ff00) >> 8) as u16; - let alpha = (v & 0x0000_00ff) as u16; + let red = ((v & 0xff00_0000) >> 24) as u8; + let green = ((v & 0x00ff_0000) >> 16) as u8; + let blue = ((v & 0x0000_ff00) >> 8) as u8; + let alpha = (v & 0x0000_00ff) as u8; Value::Color(Color::new(red, green, blue, alpha, format!("#{}", s))) } _ => Value::Ident(s, QuoteKind::None), diff --git a/tests/color.rs b/tests/color.rs index db913d2..b0e92e1 100644 --- a/tests/color.rs +++ b/tests/color.rs @@ -171,3 +171,8 @@ test!( "$a: hsl(193, 67%, 28%);\n\na {\n color: lightness($a);\n}\n", "a {\n color: 28%;\n}\n" ); +test!( + invert_no_weight, + "a {\n color: invert(white);\n}\n", + "a {\n color: black;\n}\n" +);