mirror of
https://github.com/servo/servo.git
synced 2025-07-19 05:13:55 +01:00
For now, |clip-path: path()| is chrome-only, and not for shape-outside, so we only implement the parser for clip-path. Besides, I didn't put path() in BasicShape because path() doesn't use the reference box to resolve the percentage or keywords (i.e. SVG path only accept floating point or integer number as the css pixel value). Therefore, I add it into ShapeSource, instead of BasicShape. Differential Revision: https://phabricator.services.mozilla.com/D3633
277 lines
8.6 KiB
Rust
277 lines
8.6 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
//! CSS handling for the [`basic-shape`](https://drafts.csswg.org/css-shapes/#typedef-basic-shape)
|
|
//! types that are generic over their `ToCss` implementations.
|
|
|
|
use std::fmt::{self, Write};
|
|
use style_traits::{CssWriter, ToCss};
|
|
use values::animated::{Animate, Procedure, ToAnimatedZero};
|
|
use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
|
use values::generics::border::BorderRadius;
|
|
use values::generics::position::Position;
|
|
use values::generics::rect::Rect;
|
|
use values::specified::SVGPathData;
|
|
|
|
/// A clipping shape, for `clip-path`.
|
|
pub type ClippingShape<BasicShape, Url> = ShapeSource<BasicShape, GeometryBox, Url>;
|
|
|
|
/// <https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box>
|
|
#[allow(missing_docs)]
|
|
#[derive(Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq,
|
|
SpecifiedValueInfo, ToComputedValue, ToCss)]
|
|
pub enum GeometryBox {
|
|
FillBox,
|
|
StrokeBox,
|
|
ViewBox,
|
|
ShapeBox(ShapeBox),
|
|
}
|
|
|
|
/// A float area shape, for `shape-outside`.
|
|
pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, Image>;
|
|
|
|
/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box
|
|
#[allow(missing_docs)]
|
|
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
|
#[derive(Animate, Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
|
|
SpecifiedValueInfo, ToComputedValue, ToCss)]
|
|
pub enum ShapeBox {
|
|
MarginBox,
|
|
BorderBox,
|
|
PaddingBox,
|
|
ContentBox,
|
|
}
|
|
|
|
/// A shape source, for some reference box.
|
|
#[allow(missing_docs)]
|
|
#[animation(no_bound(ImageOrUrl))]
|
|
#[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
|
|
ToComputedValue, ToCss)]
|
|
pub enum ShapeSource<BasicShape, ReferenceBox, ImageOrUrl> {
|
|
#[animation(error)]
|
|
ImageOrUrl(ImageOrUrl),
|
|
Shape(BasicShape, Option<ReferenceBox>),
|
|
#[animation(error)]
|
|
Box(ReferenceBox),
|
|
#[animation(error)]
|
|
#[css(function)]
|
|
Path(Path),
|
|
#[animation(error)]
|
|
None,
|
|
}
|
|
|
|
#[allow(missing_docs)]
|
|
#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
|
|
SpecifiedValueInfo, ToComputedValue, ToCss)]
|
|
pub enum BasicShape<H, V, LengthOrPercentage> {
|
|
Inset(#[css(field_bound)] InsetRect<LengthOrPercentage>),
|
|
Circle(#[css(field_bound)] Circle<H, V, LengthOrPercentage>),
|
|
Ellipse(#[css(field_bound)] Ellipse<H, V, LengthOrPercentage>),
|
|
Polygon(Polygon<LengthOrPercentage>),
|
|
}
|
|
|
|
/// <https://drafts.csswg.org/css-shapes/#funcdef-inset>
|
|
#[allow(missing_docs)]
|
|
#[css(function = "inset")]
|
|
#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
|
|
SpecifiedValueInfo, ToComputedValue)]
|
|
pub struct InsetRect<LengthOrPercentage> {
|
|
pub rect: Rect<LengthOrPercentage>,
|
|
pub round: Option<BorderRadius<LengthOrPercentage>>,
|
|
}
|
|
|
|
/// <https://drafts.csswg.org/css-shapes/#funcdef-circle>
|
|
#[allow(missing_docs)]
|
|
#[css(function)]
|
|
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
|
|
PartialEq, SpecifiedValueInfo, ToComputedValue)]
|
|
pub struct Circle<H, V, LengthOrPercentage> {
|
|
pub position: Position<H, V>,
|
|
pub radius: ShapeRadius<LengthOrPercentage>,
|
|
}
|
|
|
|
/// <https://drafts.csswg.org/css-shapes/#funcdef-ellipse>
|
|
#[allow(missing_docs)]
|
|
#[css(function)]
|
|
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
|
|
PartialEq, SpecifiedValueInfo, ToComputedValue)]
|
|
pub struct Ellipse<H, V, LengthOrPercentage> {
|
|
pub position: Position<H, V>,
|
|
pub semiaxis_x: ShapeRadius<LengthOrPercentage>,
|
|
pub semiaxis_y: ShapeRadius<LengthOrPercentage>,
|
|
}
|
|
|
|
/// <https://drafts.csswg.org/css-shapes/#typedef-shape-radius>
|
|
#[allow(missing_docs)]
|
|
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf,
|
|
PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
|
|
pub enum ShapeRadius<LengthOrPercentage> {
|
|
Length(LengthOrPercentage),
|
|
#[animation(error)]
|
|
ClosestSide,
|
|
#[animation(error)]
|
|
FarthestSide,
|
|
}
|
|
|
|
/// A generic type for representing the `polygon()` function
|
|
///
|
|
/// <https://drafts.csswg.org/css-shapes/#funcdef-polygon>
|
|
#[css(comma, function)]
|
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
|
|
ToComputedValue, ToCss)]
|
|
pub struct Polygon<LengthOrPercentage> {
|
|
/// The filling rule for a polygon.
|
|
#[css(skip_if = "fill_is_default")]
|
|
pub fill: FillRule,
|
|
/// A collection of (x, y) coordinates to draw the polygon.
|
|
#[css(iterable)]
|
|
pub coordinates: Vec<PolygonCoord<LengthOrPercentage>>,
|
|
}
|
|
|
|
/// Coordinates for Polygon.
|
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
|
|
ToComputedValue, ToCss)]
|
|
pub struct PolygonCoord<LengthOrPercentage>(pub LengthOrPercentage, pub LengthOrPercentage);
|
|
|
|
// https://drafts.csswg.org/css-shapes/#typedef-fill-rule
|
|
// NOTE: Basic shapes spec says that these are the only two values, however
|
|
// https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
|
|
// says that it can also be `inherit`
|
|
#[allow(missing_docs)]
|
|
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
|
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq,
|
|
SpecifiedValueInfo, ToComputedValue, ToCss)]
|
|
#[repr(u8)]
|
|
pub enum FillRule {
|
|
Nonzero,
|
|
Evenodd,
|
|
}
|
|
|
|
/// The path function defined in css-shape-2.
|
|
///
|
|
/// https://drafts.csswg.org/css-shapes-2/#funcdef-path
|
|
#[css(comma)]
|
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
|
|
pub struct Path {
|
|
/// The filling rule for the svg path.
|
|
#[css(skip_if = "fill_is_default")]
|
|
pub fill: FillRule,
|
|
/// The svg path data.
|
|
pub path: SVGPathData,
|
|
}
|
|
|
|
// FIXME(nox): Implement ComputeSquaredDistance for T types and stop
|
|
// using PartialEq here, this will let us derive this impl.
|
|
impl<B, T, U> ComputeSquaredDistance for ShapeSource<B, T, U>
|
|
where
|
|
B: ComputeSquaredDistance,
|
|
T: PartialEq,
|
|
{
|
|
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
match (self, other) {
|
|
(
|
|
&ShapeSource::Shape(ref this, ref this_box),
|
|
&ShapeSource::Shape(ref other, ref other_box),
|
|
) if this_box == other_box =>
|
|
{
|
|
this.compute_squared_distance(other)
|
|
},
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<B, T, U> ToAnimatedZero for ShapeSource<B, T, U> {
|
|
fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
Err(())
|
|
}
|
|
}
|
|
|
|
impl<L> ToCss for InsetRect<L>
|
|
where
|
|
L: ToCss + PartialEq,
|
|
{
|
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
|
where
|
|
W: Write,
|
|
{
|
|
dest.write_str("inset(")?;
|
|
self.rect.to_css(dest)?;
|
|
if let Some(ref radius) = self.round {
|
|
dest.write_str(" round ")?;
|
|
radius.to_css(dest)?;
|
|
}
|
|
dest.write_str(")")
|
|
}
|
|
}
|
|
|
|
impl<L> Default for ShapeRadius<L> {
|
|
#[inline]
|
|
fn default() -> Self {
|
|
ShapeRadius::ClosestSide
|
|
}
|
|
}
|
|
|
|
impl<L> Animate for Polygon<L>
|
|
where
|
|
L: Animate,
|
|
{
|
|
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
if self.fill != other.fill {
|
|
return Err(());
|
|
}
|
|
if self.coordinates.len() != other.coordinates.len() {
|
|
return Err(());
|
|
}
|
|
let coordinates = self.coordinates
|
|
.iter()
|
|
.zip(other.coordinates.iter())
|
|
.map(|(this, other)| {
|
|
Ok(PolygonCoord(
|
|
this.0.animate(&other.0, procedure)?,
|
|
this.1.animate(&other.1, procedure)?,
|
|
))
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()?;
|
|
Ok(Polygon {
|
|
fill: self.fill,
|
|
coordinates,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<L> ComputeSquaredDistance for Polygon<L>
|
|
where
|
|
L: ComputeSquaredDistance,
|
|
{
|
|
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
if self.fill != other.fill {
|
|
return Err(());
|
|
}
|
|
if self.coordinates.len() != other.coordinates.len() {
|
|
return Err(());
|
|
}
|
|
self.coordinates
|
|
.iter()
|
|
.zip(other.coordinates.iter())
|
|
.map(|(this, other)| {
|
|
let d1 = this.0.compute_squared_distance(&other.0)?;
|
|
let d2 = this.1.compute_squared_distance(&other.1)?;
|
|
Ok(d1 + d2)
|
|
})
|
|
.sum()
|
|
}
|
|
}
|
|
|
|
impl Default for FillRule {
|
|
#[inline]
|
|
fn default() -> Self {
|
|
FillRule::Nonzero
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn fill_is_default(fill: &FillRule) -> bool {
|
|
*fill == FillRule::default()
|
|
}
|