Skip to content

Commit a7f9637

Browse files
committed
Rgba does not store Option as component
Legacy rgb/rgba syntax does not allow the "none" keyword and although the modern rgb/rgba syntax does support it, it is immediately converted to 0, because we never have to serialze back to "none" keywords for rgb/rgba.
1 parent aec3025 commit a7f9637

File tree

3 files changed

+70
-84
lines changed

3 files changed

+70
-84
lines changed

src/color.rs

Lines changed: 58 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -81,44 +81,34 @@ fn normalize_hue(hue: f32) -> f32 {
8181

8282
/// A color with red, green, blue, and alpha components, in a byte each.
8383
#[derive(Clone, Copy, PartialEq, Debug)]
84-
pub struct RGBA {
84+
pub struct Rgba {
8585
/// The red component.
86-
pub red: Option<u8>,
86+
pub red: u8,
8787
/// The green component.
88-
pub green: Option<u8>,
88+
pub green: u8,
8989
/// The blue component.
90-
pub blue: Option<u8>,
90+
pub blue: u8,
9191
/// The alpha component.
92-
pub alpha: Option<f32>,
92+
pub alpha: f32,
9393
}
9494

95-
impl RGBA {
95+
impl Rgba {
9696
/// Constructs a new RGBA value from float components. It expects the red,
9797
/// green, blue and alpha channels in that order, and all values will be
9898
/// clamped to the 0.0 ... 1.0 range.
9999
#[inline]
100-
pub fn from_floats(
101-
red: Option<f32>,
102-
green: Option<f32>,
103-
blue: Option<f32>,
104-
alpha: Option<f32>,
105-
) -> Self {
100+
pub fn from_floats(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
106101
Self::new(
107-
red.map(clamp_unit_f32),
108-
green.map(clamp_unit_f32),
109-
blue.map(clamp_unit_f32),
110-
alpha.map(|a| a.clamp(0.0, OPAQUE)),
102+
clamp_unit_f32(red),
103+
clamp_unit_f32(green),
104+
clamp_unit_f32(blue),
105+
alpha.clamp(0.0, OPAQUE),
111106
)
112107
}
113108

114109
/// Same thing, but with `u8` values instead of floats in the 0 to 1 range.
115110
#[inline]
116-
pub const fn new(
117-
red: Option<u8>,
118-
green: Option<u8>,
119-
blue: Option<u8>,
120-
alpha: Option<f32>,
121-
) -> Self {
111+
pub const fn new(red: u8, green: u8, blue: u8, alpha: f32) -> Self {
122112
Self {
123113
red,
124114
green,
@@ -129,7 +119,7 @@ impl RGBA {
129119
}
130120

131121
#[cfg(feature = "serde")]
132-
impl Serialize for RGBA {
122+
impl Serialize for Rgba {
133123
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
134124
where
135125
S: Serializer,
@@ -139,32 +129,32 @@ impl Serialize for RGBA {
139129
}
140130

141131
#[cfg(feature = "serde")]
142-
impl<'de> Deserialize<'de> for RGBA {
132+
impl<'de> Deserialize<'de> for Rgba {
143133
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
144134
where
145135
D: Deserializer<'de>,
146136
{
147137
let (r, g, b, a) = Deserialize::deserialize(deserializer)?;
148-
Ok(RGBA::new(r, g, b, a))
138+
Ok(Rgba::new(r, g, b, a))
149139
}
150140
}
151141

152-
impl ToCss for RGBA {
142+
impl ToCss for Rgba {
153143
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
154144
where
155145
W: fmt::Write,
156146
{
157-
let has_alpha = self.alpha.unwrap_or(0.0) != OPAQUE;
147+
let has_alpha = self.alpha != OPAQUE;
158148

159149
dest.write_str(if has_alpha { "rgba(" } else { "rgb(" })?;
160-
self.red.unwrap_or(0).to_css(dest)?;
150+
self.red.to_css(dest)?;
161151
dest.write_str(", ")?;
162-
self.green.unwrap_or(0).to_css(dest)?;
152+
self.green.to_css(dest)?;
163153
dest.write_str(", ")?;
164-
self.blue.unwrap_or(0).to_css(dest)?;
154+
self.blue.to_css(dest)?;
165155

166156
// Legacy syntax does not allow none components.
167-
serialize_color_alpha(dest, Some(self.alpha.unwrap_or(0.0)), true)?;
157+
serialize_color_alpha(dest, Some(self.alpha), true)?;
168158

169159
dest.write_char(')')
170160
}
@@ -212,7 +202,7 @@ impl ToCss for Hsl {
212202
self.lightness.unwrap_or(0.0),
213203
);
214204

215-
RGBA::from_floats(Some(red), Some(green), Some(blue), self.alpha).to_css(dest)
205+
Rgba::from_floats(red, green, blue, self.alpha.unwrap_or(OPAQUE)).to_css(dest)
216206
}
217207
}
218208

@@ -279,7 +269,7 @@ impl ToCss for Hwb {
279269
self.blackness.unwrap_or(0.0),
280270
);
281271

282-
RGBA::from_floats(Some(red), Some(green), Some(blue), self.alpha).to_css(dest)
272+
Rgba::from_floats(red, green, blue, self.alpha.unwrap_or(OPAQUE)).to_css(dest)
283273
}
284274
}
285275

@@ -620,7 +610,7 @@ pub enum Color {
620610
/// The 'currentcolor' keyword.
621611
CurrentColor,
622612
/// Specify sRGB colors directly by their red/green/blue/alpha chanels.
623-
Rgba(RGBA),
613+
Rgba(Rgba),
624614
/// Specifies a color in sRGB using hue, saturation and lightness components.
625615
Hsl(Hsl),
626616
/// Specifies a color in sRGB using hue, whiteness and blackness components.
@@ -811,7 +801,7 @@ pub trait FromParsedColor {
811801
fn from_current_color() -> Self;
812802

813803
/// Construct a new color from red, green, blue and alpha components.
814-
fn from_rgba(red: Option<u8>, green: Option<u8>, blue: Option<u8>, alpha: Option<f32>) -> Self;
804+
fn from_rgba(red: u8, green: u8, blue: u8, alpha: f32) -> Self;
815805

816806
/// Construct a new color from hue, saturation, lightness and alpha components.
817807
fn from_hsl(
@@ -875,28 +865,28 @@ where
875865
{
876866
Ok(match value.len() {
877867
8 => O::from_rgba(
878-
Some(from_hex(value[0])? * 16 + from_hex(value[1])?),
879-
Some(from_hex(value[2])? * 16 + from_hex(value[3])?),
880-
Some(from_hex(value[4])? * 16 + from_hex(value[5])?),
881-
Some((from_hex(value[6])? * 16 + from_hex(value[7])?) as f32 / 255.0),
868+
from_hex(value[0])? * 16 + from_hex(value[1])?,
869+
from_hex(value[2])? * 16 + from_hex(value[3])?,
870+
from_hex(value[4])? * 16 + from_hex(value[5])?,
871+
(from_hex(value[6])? * 16 + from_hex(value[7])?) as f32 / 255.0,
882872
),
883873
6 => O::from_rgba(
884-
Some(from_hex(value[0])? * 16 + from_hex(value[1])?),
885-
Some(from_hex(value[2])? * 16 + from_hex(value[3])?),
886-
Some(from_hex(value[4])? * 16 + from_hex(value[5])?),
887-
Some(OPAQUE),
874+
from_hex(value[0])? * 16 + from_hex(value[1])?,
875+
from_hex(value[2])? * 16 + from_hex(value[3])?,
876+
from_hex(value[4])? * 16 + from_hex(value[5])?,
877+
OPAQUE,
888878
),
889879
4 => O::from_rgba(
890-
Some(from_hex(value[0])? * 17),
891-
Some(from_hex(value[1])? * 17),
892-
Some(from_hex(value[2])? * 17),
893-
Some((from_hex(value[3])? * 17) as f32 / 255.0),
880+
from_hex(value[0])? * 17,
881+
from_hex(value[1])? * 17,
882+
from_hex(value[2])? * 17,
883+
(from_hex(value[3])? * 17) as f32 / 255.0,
894884
),
895885
3 => O::from_rgba(
896-
Some(from_hex(value[0])? * 17),
897-
Some(from_hex(value[1])? * 17),
898-
Some(from_hex(value[2])? * 17),
899-
Some(OPAQUE),
886+
from_hex(value[0])? * 17,
887+
from_hex(value[1])? * 17,
888+
from_hex(value[2])? * 17,
889+
OPAQUE,
900890
),
901891
_ => return Err(()),
902892
})
@@ -934,8 +924,8 @@ impl FromParsedColor for Color {
934924
}
935925

936926
#[inline]
937-
fn from_rgba(red: Option<u8>, green: Option<u8>, blue: Option<u8>, alpha: Option<f32>) -> Self {
938-
Color::Rgba(RGBA::new(red, green, blue, alpha))
927+
fn from_rgba(red: u8, green: u8, blue: u8, alpha: f32) -> Self {
928+
Color::Rgba(Rgba::new(red, green, blue, alpha))
939929
}
940930

941931
fn from_hsl(
@@ -1173,10 +1163,10 @@ where
11731163
}
11741164

11751165
match_ignore_ascii_case! { ident ,
1176-
"transparent" => Ok(Output::from_rgba(Some(0), Some(0), Some(0), Some(0.0))),
1166+
"transparent" => Ok(Output::from_rgba(0, 0, 0, 0.0)),
11771167
"currentcolor" => Ok(Output::from_current_color()),
11781168
_ => keyword(ident)
1179-
.map(|(r, g, b)| Output::from_rgba(Some(*r), Some(*g), Some(*b), Some(1.0)))
1169+
.map(|(r, g, b)| Output::from_rgba(*r, *g, *b, 1.0))
11801170
.ok_or(()),
11811171
}
11821172
}
@@ -1326,34 +1316,35 @@ where
13261316
// are parsing the legacy syntax.
13271317
let is_legacy_syntax = maybe_red.is_some() && arguments.try_parse(|p| p.expect_comma()).is_ok();
13281318

1329-
let red: Option<u8>;
1330-
let green: Option<u8>;
1331-
let blue: Option<u8>;
1319+
let red: u8;
1320+
let green: u8;
1321+
let blue: u8;
13321322

13331323
let alpha = if is_legacy_syntax {
13341324
match maybe_red.unwrap() {
13351325
NumberOrPercentage::Number { value } => {
1336-
red = Some(clamp_floor_256_f32(value));
1337-
green = Some(clamp_floor_256_f32(color_parser.parse_number(arguments)?));
1326+
red = clamp_floor_256_f32(value);
1327+
green = clamp_floor_256_f32(color_parser.parse_number(arguments)?);
13381328
arguments.expect_comma()?;
1339-
blue = Some(clamp_floor_256_f32(color_parser.parse_number(arguments)?));
1329+
blue = clamp_floor_256_f32(color_parser.parse_number(arguments)?);
13401330
}
13411331
NumberOrPercentage::Percentage { unit_value } => {
1342-
red = Some(clamp_unit_f32(unit_value));
1343-
green = Some(clamp_unit_f32(color_parser.parse_percentage(arguments)?));
1332+
red = clamp_unit_f32(unit_value);
1333+
green = clamp_unit_f32(color_parser.parse_percentage(arguments)?);
13441334
arguments.expect_comma()?;
1345-
blue = Some(clamp_unit_f32(color_parser.parse_percentage(arguments)?));
1335+
blue = clamp_unit_f32(color_parser.parse_percentage(arguments)?);
13461336
}
13471337
}
13481338

1349-
Some(parse_legacy_alpha(color_parser, arguments)?)
1339+
parse_legacy_alpha(color_parser, arguments)?
13501340
} else {
13511341
#[inline]
1352-
fn get_component_value(c: Option<NumberOrPercentage>) -> Option<u8> {
1342+
fn get_component_value(c: Option<NumberOrPercentage>) -> u8 {
13531343
c.map(|c| match c {
13541344
NumberOrPercentage::Number { value } => clamp_floor_256_f32(value),
13551345
NumberOrPercentage::Percentage { unit_value } => clamp_unit_f32(unit_value),
13561346
})
1347+
.unwrap_or(0)
13571348
}
13581349

13591350
red = get_component_value(maybe_red);
@@ -1366,7 +1357,7 @@ where
13661357
color_parser.parse_number_or_percentage(p)
13671358
})?);
13681359

1369-
parse_modern_alpha(color_parser, arguments)?
1360+
parse_modern_alpha(color_parser, arguments)?.unwrap_or(0.0)
13701361
};
13711362

13721363
Ok(P::Output::from_rgba(red, green, blue, alpha))

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ fn parse_border_spacing(_context: &ParserContext, input: &mut Parser)
7070
pub use crate::color::{
7171
hsl_to_rgb, hwb_to_rgb, parse_color_keyword, parse_color_with, parse_hash_color,
7272
serialize_color_alpha, AngleOrNumber, Color, ColorFunction, ColorParser, FromParsedColor, Hsl,
73-
Hwb, Lab, Lch, NumberOrPercentage, Oklab, Oklch, PredefinedColorSpace, RGBA,
73+
Hwb, Lab, Lch, NumberOrPercentage, Oklab, Oklch, PredefinedColorSpace, Rgba,
7474
};
7575
pub use crate::cow_rc_str::CowRcStr;
7676
pub use crate::from_bytes::{stylesheet_encoding, EncodingSupport};

src/tests.rs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ use super::{
1818
parse_important, parse_nth, parse_one_declaration, parse_one_rule, stylesheet_encoding,
1919
AtRuleParser, BasicParseError, BasicParseErrorKind, Color, CowRcStr, DeclarationParser,
2020
Delimiter, EncodingSupport, ParseError, ParseErrorKind, Parser, ParserInput, ParserState,
21-
QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, StyleSheetParser,
22-
ToCss, Token, TokenSerializationType, UnicodeRange, RGBA,
21+
QualifiedRuleParser, Rgba, RuleBodyItemParser, RuleBodyParser, SourceLocation,
22+
StyleSheetParser, ToCss, Token, TokenSerializationType, UnicodeRange,
2323
};
2424

2525
macro_rules! JArray {
@@ -592,19 +592,19 @@ fn serialize_current_color() {
592592

593593
#[test]
594594
fn serialize_rgb_full_alpha() {
595-
let c = Color::Rgba(RGBA::new(Some(255), Some(230), Some(204), Some(1.0)));
595+
let c = Color::Rgba(Rgba::new(255, 230, 204, 1.0));
596596
assert_eq!(c.to_css_string(), "rgb(255, 230, 204)");
597597
}
598598

599599
#[test]
600600
fn serialize_rgba() {
601-
let c = Color::Rgba(RGBA::new(Some(26), Some(51), Some(77), Some(0.125)));
601+
let c = Color::Rgba(Rgba::new(26, 51, 77, 0.125));
602602
assert_eq!(c.to_css_string(), "rgba(26, 51, 77, 0.125)");
603603
}
604604

605605
#[test]
606606
fn serialize_rgba_two_digit_float_if_roundtrips() {
607-
let c = Color::Rgba(RGBA::from_floats(Some(0.), Some(0.), Some(0.), Some(0.5)));
607+
let c = Color::Rgba(Rgba::from_floats(0., 0., 0., 0.5));
608608
assert_eq!(c.to_css_string(), "rgba(0, 0, 0, 0.5)");
609609
}
610610

@@ -1526,7 +1526,7 @@ fn generic_parser() {
15261526
#[derive(Debug, PartialEq)]
15271527
enum OutputType {
15281528
CurrentColor,
1529-
Rgba(Option<u8>, Option<u8>, Option<u8>, Option<f32>),
1529+
Rgba(u8, u8, u8, f32),
15301530
Hsl(Option<f32>, Option<f32>, Option<f32>, Option<f32>),
15311531
Hwb(Option<f32>, Option<f32>, Option<f32>, Option<f32>),
15321532
Lab(Option<f32>, Option<f32>, Option<f32>, Option<f32>),
@@ -1547,12 +1547,7 @@ fn generic_parser() {
15471547
OutputType::CurrentColor
15481548
}
15491549

1550-
fn from_rgba(
1551-
red: Option<u8>,
1552-
green: Option<u8>,
1553-
blue: Option<u8>,
1554-
alpha: Option<f32>,
1555-
) -> Self {
1550+
fn from_rgba(red: u8, green: u8, blue: u8, alpha: f32) -> Self {
15561551
OutputType::Rgba(red, green, blue, alpha)
15571552
}
15581553

@@ -1630,10 +1625,10 @@ fn generic_parser() {
16301625
#[rustfmt::skip]
16311626
const TESTS: &[(&str, OutputType)] = &[
16321627
("currentColor", OutputType::CurrentColor),
1633-
("rgb(1, 2, 3)", OutputType::Rgba(Some(1), Some(2), Some(3), Some(1.0))),
1634-
("rgba(1, 2, 3, 0.4)", OutputType::Rgba(Some(1), Some(2), Some(3), Some(0.4))),
1635-
("rgb(none none none / none)", OutputType::Rgba(None, None, None, None)),
1636-
("rgb(1 none 3 / none)", OutputType::Rgba(Some(1), None, Some(3), None)),
1628+
("rgb(1, 2, 3)", OutputType::Rgba(1, 2, 3, 1.0)),
1629+
("rgba(1, 2, 3, 0.4)", OutputType::Rgba(1, 2, 3, 0.4)),
1630+
("rgb(none none none / none)", OutputType::Rgba(0, 0, 0, 0.0)),
1631+
("rgb(1 none 3 / none)", OutputType::Rgba(1, 0, 3, 0.0)),
16371632

16381633
("hsla(45deg, 20%, 30%, 0.4)", OutputType::Hsl(Some(45.0), Some(0.2), Some(0.3), Some(0.4))),
16391634
("hsl(45deg none none)", OutputType::Hsl(Some(45.0), None, None, Some(1.0))),

0 commit comments

Comments
 (0)