From b02ba84264831ee97e7852f1e33cc78941dba13c Mon Sep 17 00:00:00 2001 From: Laurenz Date: Thu, 17 Dec 2020 12:16:17 +0100 Subject: [PATCH] =?UTF-8?q?Test=20[rgb]=20=F0=9F=8E=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/color.rs | 17 ++++++++--------- src/library/style.rs | 29 ++++++++++++++++------------- src/parse/mod.rs | 3 ++- src/parse/tests.rs | 11 ++++++----- tests/ref/rgb.png | Bin 0 -> 4142 bytes tests/typ/rgb.typ | 24 ++++++++++++++++++++++++ tests/typeset.rs | 15 ++++++++------- 7 files changed, 64 insertions(+), 35 deletions(-) create mode 100644 tests/ref/rgb.png create mode 100644 tests/typ/rgb.typ diff --git a/src/color.rs b/src/color.rs index 922b9a16e..427557d2b 100644 --- a/src/color.rs +++ b/src/color.rs @@ -29,14 +29,14 @@ pub struct RgbaColor { } impl RgbaColor { - /// Constructs a new color. + /// Constructs a new, unhealed 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 the healed property set to true. - pub fn new_healed(r: u8, g: u8, b: u8, a: u8) -> Self { - Self { r, g, b, a, healed: true } + /// 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 } } } @@ -87,11 +87,10 @@ impl Debug for RgbaColor { self.r, self.g, self.b, self.a, )?; } else { - write!( - f, - "#{:02x}{:02x}{:02x}{:02x}", - self.r, self.g, self.b, self.a, - )?; + write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?; + if self.a != 255 { + write!(f, "{:02x}", self.a)?; + } } if self.healed { f.write_str(" [healed]")?; diff --git a/src/library/style.rs b/src/library/style.rs index bb4725365..804e94247 100644 --- a/src/library/style.rs +++ b/src/library/style.rs @@ -117,30 +117,33 @@ pub fn font(mut args: Args, ctx: &mut EvalContext) -> Value { /// `rgb`: Create an RGB(A) color. /// /// # Positional arguments -/// - The red component (integer between 0 and 255). -/// - The green component (integer between 0 and 255). -/// - The blue component (integer between 0 and 255). -/// - The alpha component (optional, integer between 0 and 255). +/// - The red component (float between 0.0 and 1.0). +/// - The green component (float between 0.0 and 1.0). +/// - The blue component (float between 0.0 and 1.0). +/// - The alpha component (optional, float between 0.0 and 1.0). pub fn rgb(mut args: Args, ctx: &mut EvalContext) -> Value { - let r = args.need::<_, Spanned>(ctx, 0, "red value"); - let g = args.need::<_, Spanned>(ctx, 1, "green value"); - let b = args.need::<_, Spanned>(ctx, 2, "blue value"); - let a = args.get::<_, Spanned>(ctx, 3); + let r = args.need::<_, Spanned>(ctx, 0, "red component"); + let g = args.need::<_, Spanned>(ctx, 1, "green component"); + let b = args.need::<_, Spanned>(ctx, 2, "blue component"); + let a = args.get::<_, Spanned>(ctx, 3); args.done(ctx); - let mut clamp = |component: Option>, default| { + 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 || c.v > 255 { - ctx.diag(error!(c.span, "should be between 0 and 255")); + 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).min(255) as u8 + (c.v.max(0.0).min(1.0) * 255.0).round() as u8 }) }; - Value::Color(RgbaColor::new( + Value::Color(RgbaColor::with_healed( 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 61c90f6c5..261bbabb8 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -196,6 +196,7 @@ fn bracket_subheader(p: &mut Parser) -> ExprCall { p.skip_white(); let args = if p.eat_if(Token::Colon) { + p.skip_white(); p.span(|p| dict_contents(p).0) } else { // Ignore the rest if there's no colon. @@ -488,7 +489,7 @@ fn color(p: &mut Parser, hex: &str, start: Pos) -> RgbaColor { RgbaColor::from_str(hex).unwrap_or_else(|_| { // Heal color by assuming black. p.diag(error!(start .. p.pos(), "invalid color")); - RgbaColor::new_healed(0, 0, 0, 255) + RgbaColor::with_healed(0, 0, 0, 255, true) }) } diff --git a/src/parse/tests.rs b/src/parse/tests.rs index 2fd6d0b11..6d5eee384 100644 --- a/src/parse/tests.rs +++ b/src/parse/tests.rs @@ -193,6 +193,7 @@ macro_rules! d { /// and the source of their test case if they aren't. /// /// When `cmp_spans` is false, spans are ignored. +#[track_caller] pub fn check(src: &str, exp: T, found: T, cmp_spans: bool) where T: Debug + PartialEq, @@ -435,7 +436,7 @@ fn test_parse_values() { e!("[val: [hi]]" => ); // Healed colors. - v!("#12345" => Color(RgbaColor::new_healed(0, 0, 0, 0xff))); + v!("#12345" => Color(RgbaColor::with_healed(0, 0, 0, 0xff, true))); e!("[val: #12345]" => s(6, 12, "invalid color")); e!("[val: #a5]" => s(6, 9, "invalid color")); e!("[val: #14b2ah]" => s(6, 13, "invalid color")); @@ -447,7 +448,7 @@ fn test_parse_values() { s(13, 13, "expected closing bracket")); // Spanned. - ts!("[val: 1.4]" => s(0, 10, F!(s(1, 4, "val"), 5 .. 9; s(6, 9, Float(1.4))))); + ts!("[val: 1.4]" => s(0, 10, F!(s(1, 4, "val"), 6 .. 9; s(6, 9, Float(1.4))))); } #[test] @@ -485,7 +486,7 @@ fn test_parse_expressions() { // Spanned. ts!("[val: 1 + 3]" => s(0, 12, F!( - s(1, 4, "val"), 5 .. 11; s(6, 11, Binary( + s(1, 4, "val"), 6 .. 11; s(6, 11, Binary( s(8, 9, Add), s(6, 7, Int(1)), s(10, 11, Int(3)) @@ -493,7 +494,7 @@ fn test_parse_expressions() { ))); // Span of parenthesized expression contains parens. - ts!("[val: (1)]" => s(0, 10, F!(s(1, 4, "val"), 5 .. 9; s(6, 9, Int(1))))); + ts!("[val: (1)]" => s(0, 10, F!(s(1, 4, "val"), 6 .. 9; s(6, 9, Int(1))))); // Invalid expressions. v!("4pt--" => Length(4.0, Pt)); @@ -522,7 +523,7 @@ fn test_parse_dicts() { // Spanned with spacing around keyword arguments. ts!("[val: \n hi \n = /* //\n */ \"s\n\"]" => s(0, 30, F!( s(1, 4, "val"), - 5 .. 29; s(8, 10, "hi") => s(25, 29, Str("s\n")) + 8 .. 29; s(8, 10, "hi") => s(25, 29, Str("s\n")) ))); e!("[val: \n hi \n = /* //\n */ \"s\n\"]" => ); } diff --git a/tests/ref/rgb.png b/tests/ref/rgb.png new file mode 100644 index 0000000000000000000000000000000000000000..e394ccf1b9e4f7e5cb9827cc366103dfe0aa03f3 GIT binary patch literal 4142 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 literal 0 HcmV?d00001 diff --git a/tests/typ/rgb.typ b/tests/typ/rgb.typ new file mode 100644 index 000000000..44d878192 --- /dev/null +++ b/tests/typ/rgb.typ @@ -0,0 +1,24 @@ +// Test the `rgb` function. + +// Check the output. +[rgb: 0.0, 0.3, 0.7] [val: #004db3] + +// Alpha channel. +[rgb: 1.0, 0.0, 0.0, 0.5] + +// Value smaller than 0.0 and larger than 1.0 +[rgb: -30, 15.5, 0.5] + +// Missing blue component. +[rgb: 0, 1] + +// Missing all components. +[rgb] + +// error: 4:23-4:26 unknown function +// error: 10:7-10:10 should be between 0.0 and 1.0 +// error: 10:12-10:16 should be between 0.0 and 1.0 +// error: 13:7-13:11 missing argument: blue component +// error: 16:5-16:5 missing argument: red component +// error: 16:5-16:5 missing argument: green component +// error: 16:5-16:5 missing argument: blue component diff --git a/tests/typeset.rs b/tests/typeset.rs index 5f1085858..01576cf09 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -290,15 +290,16 @@ fn draw_text(canvas: &mut Canvas, pos: Point, env: &Env, shaped: &Shaped) { let mut builder = WrappedPathBuilder(PathBuilder::new()); face.outline_glyph(glyph, &mut builder); - let path = builder.0.finish().unwrap(); - let placed = path - .transform(&Transform::from_row(scale, 0.0, 0.0, -scale, x, y).unwrap()) - .unwrap(); + if let Some(path) = builder.0.finish() { + let placed = path + .transform(&Transform::from_row(scale, 0.0, 0.0, -scale, x, y).unwrap()) + .unwrap(); - let mut paint = Paint::default(); - paint.anti_alias = true; + let mut paint = Paint::default(); + paint.anti_alias = true; - canvas.fill_path(&placed, &paint, FillRule::default()); + canvas.fill_path(&placed, &paint, FillRule::default()); + } } }