diff options
author | CYBAI <cyb.ai.815@gmail.com> | 2018-01-10 21:00:33 +0800 |
---|---|---|
committer | CYBAI <cyb.ai.815@gmail.com> | 2018-01-10 21:26:33 +0800 |
commit | 2bc02bc78de7cf3028b9a78979ae7cf08046f7bd (patch) | |
tree | 3067a3c2bcb6f11c7a491fd12a5a1515de951ede /components/style/values/specified/svg.rs | |
parent | e2c89df8eeb5f2dbac1436335aea52099a622d0d (diff) | |
download | servo-2bc02bc78de7cf3028b9a78979ae7cf08046f7bd.tar.gz servo-2bc02bc78de7cf3028b9a78979ae7cf08046f7bd.zip |
style: Move paint-order outside of mako
Diffstat (limited to 'components/style/values/specified/svg.rs')
-rw-r--r-- | components/style/values/specified/svg.rs | 138 |
1 files changed, 137 insertions, 1 deletions
diff --git a/components/style/values/specified/svg.rs b/components/style/values/specified/svg.rs index 8f6b62a71e9..63f14b741e0 100644 --- a/components/style/values/specified/svg.rs +++ b/components/style/values/specified/svg.rs @@ -6,7 +6,8 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; -use style_traits::{CommaWithSpace, ParseError, Separator, StyleParseErrorKind}; +use std::fmt; +use style_traits::{CommaWithSpace, ParseError, Separator, StyleParseErrorKind, ToCss}; use values::generics::svg as generic; use values::specified::{LengthOrPercentage, NonNegativeLengthOrPercentage, NonNegativeNumber}; use values::specified::{Number, Opacity, SpecifiedUrl}; @@ -124,3 +125,138 @@ impl Parse for SVGOpacity { } } } + +/// The specified value for a single CSS paint-order property. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, ToCss)] +pub enum PaintOrder { + /// `normal` variant + Normal = 0, + /// `fill` variant + Fill = 1, + /// `stroke` variant + Stroke = 2, + /// `markers` variant + Markers = 3, +} + +/// Number of non-normal components +const PAINT_ORDER_COUNT: u8 = 3; + +/// Number of bits for each component +const PAINT_ORDER_SHIFT: u8 = 2; + +/// Mask with above bits set +const PAINT_ORDER_MASK: u8 = 0b11; + +/// The specified value is tree `PaintOrder` values packed into the +/// bitfields below, as a six-bit field, of 3 two-bit pairs +/// +/// Each pair can be set to FILL, STROKE, or MARKERS +/// Lowest significant bit pairs are highest priority. +/// `normal` is the empty bitfield. The three pairs are +/// never zero in any case other than `normal`. +/// +/// Higher priority values, i.e. the values specified first, +/// will be painted first (and may be covered by paintings of lower priority) +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)] +pub struct SVGPaintOrder(pub u8); + +impl SVGPaintOrder { + /// Get default `paint-order` with `0` + pub fn normal() -> Self { + SVGPaintOrder(0) + } + + /// Get variant of `paint-order` + pub fn order_at(&self, pos: u8) -> PaintOrder { + // Safe because PaintOrder covers all possible patterns. + unsafe { ::std::mem::transmute((self.0 >> pos * PAINT_ORDER_SHIFT) & PAINT_ORDER_MASK) } + } +} + +impl Parse for SVGPaintOrder { + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't> + ) -> Result<SVGPaintOrder, ParseError<'i>> { + if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) { + return Ok(SVGPaintOrder::normal()) + } + + let mut value = 0; + // bitfield representing what we've seen so far + // bit 1 is fill, bit 2 is stroke, bit 3 is markers + let mut seen = 0; + let mut pos = 0; + + loop { + let result: Result<_, ParseError> = input.try(|input| { + try_match_ident_ignore_ascii_case! { input, + "fill" => Ok(PaintOrder::Fill), + "stroke" => Ok(PaintOrder::Stroke), + "markers" => Ok(PaintOrder::Markers), + } + }); + + match result { + Ok(val) => { + if (seen & (1 << val as u8)) != 0 { + // don't parse the same ident twice + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + + value |= (val as u8) << (pos * PAINT_ORDER_SHIFT); + seen |= 1 << (val as u8); + pos += 1; + } + Err(_) => break, + } + } + + if value == 0 { + // Couldn't find any keyword + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + } + + // fill in rest + for i in pos..PAINT_ORDER_COUNT { + for paint in 0..PAINT_ORDER_COUNT { + // if not seen, set bit at position, mark as seen + if (seen & (1 << paint)) == 0 { + seen |= 1 << paint; + value |= paint << (i * PAINT_ORDER_SHIFT); + break; + } + } + } + + Ok(SVGPaintOrder(value)) + } +} + +impl ToCss for SVGPaintOrder { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if self.0 == 0 { + return dest.write_str("normal") + } + + let mut last_pos_to_serialize = 0; + for i in (1..PAINT_ORDER_COUNT).rev() { + let component = self.order_at(i); + let earlier_component = self.order_at(i - 1); + if component < earlier_component { + last_pos_to_serialize = i - 1; + break; + } + } + + for pos in 0..last_pos_to_serialize + 1 { + if pos != 0 { + dest.write_str(" ")? + } + self.order_at(pos).to_css(dest)?; + } + Ok(()) + } +} |