Add color enum 🎨

This commit is contained in:
Laurenz 2020-12-27 19:23:26 +01:00
parent c44ebf876f
commit 750d220bb0
7 changed files with 37 additions and 37 deletions

View File

@ -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<Self, Self::Err> {
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")
}

View File

@ -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()),

View File

@ -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);

View File

@ -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<f64>>(ctx, 3);
args.done(ctx);
let mut healed = r.is_none() || g.is_none() || b.is_none();
let mut clamp = |component: Option<Spanned<f64>>, 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,
))
)))
}

View File

@ -487,9 +487,9 @@ fn ident(p: &mut Parser) -> Option<Ident> {
/// 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)
})
}

View File

@ -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"));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB