From 750d220bb080be077cd7ede6d18d485b1c3fb0c9 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sun, 27 Dec 2020 19:23:26 +0100 Subject: [PATCH] =?UTF-8?q?Add=20color=20enum=20=F0=9F=8E=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/color.rs | 49 +++++++++++++++++++++-------------------- src/eval/mod.rs | 3 ++- src/eval/value.rs | 7 +++--- src/library/style.rs | 9 +++----- src/parse/mod.rs | 4 ++-- src/parse/tests.rs | 2 +- tests/ref/func-rgb.png | Bin 4142 -> 3153 bytes 7 files changed, 37 insertions(+), 37 deletions(-) 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 e394ccf1b9e4f7e5cb9827cc366103dfe0aa03f3..0f34d661d02a4e27e61c900f6253e38172a941cc 100644 GIT binary patch delta 2954 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 AwEzGB delta 3958 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 Q(EtDd07*qoM6N<$g3