Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for transparency #162

Merged
merged 11 commits into from
May 26, 2022

Conversation

superhawk610
Copy link
Contributor

@superhawk610 superhawk610 commented May 1, 2022

This PR adds support for parsing and displaying partially transparent colors. It adds a new function Color::composite which implements simple alpha compositing, and uses this to enhance Canvas::draw_rect to support painting partially transparent pixels over existing pixels. This is used to enable drawing color previews for transparent colors (see example below).

Additionally, this PR enhances the parser to support parsing all common alpha formats, including:

  • RGBA
  • RRGGBBAA
  • rgba()
  • hsla()

The lab parser also supports specifying alpha. The alpha value may be provided as a double or percentage, and may be slash, comma, or space delimited; all of the following are valid:

  • rgba(255 127 0 0.4)
  • rgba(255, 127, 0, 0.4)
  • rgba(255, 127, 0, 40%)

For backwards compatibility, rgb() and hsl() also support alpha values. You can check the parse_alpha_syntax test to see all supported formats.

All Color::to_*_string methods now include the alpha value when it's not 1.0, and omit it when it is.

Closes #131.

Open Questions

This PR doesn't update the simplified format used when displaying colors with pastel format (the alpha channel value will be displayed, but the color itself will be fully opaque). It also doesn't add support for rendering colored text with partial transparency.

Example

$ pastel color 6633997f

image

src/lib.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated Show resolved Hide resolved
src/parser.rs Show resolved Hide resolved
src/parser.rs Outdated Show resolved Hide resolved
src/lib.rs Show resolved Hide resolved
Copy link
Owner

@sharkdp sharkdp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much!

Great PR description, implementation and tests!

@superhawk610 superhawk610 requested a review from sharkdp May 3, 2022 22:12
Copy link
Contributor Author

@superhawk610 superhawk610 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, updated with a few changes:

  • remove support for the / alpha notation
  • updated the CLI help for color to include alpha transparency notation
  • omit trailing zeroes when printing decimal alpha values

I originally had a function like this to implement max precision:

fn max_precision(value: f64, max_precision: u32) -> String {
    // (...snip...)
    format!("{}", rounded)
}

but I ended up going with a struct MaxPrecision that implements std::fmt::Display to save the String allocation. I'm still fairly new to Rust so let me know your thoughts on this 🙂.

src/lib.rs Outdated Show resolved Hide resolved
Comment on lines +1787 to +1799
#[test]
fn alpha_roundtrip_hex_to_decimal() {
// We use a max of 3 decimal places when displaying RGB floating point
// alpha values. This test insures that is sufficient to "roundtrip"
// from hex (0 < n < 255) to float (0 < n < 1) and back again,
// e.g. hex `80` is float `0.502`, which parses to hex `80`, and so on.
for alpha_int in 0..255 {
let hex_string = format!("#000000{:02x}", alpha_int);
let parsed_from_hex = hex_string.parse::<Color>().unwrap();
let rgba_string = parsed_from_hex.to_rgb_float_string(Format::Spaces);
let parsed_from_rgba = rgba_string.parse::<Color>().unwrap();
assert_eq!(hex_string, parsed_from_rgba.to_rgb_hex_string(true));
}
Copy link
Contributor Author

@superhawk610 superhawk610 May 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I think this should provide proper coverage. This asserts that:

  • for every hex alpha value, 0 < n < 255 (00 - fe)
  • parsing a color with this alpha, in hex notation
  • outputting that color as rgba(), with alpha as decimal
  • parsing that color
  • outputting that color using hex notation
  • generates the same value as the original hex input string

For example, #00000080 parses to a color that stringifies to rgba(0, 0, 0, 0.502), that in turn parses to a color that stringifies to #00000080. This actually caught a bug, where I was outputting alpha hex values using (f64 * 255.) as u8, when I should have been using (f64 * 255.).round() as u8.

I also threw in an impl FromStr for Color that calls parser::parse_color, that's not required, just thought it may be useful.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, thank you!

@superhawk610 superhawk610 requested a review from sharkdp May 26, 2022 03:53
Copy link
Owner

@sharkdp sharkdp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@sharkdp sharkdp merged commit 83e946b into sharkdp:master May 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for RGBA?
2 participants