Skip to content

Commit

Permalink
Auto merge of #18627 - emilio:paint-order, r=nox
Browse files Browse the repository at this point in the history
style: paint-order serialization fixes.

See each commit for details.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18627)
<!-- Reviewable:end -->
  • Loading branch information
bors-servo committed Sep 25, 2017
2 parents d9cec56 + 638ba2c commit bbb5c84
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 111 deletions.
43 changes: 3 additions & 40 deletions components/style/properties/gecko.mako.rs
Expand Up @@ -5060,51 +5060,14 @@ clip-path
<%self:impl_trait style_struct_name="InheritedSVG"
skip_longhands="paint-order stroke-dasharray -moz-context-properties">
pub fn set_paint_order(&mut self, v: longhands::paint_order::computed_value::T) {
use self::longhands::paint_order;

if v.0 == 0 {
self.gecko.mPaintOrder = structs::NS_STYLE_PAINT_ORDER_NORMAL as u8;
} else {
let mut order = 0;

for pos in 0..3 {
let geckoval = match v.bits_at(pos) {
paint_order::FILL => structs::NS_STYLE_PAINT_ORDER_FILL as u8,
paint_order::STROKE => structs::NS_STYLE_PAINT_ORDER_STROKE as u8,
paint_order::MARKERS => structs::NS_STYLE_PAINT_ORDER_MARKERS as u8,
_ => unreachable!(),
};
order |= geckoval << (pos * structs::NS_STYLE_PAINT_ORDER_BITWIDTH as u8);
}

self.gecko.mPaintOrder = order;
}
self.gecko.mPaintOrder = v.0;
}

${impl_simple_copy('paint_order', 'mPaintOrder')}

pub fn clone_paint_order(&self) -> longhands::paint_order::computed_value::T {
use self::longhands::paint_order::{COUNT, FILL, MARKERS, NORMAL, SHIFT, STROKE};
use self::longhands::paint_order::computed_value::T;

if self.gecko.mPaintOrder == structs::NS_STYLE_PAINT_ORDER_NORMAL as u8 {
return T(NORMAL);
}

const PAINT_ORDER_BITWIDTH: u8 = structs::NS_STYLE_PAINT_ORDER_BITWIDTH as u8;
let mask = (1 << PAINT_ORDER_BITWIDTH) - 1;
let mut order = 0;
for pos in 0..COUNT {
let value =
match (self.gecko.mPaintOrder >> pos * PAINT_ORDER_BITWIDTH & mask) as u32 {
structs::NS_STYLE_PAINT_ORDER_FILL => FILL,
structs::NS_STYLE_PAINT_ORDER_STROKE => STROKE,
structs::NS_STYLE_PAINT_ORDER_MARKERS => MARKERS,
_ => unreachable!(),
};
order |= value << (pos * SHIFT);
};
T(order)
use properties::longhands::paint_order::computed_value::T;
T(self.gecko.mPaintOrder)
}

pub fn set_stroke_dasharray(&mut self, v: longhands::stroke_dasharray::computed_value::T) {
Expand Down
162 changes: 91 additions & 71 deletions components/style/properties/longhand/inherited_svg.mako.rs
Expand Up @@ -137,21 +137,27 @@ ${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
use std::fmt;
use style_traits::ToCss;

pub const NORMAL: u8 = 0;
pub const FILL: u8 = 1;
pub const STROKE: u8 = 2;
pub const MARKERS: u8 = 3;

// number of bits for each component
pub const SHIFT: u8 = 2;
// mask with above bits set
pub const MASK: u8 = 0b11;
// number of non-normal keyword values
pub const COUNT: u8 = 3;
// all keywords
pub const ALL: [u8; 3] = [FILL, STROKE, MARKERS];

/// Represented as a six-bit field, of 3 two-bit pairs
/// 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 = 0,
Fill = 1,
Stroke = 2,
Markers = 3,
}

/// Number of non-normal components.
const COUNT: u8 = 3;

/// Number of bits for each component
const SHIFT: u8 = 2;

/// Mask with above bits set
const 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.
Expand All @@ -165,74 +171,83 @@ ${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
#[derive(Clone, Copy, Debug, PartialEq, ToComputedValue)]
pub struct SpecifiedValue(pub u8);

impl SpecifiedValue {
fn normal() -> Self {
SpecifiedValue(0)
}
}

pub mod computed_value {
pub use super::SpecifiedValue as T;
}

pub fn get_initial_value() -> SpecifiedValue {
SpecifiedValue(NORMAL)
SpecifiedValue::normal()
}

impl SpecifiedValue {
pub fn bits_at(&self, pos: u8) -> u8 {
(self.0 >> pos * SHIFT) & MASK
fn order_at(&self, pos: u8) -> PaintOrder {
// Safe because PaintOrder covers all possible patterns.
unsafe { ::std::mem::transmute((self.0 >> pos * SHIFT) & MASK) }
}
}

pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
-> Result<SpecifiedValue,ParseError<'i>> {
pub fn parse<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>
) -> Result<SpecifiedValue,ParseError<'i>> {
if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) {
Ok(SpecifiedValue(0))
} else {
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(|i| {
try_match_ident_ignore_ascii_case! { i.expect_ident()?,
"fill" => Ok(FILL),
"stroke" => Ok(STROKE),
"markers" => Ok(MARKERS),
}
});

match result {
Ok(val) => {
if (seen & (1 << val)) != 0 {
// don't parse the same ident twice
return Err(StyleParseError::UnspecifiedError.into())
} else {
value |= val << (pos * SHIFT);
seen |= 1 << val;
pos += 1;
}
}
Err(_) => break,
return Ok(SpecifiedValue::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(|i| {
try_match_ident_ignore_ascii_case! { i.expect_ident()?,
"fill" => Ok(PaintOrder::Fill),
"stroke" => Ok(PaintOrder::Stroke),
"markers" => Ok(PaintOrder::Markers),
}
}
});

if value == 0 {
// couldn't find any keyword
Err(StyleParseError::UnspecifiedError.into())
} else {
// fill in rest
for i in pos..COUNT {
for paint in &ALL {
// if not seen, set bit at position, mark as seen
if (seen & (1 << paint)) == 0 {
seen |= 1 << paint;
value |= paint << (i * SHIFT);
break;
}
match result {
Ok(val) => {
if (seen & (1 << val as u8)) != 0 {
// don't parse the same ident twice
return Err(StyleParseError::UnspecifiedError.into())
}

value |= (val as u8) << (pos * SHIFT);
seen |= 1 << (val as u8);
pos += 1;
}
Err(_) => break,
}
}

if value == 0 {
// Couldn't find any keyword
return Err(StyleParseError::UnspecifiedError.into())
}

Ok(SpecifiedValue(value))
// fill in rest
for i in pos..COUNT {
for paint in 0..COUNT {
// if not seen, set bit at position, mark as seen
if (seen & (1 << paint)) == 0 {
seen |= 1 << paint;
value |= paint << (i * SHIFT);
break;
}
}
}

Ok(SpecifiedValue(value))
}

impl ToCss for SpecifiedValue {
Expand All @@ -241,16 +256,21 @@ ${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)",
return dest.write_str("normal")
}

for pos in 0..COUNT {
let mut last_pos_to_serialize = 0;
for i in (1..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(" ")?
}
match self.bits_at(pos) {
FILL => dest.write_str("fill")?,
STROKE => dest.write_str("stroke")?,
MARKERS => dest.write_str("markers")?,
_ => unreachable!(),
}
self.order_at(pos).to_css(dest)?;
}
Ok(())
}
Expand Down

0 comments on commit bbb5c84

Please sign in to comment.