style: Add bindings for ArcSlice and ThinArc, and use them to reduce copies of SVG path data.

As I said over bug 1549593, the eventual goal is to use ArcSlice in all
inherited properties. But this seemed like a good first candidate that doesn't
require me to move around a lot more code, since we were already using cbindgen
for the path commands.

Differential Revision: https://phabricator.services.mozilla.com/D30134
This commit is contained in:
Emilio Cobos Álvarez 2019-05-09 12:43:19 +00:00
parent 0d5c4481b8
commit f429c28f23
5 changed files with 39 additions and 41 deletions

View file

@ -29,16 +29,15 @@ use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
ToResolvedValue,
ToShmem,
)]
pub struct SVGPathData(Box<[PathCommand]>);
#[repr(C)]
pub struct SVGPathData(
// TODO(emilio): Should probably measure this somehow only from the
// specified values.
#[ignore_malloc_size_of = "Arc"]
pub crate::ArcSlice<PathCommand>
);
impl SVGPathData {
/// Return SVGPathData by a slice of PathCommand.
#[inline]
pub fn new(cmd: Box<[PathCommand]>) -> Self {
debug_assert!(!cmd.is_empty());
SVGPathData(cmd)
}
/// Get the array of PathCommand.
#[inline]
pub fn commands(&self) -> &[PathCommand] {
@ -46,9 +45,9 @@ impl SVGPathData {
&self.0
}
/// Create a normalized copy of this path by converting each relative command to an absolute
/// command.
fn normalize(&self) -> Self {
/// Create a normalized copy of this path by converting each relative
/// command to an absolute command.
fn normalize(&self) -> Box<[PathCommand]> {
let mut state = PathTraversalState {
subpath_start: CoordPair::new(0.0, 0.0),
pos: CoordPair::new(0.0, 0.0),
@ -58,7 +57,7 @@ impl SVGPathData {
.iter()
.map(|seg| seg.normalize(&mut state))
.collect::<Vec<_>>();
SVGPathData(result.into_boxed_slice())
result.into_boxed_slice()
}
}
@ -71,7 +70,7 @@ impl ToCss for SVGPathData {
dest.write_char('"')?;
{
let mut writer = SequenceWriter::new(dest, " ");
for command in self.0.iter() {
for command in self.commands() {
writer.item(command)?;
}
}
@ -104,7 +103,7 @@ impl Parse for SVGPathData {
}
}
Ok(SVGPathData::new(path_parser.path.into_boxed_slice()))
Ok(SVGPathData(crate::ArcSlice::from_iter(path_parser.path.into_iter())))
}
}
@ -114,14 +113,17 @@ impl Animate for SVGPathData {
return Err(());
}
// FIXME(emilio): This allocates three copies of the path, that's not
// great! Specially, once we're normalized once, we don't need to
// re-normalize again.
let result = self
.normalize()
.0
.iter()
.zip(other.normalize().0.iter())
.zip(other.normalize().iter())
.map(|(a, b)| a.animate(&b, procedure))
.collect::<Result<Vec<_>, _>>()?;
Ok(SVGPathData::new(result.into_boxed_slice()))
Ok(SVGPathData(crate::ArcSlice::from_iter(result.into_iter())))
}
}
@ -131,9 +133,8 @@ impl ComputeSquaredDistance for SVGPathData {
return Err(());
}
self.normalize()
.0
.iter()
.zip(other.normalize().0.iter())
.zip(other.normalize().iter())
.map(|(this, other)| this.compute_squared_distance(&other))
.sum()
}