mirror of
https://github.com/servo/servo.git
synced 2025-07-15 19:33:46 +01:00
Auto merge of #21653 - emilio:gecko-sync, r=emilio
style: sync changes from mozilla-central. See each individual 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/21653) <!-- Reviewable:end -->
This commit is contained in:
commit
a39a57c076
5 changed files with 255 additions and 43 deletions
|
@ -379,7 +379,7 @@ ${helpers.predefined_type(
|
||||||
"OffsetPath",
|
"OffsetPath",
|
||||||
"computed::OffsetPath::none()",
|
"computed::OffsetPath::none()",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
animation_value_type="none",
|
animation_value_type="ComputedValue",
|
||||||
gecko_pref="layout.css.motion-path.enabled",
|
gecko_pref="layout.css.motion-path.enabled",
|
||||||
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
|
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
|
||||||
spec="https://drafts.fxtf.org/motion-1/#offset-path-property",
|
spec="https://drafts.fxtf.org/motion-1/#offset-path-property",
|
||||||
|
|
|
@ -392,3 +392,14 @@ where
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> ToAnimatedZero for Box<[T]>
|
||||||
|
where
|
||||||
|
T: ToAnimatedZero,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||||
|
let v = self.iter().map(|v| v.to_animated_zero()).collect::<Result<Vec<_>, _>>()?;
|
||||||
|
Ok(v.into_boxed_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -54,7 +54,6 @@ pub enum ShapeSource<BasicShape, ReferenceBox, ImageOrUrl> {
|
||||||
Shape(BasicShape, Option<ReferenceBox>),
|
Shape(BasicShape, Option<ReferenceBox>),
|
||||||
#[animation(error)]
|
#[animation(error)]
|
||||||
Box(ReferenceBox),
|
Box(ReferenceBox),
|
||||||
#[animation(error)]
|
|
||||||
#[css(function)]
|
#[css(function)]
|
||||||
Path(Path),
|
Path(Path),
|
||||||
#[animation(error)]
|
#[animation(error)]
|
||||||
|
@ -152,10 +151,12 @@ pub enum FillRule {
|
||||||
///
|
///
|
||||||
/// https://drafts.csswg.org/css-shapes-2/#funcdef-path
|
/// https://drafts.csswg.org/css-shapes-2/#funcdef-path
|
||||||
#[css(comma)]
|
#[css(comma)]
|
||||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
|
#[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
|
||||||
|
ToComputedValue, ToCss)]
|
||||||
pub struct Path {
|
pub struct Path {
|
||||||
/// The filling rule for the svg path.
|
/// The filling rule for the svg path.
|
||||||
#[css(skip_if = "fill_is_default")]
|
#[css(skip_if = "fill_is_default")]
|
||||||
|
#[animation(constant)]
|
||||||
pub fill: FillRule,
|
pub fill: FillRule,
|
||||||
/// The svg path data.
|
/// The svg path data.
|
||||||
pub path: SVGPathData,
|
pub path: SVGPathData,
|
||||||
|
@ -177,6 +178,13 @@ where
|
||||||
{
|
{
|
||||||
this.compute_squared_distance(other)
|
this.compute_squared_distance(other)
|
||||||
},
|
},
|
||||||
|
(
|
||||||
|
&ShapeSource::Path(ref this),
|
||||||
|
&ShapeSource::Path(ref other),
|
||||||
|
) if this.fill == other.fill =>
|
||||||
|
{
|
||||||
|
this.path.compute_squared_distance(&other.path)
|
||||||
|
}
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,8 @@ use values::specified::SVGPathData;
|
||||||
/// The offset-path value.
|
/// The offset-path value.
|
||||||
///
|
///
|
||||||
/// https://drafts.fxtf.org/motion-1/#offset-path-property
|
/// https://drafts.fxtf.org/motion-1/#offset-path-property
|
||||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
|
#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
|
||||||
|
SpecifiedValueInfo, ToAnimatedZero, ToComputedValue, ToCss)]
|
||||||
pub enum OffsetPath {
|
pub enum OffsetPath {
|
||||||
// We could merge SVGPathData into ShapeSource, so we could reuse them. However,
|
// We could merge SVGPathData into ShapeSource, so we could reuse them. However,
|
||||||
// we don't want to support other value for offset-path, so use SVGPathData only for now.
|
// we don't want to support other value for offset-path, so use SVGPathData only for now.
|
||||||
|
@ -20,6 +21,7 @@ pub enum OffsetPath {
|
||||||
#[css(function)]
|
#[css(function)]
|
||||||
Path(SVGPathData),
|
Path(SVGPathData),
|
||||||
/// None value.
|
/// None value.
|
||||||
|
#[animation(error)]
|
||||||
None,
|
None,
|
||||||
// Bug 1186329: Implement ray(), <basic-shape>, <geometry-box>, and <url>.
|
// Bug 1186329: Implement ray(), <basic-shape>, <geometry-box>, and <url>.
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,20 @@ use cssparser::Parser;
|
||||||
use parser::{Parse, ParserContext};
|
use parser::{Parse, ParserContext};
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use std::iter::{Cloned, Peekable};
|
use std::iter::{Cloned, Peekable};
|
||||||
|
use std::ops::AddAssign;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||||
use style_traits::values::SequenceWriter;
|
use style_traits::values::SequenceWriter;
|
||||||
use values::CSSFloat;
|
use values::CSSFloat;
|
||||||
|
use values::animated::{Animate, Procedure};
|
||||||
|
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||||
|
|
||||||
|
|
||||||
/// The SVG path data.
|
/// The SVG path data.
|
||||||
///
|
///
|
||||||
/// https://www.w3.org/TR/SVG11/paths.html#PathData
|
/// https://www.w3.org/TR/SVG11/paths.html#PathData
|
||||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)]
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedZero,
|
||||||
|
ToComputedValue)]
|
||||||
pub struct SVGPathData(Box<[PathCommand]>);
|
pub struct SVGPathData(Box<[PathCommand]>);
|
||||||
|
|
||||||
impl SVGPathData {
|
impl SVGPathData {
|
||||||
|
@ -34,6 +38,17 @@ impl SVGPathData {
|
||||||
debug_assert!(!self.0.is_empty());
|
debug_assert!(!self.0.is_empty());
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a normalized copy of this path by converting each relative command to an absolute
|
||||||
|
/// command.
|
||||||
|
fn normalize(&self) -> Self {
|
||||||
|
let mut state = PathTraversalState {
|
||||||
|
subpath_start: CoordPair::new(0.0, 0.0),
|
||||||
|
pos: CoordPair::new(0.0, 0.0),
|
||||||
|
};
|
||||||
|
let result = self.0.iter().map(|seg| seg.normalize(&mut state)).collect::<Vec<_>>();
|
||||||
|
SVGPathData(result.into_boxed_slice())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for SVGPathData {
|
impl ToCss for SVGPathData {
|
||||||
|
@ -82,6 +97,33 @@ impl Parse for SVGPathData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Animate for SVGPathData {
|
||||||
|
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||||
|
if self.0.len() != other.0.len() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = self.normalize().0
|
||||||
|
.iter()
|
||||||
|
.zip(other.normalize().0.iter())
|
||||||
|
.map(|(a, b)| a.animate(&b, procedure))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
Ok(SVGPathData::new(result.into_boxed_slice()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComputeSquaredDistance for SVGPathData {
|
||||||
|
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||||
|
if self.0.len() != other.0.len() {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
self.normalize().0
|
||||||
|
.iter()
|
||||||
|
.zip(other.normalize().0.iter())
|
||||||
|
.map(|(this, other)| this.compute_squared_distance(&other))
|
||||||
|
.sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The SVG path command.
|
/// The SVG path command.
|
||||||
/// The fields of these commands are self-explanatory, so we skip the documents.
|
/// The fields of these commands are self-explanatory, so we skip the documents.
|
||||||
|
@ -89,7 +131,8 @@ impl Parse for SVGPathData {
|
||||||
/// points of the Bézier curve in the spec.
|
/// points of the Bézier curve in the spec.
|
||||||
///
|
///
|
||||||
/// https://www.w3.org/TR/SVG11/paths.html#PathData
|
/// https://www.w3.org/TR/SVG11/paths.html#PathData
|
||||||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)]
|
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
|
||||||
|
SpecifiedValueInfo, ToAnimatedZero)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[repr(C, u8)]
|
#[repr(C, u8)]
|
||||||
pub enum PathCommand {
|
pub enum PathCommand {
|
||||||
|
@ -97,35 +140,131 @@ pub enum PathCommand {
|
||||||
/// https://www.w3.org/TR/SVG/paths.html#__svg__SVGPathSeg__PATHSEG_UNKNOWN
|
/// https://www.w3.org/TR/SVG/paths.html#__svg__SVGPathSeg__PATHSEG_UNKNOWN
|
||||||
Unknown,
|
Unknown,
|
||||||
/// The "moveto" command.
|
/// The "moveto" command.
|
||||||
MoveTo { point: CoordPair, absolute: bool },
|
MoveTo { point: CoordPair, absolute: IsAbsolute },
|
||||||
/// The "lineto" command.
|
/// The "lineto" command.
|
||||||
LineTo { point: CoordPair, absolute: bool },
|
LineTo { point: CoordPair, absolute: IsAbsolute },
|
||||||
/// The horizontal "lineto" command.
|
/// The horizontal "lineto" command.
|
||||||
HorizontalLineTo { x: CSSFloat, absolute: bool },
|
HorizontalLineTo { x: CSSFloat, absolute: IsAbsolute },
|
||||||
/// The vertical "lineto" command.
|
/// The vertical "lineto" command.
|
||||||
VerticalLineTo { y: CSSFloat, absolute: bool },
|
VerticalLineTo { y: CSSFloat, absolute: IsAbsolute },
|
||||||
/// The cubic Bézier curve command.
|
/// The cubic Bézier curve command.
|
||||||
CurveTo { control1: CoordPair, control2: CoordPair, point: CoordPair, absolute: bool },
|
CurveTo { control1: CoordPair, control2: CoordPair, point: CoordPair, absolute: IsAbsolute },
|
||||||
/// The smooth curve command.
|
/// The smooth curve command.
|
||||||
SmoothCurveTo { control2: CoordPair, point: CoordPair, absolute: bool },
|
SmoothCurveTo { control2: CoordPair, point: CoordPair, absolute: IsAbsolute },
|
||||||
/// The quadratic Bézier curve command.
|
/// The quadratic Bézier curve command.
|
||||||
QuadBezierCurveTo { control1: CoordPair, point: CoordPair, absolute: bool },
|
QuadBezierCurveTo { control1: CoordPair, point: CoordPair, absolute: IsAbsolute },
|
||||||
/// The smooth quadratic Bézier curve command.
|
/// The smooth quadratic Bézier curve command.
|
||||||
SmoothQuadBezierCurveTo { point: CoordPair, absolute: bool },
|
SmoothQuadBezierCurveTo { point: CoordPair, absolute: IsAbsolute },
|
||||||
/// The elliptical arc curve command.
|
/// The elliptical arc curve command.
|
||||||
EllipticalArc {
|
EllipticalArc {
|
||||||
rx: CSSFloat,
|
rx: CSSFloat,
|
||||||
ry: CSSFloat,
|
ry: CSSFloat,
|
||||||
angle: CSSFloat,
|
angle: CSSFloat,
|
||||||
large_arc_flag: bool,
|
#[animation(constant)]
|
||||||
sweep_flag: bool,
|
large_arc_flag: ArcFlag,
|
||||||
|
#[animation(constant)]
|
||||||
|
sweep_flag: ArcFlag,
|
||||||
point: CoordPair,
|
point: CoordPair,
|
||||||
absolute: bool
|
absolute: IsAbsolute
|
||||||
},
|
},
|
||||||
/// The "closepath" command.
|
/// The "closepath" command.
|
||||||
ClosePath,
|
ClosePath,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// For internal SVGPath normalization.
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
struct PathTraversalState {
|
||||||
|
subpath_start: CoordPair,
|
||||||
|
pos: CoordPair,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PathCommand {
|
||||||
|
/// Create a normalized copy of this PathCommand. Absolute commands will be copied as-is while
|
||||||
|
/// for relative commands an equivalent absolute command will be returned.
|
||||||
|
///
|
||||||
|
/// See discussion: https://github.com/w3c/svgwg/issues/321
|
||||||
|
fn normalize(&self, state: &mut PathTraversalState) -> Self {
|
||||||
|
use self::PathCommand::*;
|
||||||
|
match *self {
|
||||||
|
Unknown => Unknown,
|
||||||
|
ClosePath => {
|
||||||
|
state.pos = state.subpath_start;
|
||||||
|
ClosePath
|
||||||
|
},
|
||||||
|
MoveTo { mut point, absolute } => {
|
||||||
|
if !absolute.is_yes() {
|
||||||
|
point += state.pos;
|
||||||
|
}
|
||||||
|
state.pos = point;
|
||||||
|
state.subpath_start = point;
|
||||||
|
MoveTo { point, absolute: IsAbsolute::Yes }
|
||||||
|
},
|
||||||
|
LineTo { mut point, absolute } => {
|
||||||
|
if !absolute.is_yes() {
|
||||||
|
point += state.pos;
|
||||||
|
}
|
||||||
|
state.pos = point;
|
||||||
|
LineTo { point, absolute: IsAbsolute::Yes }
|
||||||
|
},
|
||||||
|
HorizontalLineTo { mut x, absolute } => {
|
||||||
|
if !absolute.is_yes() {
|
||||||
|
x += state.pos.0;
|
||||||
|
}
|
||||||
|
state.pos.0 = x;
|
||||||
|
HorizontalLineTo { x, absolute: IsAbsolute::Yes }
|
||||||
|
},
|
||||||
|
VerticalLineTo { mut y, absolute } => {
|
||||||
|
if !absolute.is_yes() {
|
||||||
|
y += state.pos.1;
|
||||||
|
}
|
||||||
|
state.pos.1 = y;
|
||||||
|
VerticalLineTo { y, absolute: IsAbsolute::Yes }
|
||||||
|
},
|
||||||
|
CurveTo { mut control1, mut control2, mut point, absolute } => {
|
||||||
|
if !absolute.is_yes() {
|
||||||
|
control1 += state.pos;
|
||||||
|
control2 += state.pos;
|
||||||
|
point += state.pos;
|
||||||
|
}
|
||||||
|
state.pos = point;
|
||||||
|
CurveTo { control1, control2, point, absolute: IsAbsolute::Yes }
|
||||||
|
},
|
||||||
|
SmoothCurveTo { mut control2, mut point, absolute } => {
|
||||||
|
if !absolute.is_yes() {
|
||||||
|
control2 += state.pos;
|
||||||
|
point += state.pos;
|
||||||
|
}
|
||||||
|
state.pos = point;
|
||||||
|
SmoothCurveTo { control2, point, absolute: IsAbsolute::Yes }
|
||||||
|
},
|
||||||
|
QuadBezierCurveTo { mut control1, mut point, absolute } => {
|
||||||
|
if !absolute.is_yes() {
|
||||||
|
control1 += state.pos;
|
||||||
|
point += state.pos;
|
||||||
|
}
|
||||||
|
state.pos = point;
|
||||||
|
QuadBezierCurveTo { control1, point, absolute: IsAbsolute::Yes }
|
||||||
|
},
|
||||||
|
SmoothQuadBezierCurveTo { mut point, absolute } => {
|
||||||
|
if !absolute.is_yes() {
|
||||||
|
point += state.pos;
|
||||||
|
}
|
||||||
|
state.pos = point;
|
||||||
|
SmoothQuadBezierCurveTo { point, absolute: IsAbsolute::Yes }
|
||||||
|
},
|
||||||
|
EllipticalArc { rx, ry, angle, large_arc_flag, sweep_flag, mut point, absolute } => {
|
||||||
|
if !absolute.is_yes() {
|
||||||
|
point += state.pos;
|
||||||
|
}
|
||||||
|
state.pos = point;
|
||||||
|
EllipticalArc {
|
||||||
|
rx, ry, angle, large_arc_flag, sweep_flag, point, absolute: IsAbsolute::Yes
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToCss for PathCommand {
|
impl ToCss for PathCommand {
|
||||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
where
|
where
|
||||||
|
@ -136,17 +275,17 @@ impl ToCss for PathCommand {
|
||||||
Unknown => dest.write_char('X'),
|
Unknown => dest.write_char('X'),
|
||||||
ClosePath => dest.write_char('Z'),
|
ClosePath => dest.write_char('Z'),
|
||||||
MoveTo { point, absolute } => {
|
MoveTo { point, absolute } => {
|
||||||
dest.write_char(if absolute { 'M' } else { 'm' })?;
|
dest.write_char(if absolute.is_yes() { 'M' } else { 'm' })?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
point.to_css(dest)
|
point.to_css(dest)
|
||||||
}
|
}
|
||||||
LineTo { point, absolute } => {
|
LineTo { point, absolute } => {
|
||||||
dest.write_char(if absolute { 'L' } else { 'l' })?;
|
dest.write_char(if absolute.is_yes() { 'L' } else { 'l' })?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
point.to_css(dest)
|
point.to_css(dest)
|
||||||
}
|
}
|
||||||
CurveTo { control1, control2, point, absolute } => {
|
CurveTo { control1, control2, point, absolute } => {
|
||||||
dest.write_char(if absolute { 'C' } else { 'c' })?;
|
dest.write_char(if absolute.is_yes() { 'C' } else { 'c' })?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
control1.to_css(dest)?;
|
control1.to_css(dest)?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
|
@ -155,14 +294,14 @@ impl ToCss for PathCommand {
|
||||||
point.to_css(dest)
|
point.to_css(dest)
|
||||||
},
|
},
|
||||||
QuadBezierCurveTo { control1, point, absolute } => {
|
QuadBezierCurveTo { control1, point, absolute } => {
|
||||||
dest.write_char(if absolute { 'Q' } else { 'q' })?;
|
dest.write_char(if absolute.is_yes() { 'Q' } else { 'q' })?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
control1.to_css(dest)?;
|
control1.to_css(dest)?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
point.to_css(dest)
|
point.to_css(dest)
|
||||||
},
|
},
|
||||||
EllipticalArc { rx, ry, angle, large_arc_flag, sweep_flag, point, absolute } => {
|
EllipticalArc { rx, ry, angle, large_arc_flag, sweep_flag, point, absolute } => {
|
||||||
dest.write_char(if absolute { 'A' } else { 'a' })?;
|
dest.write_char(if absolute.is_yes() { 'A' } else { 'a' })?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
rx.to_css(dest)?;
|
rx.to_css(dest)?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
|
@ -170,31 +309,31 @@ impl ToCss for PathCommand {
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
angle.to_css(dest)?;
|
angle.to_css(dest)?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
(large_arc_flag as i32).to_css(dest)?;
|
large_arc_flag.to_css(dest)?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
(sweep_flag as i32).to_css(dest)?;
|
sweep_flag.to_css(dest)?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
point.to_css(dest)
|
point.to_css(dest)
|
||||||
},
|
},
|
||||||
HorizontalLineTo { x, absolute } => {
|
HorizontalLineTo { x, absolute } => {
|
||||||
dest.write_char(if absolute { 'H' } else { 'h' })?;
|
dest.write_char(if absolute.is_yes() { 'H' } else { 'h' })?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
x.to_css(dest)
|
x.to_css(dest)
|
||||||
},
|
},
|
||||||
VerticalLineTo { y, absolute } => {
|
VerticalLineTo { y, absolute } => {
|
||||||
dest.write_char(if absolute { 'V' } else { 'v' })?;
|
dest.write_char(if absolute.is_yes() { 'V' } else { 'v' })?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
y.to_css(dest)
|
y.to_css(dest)
|
||||||
},
|
},
|
||||||
SmoothCurveTo { control2, point, absolute } => {
|
SmoothCurveTo { control2, point, absolute } => {
|
||||||
dest.write_char(if absolute { 'S' } else { 's' })?;
|
dest.write_char(if absolute.is_yes() { 'S' } else { 's' })?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
control2.to_css(dest)?;
|
control2.to_css(dest)?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
point.to_css(dest)
|
point.to_css(dest)
|
||||||
},
|
},
|
||||||
SmoothQuadBezierCurveTo { point, absolute } => {
|
SmoothQuadBezierCurveTo { point, absolute } => {
|
||||||
dest.write_char(if absolute { 'T' } else { 't' })?;
|
dest.write_char(if absolute.is_yes() { 'T' } else { 't' })?;
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
point.to_css(dest)
|
point.to_css(dest)
|
||||||
},
|
},
|
||||||
|
@ -202,9 +341,27 @@ impl ToCss for PathCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The path command absolute type.
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
|
||||||
|
SpecifiedValueInfo, ToAnimatedZero)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum IsAbsolute {
|
||||||
|
Yes,
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IsAbsolute {
|
||||||
|
/// Return true if this is IsAbsolute::Yes.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_yes(&self) -> bool {
|
||||||
|
*self == IsAbsolute::Yes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The path coord type.
|
/// The path coord type.
|
||||||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
|
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq,
|
||||||
|
SpecifiedValueInfo, ToAnimatedZero, ToCss)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct CoordPair(CSSFloat, CSSFloat);
|
pub struct CoordPair(CSSFloat, CSSFloat);
|
||||||
|
|
||||||
|
@ -216,6 +373,36 @@ impl CoordPair {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AddAssign for CoordPair {
|
||||||
|
#[inline]
|
||||||
|
fn add_assign(&mut self, other: Self) {
|
||||||
|
self.0 += other.0;
|
||||||
|
self.1 += other.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The EllipticalArc flag type.
|
||||||
|
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ArcFlag(bool);
|
||||||
|
|
||||||
|
impl ToCss for ArcFlag {
|
||||||
|
#[inline]
|
||||||
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: fmt::Write
|
||||||
|
{
|
||||||
|
(self.0 as i32).to_css(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComputeSquaredDistance for ArcFlag {
|
||||||
|
#[inline]
|
||||||
|
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
||||||
|
(self.0 as i32).compute_squared_distance(&(other.0 as i32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// SVG Path parser.
|
/// SVG Path parser.
|
||||||
struct PathParser<'a> {
|
struct PathParser<'a> {
|
||||||
|
@ -276,7 +463,11 @@ impl<'a> PathParser<'a> {
|
||||||
|
|
||||||
match self.chars.next() {
|
match self.chars.next() {
|
||||||
Some(command) => {
|
Some(command) => {
|
||||||
let abs = command.is_ascii_uppercase();
|
let abs = if command.is_ascii_uppercase() {
|
||||||
|
IsAbsolute::Yes
|
||||||
|
} else {
|
||||||
|
IsAbsolute::No
|
||||||
|
};
|
||||||
macro_rules! parse_command {
|
macro_rules! parse_command {
|
||||||
( $($($p:pat)|+ => $parse_func:ident,)* ) => {
|
( $($($p:pat)|+ => $parse_func:ident,)* ) => {
|
||||||
match command {
|
match command {
|
||||||
|
@ -299,7 +490,7 @@ impl<'a> PathParser<'a> {
|
||||||
b'S' | b's' => parse_smooth_curveto,
|
b'S' | b's' => parse_smooth_curveto,
|
||||||
b'Q' | b'q' => parse_quadratic_bezier_curveto,
|
b'Q' | b'q' => parse_quadratic_bezier_curveto,
|
||||||
b'T' | b't' => parse_smooth_quadratic_bezier_curveto,
|
b'T' | b't' => parse_smooth_quadratic_bezier_curveto,
|
||||||
b'A' | b'a' => parse_elliprical_arc,
|
b'A' | b'a' => parse_elliptical_arc,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
_ => break, // no more commands.
|
_ => break, // no more commands.
|
||||||
|
@ -317,7 +508,7 @@ impl<'a> PathParser<'a> {
|
||||||
|
|
||||||
skip_wsp(&mut self.chars);
|
skip_wsp(&mut self.chars);
|
||||||
let point = parse_coord(&mut self.chars)?;
|
let point = parse_coord(&mut self.chars)?;
|
||||||
let absolute = command == b'M';
|
let absolute = if command == b'M' { IsAbsolute::Yes } else { IsAbsolute::No };
|
||||||
self.path.push(PathCommand::MoveTo { point, absolute } );
|
self.path.push(PathCommand::MoveTo { point, absolute } );
|
||||||
|
|
||||||
// End of string or the next character is a possible new command.
|
// End of string or the next character is a possible new command.
|
||||||
|
@ -333,58 +524,58 @@ impl<'a> PathParser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse "closepath" command.
|
/// Parse "closepath" command.
|
||||||
fn parse_closepath(&mut self, _absolute: bool) -> Result<(), ()> {
|
fn parse_closepath(&mut self, _absolute: IsAbsolute) -> Result<(), ()> {
|
||||||
self.path.push(PathCommand::ClosePath);
|
self.path.push(PathCommand::ClosePath);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse "lineto" command.
|
/// Parse "lineto" command.
|
||||||
fn parse_lineto(&mut self, absolute: bool) -> Result<(), ()> {
|
fn parse_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
|
||||||
parse_arguments!(self, absolute, LineTo, [ point => parse_coord ])
|
parse_arguments!(self, absolute, LineTo, [ point => parse_coord ])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse horizontal "lineto" command.
|
/// Parse horizontal "lineto" command.
|
||||||
fn parse_h_lineto(&mut self, absolute: bool) -> Result<(), ()> {
|
fn parse_h_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
|
||||||
parse_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ])
|
parse_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse vertical "lineto" command.
|
/// Parse vertical "lineto" command.
|
||||||
fn parse_v_lineto(&mut self, absolute: bool) -> Result<(), ()> {
|
fn parse_v_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
|
||||||
parse_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ])
|
parse_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse cubic Bézier curve command.
|
/// Parse cubic Bézier curve command.
|
||||||
fn parse_curveto(&mut self, absolute: bool) -> Result<(), ()> {
|
fn parse_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
|
||||||
parse_arguments!(self, absolute, CurveTo, [
|
parse_arguments!(self, absolute, CurveTo, [
|
||||||
control1 => parse_coord, control2 => parse_coord, point => parse_coord
|
control1 => parse_coord, control2 => parse_coord, point => parse_coord
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse smooth "curveto" command.
|
/// Parse smooth "curveto" command.
|
||||||
fn parse_smooth_curveto(&mut self, absolute: bool) -> Result<(), ()> {
|
fn parse_smooth_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
|
||||||
parse_arguments!(self, absolute, SmoothCurveTo, [
|
parse_arguments!(self, absolute, SmoothCurveTo, [
|
||||||
control2 => parse_coord, point => parse_coord
|
control2 => parse_coord, point => parse_coord
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse quadratic Bézier curve command.
|
/// Parse quadratic Bézier curve command.
|
||||||
fn parse_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> {
|
fn parse_quadratic_bezier_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
|
||||||
parse_arguments!(self, absolute, QuadBezierCurveTo, [
|
parse_arguments!(self, absolute, QuadBezierCurveTo, [
|
||||||
control1 => parse_coord, point => parse_coord
|
control1 => parse_coord, point => parse_coord
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse smooth quadratic Bézier curveto command.
|
/// Parse smooth quadratic Bézier curveto command.
|
||||||
fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> {
|
fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
|
||||||
parse_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ])
|
parse_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse elliptical arc curve command.
|
/// Parse elliptical arc curve command.
|
||||||
fn parse_elliprical_arc(&mut self, absolute: bool) -> Result<(), ()> {
|
fn parse_elliptical_arc(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
|
||||||
// Parse a flag whose value is '0' or '1'; otherwise, return Err(()).
|
// Parse a flag whose value is '0' or '1'; otherwise, return Err(()).
|
||||||
let parse_flag = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| -> Result<bool, ()> {
|
let parse_flag = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| {
|
||||||
match iter.next() {
|
match iter.next() {
|
||||||
Some(c) if c == b'0' || c == b'1' => Ok(c == b'1'),
|
Some(c) if c == b'0' || c == b'1' => Ok(ArcFlag(c == b'1')),
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue