diff --git a/src/color.rs b/src/color.rs index b7266e1b9..b676be7c3 100644 --- a/src/color.rs +++ b/src/color.rs @@ -3,6 +3,21 @@ use std::fmt::{self, Debug, Formatter}; use std::str::FromStr; +/// A color in a dynamic format. +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum Color { + /// An 8-bit RGBA color. + Rgba(RgbaColor), +} + +impl Debug for Color { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Rgba(c) => c.fmt(f), + } + } +} + /// An 8-bit RGBA color. /// /// # Example @@ -20,34 +35,23 @@ pub struct RgbaColor { pub b: u8, /// Alpha channel. pub a: u8, - /// Whether the color was provided as a fail-over by the parser because the - /// user-defined value was invalid. - /// - /// If this is true, the color may be replaced with any color deemed - /// appropriate at the use-site. - pub healed: bool, } impl RgbaColor { - /// Constructs a new, unhealed color. + /// Constructs a new RGBA color. pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self { - Self { r, g, b, a, healed: false } - } - - /// Constructs a new color with a configurable healed status. - pub fn with_healed(r: u8, g: u8, b: u8, a: u8, healed: bool) -> Self { - Self { r, g, b, a, healed } + Self { r, g, b, a } } } impl FromStr for RgbaColor { - type Err = ParseColorError; + type Err = ParseRgbaError; /// Constructs a new color from a hex string like `7a03c2`. Do not specify a /// leading `#`. fn from_str(hex_str: &str) -> Result { if !hex_str.is_ascii() { - return Err(ParseColorError); + return Err(ParseRgbaError); } let len = hex_str.len(); @@ -56,7 +60,7 @@ impl FromStr for RgbaColor { let alpha = len == 4 || len == 8; if !long && !short { - return Err(ParseColorError); + return Err(ParseRgbaError); } let mut values: [u8; 4] = [255; 4]; @@ -66,7 +70,7 @@ impl FromStr for RgbaColor { let pos = elem * item_len; let item = &hex_str[pos .. (pos + item_len)]; - values[elem] = u8::from_str_radix(item, 16).map_err(|_| ParseColorError)?; + values[elem] = u8::from_str_radix(item, 16).map_err(|_| ParseRgbaError)?; if short { // Duplicate number for shorthand notation, i.e. `a` -> `aa` @@ -92,20 +96,17 @@ impl Debug for RgbaColor { write!(f, "{:02x}", self.a)?; } } - if self.healed { - f.write_str(" [healed]")?; - } Ok(()) } } -/// The error when parsing an [`RgbaColor`] fails. +/// The error when parsing an [`RgbaColor`] from a string fails. #[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct ParseColorError; +pub struct ParseRgbaError; -impl std::error::Error for ParseColorError {} +impl std::error::Error for ParseRgbaError {} -impl fmt::Display for ParseColorError { +impl fmt::Display for ParseRgbaError { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.pad("invalid color") } diff --git a/src/eval/mod.rs b/src/eval/mod.rs index c2b80b68f..ed73b07c7 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -20,6 +20,7 @@ use fontdock::FontStyle; use crate::diag::Diag; use crate::diag::{Deco, Feedback, Pass}; +use crate::color::Color; use crate::env::SharedEnv; use crate::geom::{BoxAlign, Dir, Flow, Gen, Length, Linear, Relative, Sides, Size}; use crate::layout::{ @@ -436,7 +437,7 @@ impl Eval for Lit { Lit::Float(v) => Value::Float(v), Lit::Length(v, unit) => Value::Length(Length::with_unit(v, unit)), Lit::Percent(v) => Value::Relative(Relative::new(v / 100.0)), - Lit::Color(v) => Value::Color(v), + Lit::Color(v) => Value::Color(Color::Rgba(v)), Lit::Str(ref v) => Value::Str(v.clone()), Lit::Dict(ref v) => Value::Dict(v.eval(ctx)), Lit::Content(ref v) => Value::Content(v.clone()), diff --git a/src/eval/value.rs b/src/eval/value.rs index 8c98257e3..a27e9aa9a 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -7,7 +7,7 @@ use std::rc::Rc; use fontdock::{FontStretch, FontStyle, FontWeight}; use super::{Args, Dict, Eval, EvalContext, SpannedEntry}; -use crate::color::RgbaColor; +use crate::color::Color; use crate::diag::Diag; use crate::geom::{Dir, Length, Linear, Relative}; use crate::paper::Paper; @@ -32,8 +32,8 @@ pub enum Value { Relative(Relative), /// A combination of an absolute length and a relative value: `20% + 5cm`. Linear(Linear), - /// A color value with alpha channel: `#f79143ff`. - Color(RgbaColor), + /// A color value: `#f79143ff`. + Color(Color), /// A string: `"string"`. Str(String), /// A dictionary value: `(false, 12cm, greeting="hi")`. @@ -285,6 +285,7 @@ try_from_match!(Linear["linear"]: Value::Length(v) => v.into(), Value::Relative(v) => v.into(), ); +try_from_match!(Color["color"]: Value::Color(v) => v); try_from_match!(String["string"]: Value::Str(v) => v); try_from_match!(SynTree["tree"]: Value::Content(v) => v); try_from_match!(ValueDict["dictionary"]: Value::Dict(v) => v); diff --git a/src/library/style.rs b/src/library/style.rs index 433802490..8b8d9f26e 100644 --- a/src/library/style.rs +++ b/src/library/style.rs @@ -2,9 +2,9 @@ use std::rc::Rc; use fontdock::{FontStretch, FontStyle, FontWeight}; -use crate::color::RgbaColor; use crate::eval::StringLike; use crate::geom::Linear; +use crate::color::{Color, RgbaColor}; use crate::prelude::*; /// `font`: Configure the font. @@ -152,22 +152,19 @@ pub fn rgb(mut args: Args, ctx: &mut EvalContext) -> Value { let a = args.get::<_, Spanned>(ctx, 3); args.done(ctx); - let mut healed = r.is_none() || g.is_none() || b.is_none(); let mut clamp = |component: Option>, default| { component.map_or(default, |c| { if c.v < 0.0 || c.v > 1.0 { ctx.diag(error!(c.span, "should be between 0.0 and 1.0")); - healed = true; } (c.v.max(0.0).min(1.0) * 255.0).round() as u8 }) }; - Value::Color(RgbaColor::with_healed( + Value::Color(Color::Rgba(RgbaColor::new( clamp(r, 0), clamp(g, 0), clamp(b, 0), clamp(a, 255), - healed, - )) + ))) } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 261bbabb8..04a9de584 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -487,9 +487,9 @@ fn ident(p: &mut Parser) -> Option { /// Parse a color. fn color(p: &mut Parser, hex: &str, start: Pos) -> RgbaColor { RgbaColor::from_str(hex).unwrap_or_else(|_| { - // Heal color by assuming black. + // Replace color with black. p.diag(error!(start .. p.pos(), "invalid color")); - RgbaColor::with_healed(0, 0, 0, 255, true) + RgbaColor::new(0, 0, 0, 255) }) } diff --git a/src/parse/tests.rs b/src/parse/tests.rs index 6d5eee384..1b6df74b3 100644 --- a/src/parse/tests.rs +++ b/src/parse/tests.rs @@ -436,7 +436,7 @@ fn test_parse_values() { e!("[val: [hi]]" => ); // Healed colors. - v!("#12345" => Color(RgbaColor::with_healed(0, 0, 0, 0xff, true))); + v!("#12345" => Color(RgbaColor::new(0, 0, 0, 0xff))); e!("[val: #12345]" => s(6, 12, "invalid color")); e!("[val: #a5]" => s(6, 9, "invalid color")); e!("[val: #14b2ah]" => s(6, 13, "invalid color")); diff --git a/tests/ref/func-rgb.png b/tests/ref/func-rgb.png index e394ccf1b..0f34d661d 100644 Binary files a/tests/ref/func-rgb.png and b/tests/ref/func-rgb.png differ