diff options
Diffstat (limited to 'servo/components/style/values/generics/basic_shape.rs')
-rw-r--r-- | servo/components/style/values/generics/basic_shape.rs | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/servo/components/style/values/generics/basic_shape.rs b/servo/components/style/values/generics/basic_shape.rs new file mode 100644 index 0000000000..5af1580ffd --- /dev/null +++ b/servo/components/style/values/generics/basic_shape.rs @@ -0,0 +1,535 @@ +/* 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 https://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 crate::values::animated::{Animate, Procedure, ToAnimatedZero}; +use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; +use crate::values::generics::border::GenericBorderRadius; +use crate::values::generics::position::GenericPosition; +use crate::values::generics::rect::Rect; +use crate::values::specified::SVGPathData; +use crate::Zero; +use std::fmt::{self, Write}; +use style_traits::{CssWriter, ToCss}; + +/// <https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box> +#[allow(missing_docs)] +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Copy, + Debug, + MallocSizeOf, + PartialEq, + Parse, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(u8)] +pub enum ShapeGeometryBox { + /// Depending on which kind of element this style value applied on, the + /// default value of the reference-box can be different. For an HTML + /// element, the default value of reference-box is border-box; for an SVG + /// element, the default value is fill-box. Since we can not determine the + /// default value at parsing time, we keep this value to make a decision on + /// it. + #[css(skip)] + ElementDependent, + FillBox, + StrokeBox, + ViewBox, + ShapeBox(ShapeBox), +} + +impl Default for ShapeGeometryBox { + fn default() -> Self { + Self::ElementDependent + } +} + +/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box +#[allow(missing_docs)] +#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +#[derive( + Animate, + Clone, + Copy, + ComputeSquaredDistance, + Debug, + Eq, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(u8)] +pub enum ShapeBox { + MarginBox, + BorderBox, + PaddingBox, + ContentBox, +} + +impl Default for ShapeBox { + fn default() -> Self { + ShapeBox::MarginBox + } +} + +/// A value for the `clip-path` property. +#[allow(missing_docs)] +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[animation(no_bound(U))] +#[repr(u8)] +pub enum GenericClipPath<BasicShape, U> { + #[animation(error)] + None, + #[animation(error)] + Url(U), + #[css(function)] + Path(Path), + Shape( + Box<BasicShape>, + #[css(skip_if = "is_default")] ShapeGeometryBox, + ), + #[animation(error)] + Box(ShapeGeometryBox), +} + +pub use self::GenericClipPath as ClipPath; + +/// A value for the `shape-outside` property. +#[allow(missing_docs)] +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[animation(no_bound(I))] +#[repr(u8)] +pub enum GenericShapeOutside<BasicShape, I> { + #[animation(error)] + None, + #[animation(error)] + Image(I), + Shape(Box<BasicShape>, #[css(skip_if = "is_default")] ShapeBox), + #[animation(error)] + Box(ShapeBox), +} + +pub use self::GenericShapeOutside as ShapeOutside; + +#[allow(missing_docs)] +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C, u8)] +pub enum GenericBasicShape<H, V, LengthPercentage, NonNegativeLengthPercentage> { + Inset( + #[css(field_bound)] + #[shmem(field_bound)] + InsetRect<LengthPercentage, NonNegativeLengthPercentage>, + ), + Circle( + #[css(field_bound)] + #[shmem(field_bound)] + Circle<H, V, NonNegativeLengthPercentage>, + ), + Ellipse( + #[css(field_bound)] + #[shmem(field_bound)] + Ellipse<H, V, NonNegativeLengthPercentage>, + ), + Polygon(GenericPolygon<LengthPercentage>), +} + +pub use self::GenericBasicShape as BasicShape; + +/// <https://drafts.csswg.org/css-shapes/#funcdef-inset> +#[allow(missing_docs)] +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[css(function = "inset")] +#[repr(C)] +pub struct InsetRect<LengthPercentage, NonNegativeLengthPercentage> { + pub rect: Rect<LengthPercentage>, + #[shmem(field_bound)] + pub round: GenericBorderRadius<NonNegativeLengthPercentage>, +} + +/// <https://drafts.csswg.org/css-shapes/#funcdef-circle> +#[allow(missing_docs)] +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Copy, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[css(function)] +#[repr(C)] +pub struct Circle<H, V, NonNegativeLengthPercentage> { + pub position: GenericPosition<H, V>, + pub radius: GenericShapeRadius<NonNegativeLengthPercentage>, +} + +/// <https://drafts.csswg.org/css-shapes/#funcdef-ellipse> +#[allow(missing_docs)] +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Copy, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[css(function)] +#[repr(C)] +pub struct Ellipse<H, V, NonNegativeLengthPercentage> { + pub position: GenericPosition<H, V>, + pub semiaxis_x: GenericShapeRadius<NonNegativeLengthPercentage>, + pub semiaxis_y: GenericShapeRadius<NonNegativeLengthPercentage>, +} + +/// <https://drafts.csswg.org/css-shapes/#typedef-shape-radius> +#[allow(missing_docs)] +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Copy, + Debug, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C, u8)] +pub enum GenericShapeRadius<NonNegativeLengthPercentage> { + Length(NonNegativeLengthPercentage), + #[animation(error)] + ClosestSide, + #[animation(error)] + FarthestSide, +} + +pub use self::GenericShapeRadius as ShapeRadius; + +/// A generic type for representing the `polygon()` function +/// +/// <https://drafts.csswg.org/css-shapes/#funcdef-polygon> +#[derive( + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[css(comma, function = "polygon")] +#[repr(C)] +pub struct GenericPolygon<LengthPercentage> { + /// The filling rule for a polygon. + #[css(skip_if = "is_default")] + pub fill: FillRule, + /// A collection of (x, y) coordinates to draw the polygon. + #[css(iterable)] + pub coordinates: crate::OwnedSlice<PolygonCoord<LengthPercentage>>, +} + +pub use self::GenericPolygon as Polygon; + +/// Coordinates for Polygon. +#[derive( + Clone, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C)] +pub struct PolygonCoord<LengthPercentage>(pub LengthPercentage, pub LengthPercentage); + +// 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, + ToAnimatedValue, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(u8)] +pub enum FillRule { + Nonzero, + Evenodd, +} + +/// The path function defined in css-shape-2. +/// +/// https://drafts.csswg.org/css-shapes-2/#funcdef-path +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[css(comma)] +#[repr(C)] +pub struct Path { + /// The filling rule for the svg path. + #[css(skip_if = "is_default")] + #[animation(constant)] + pub fill: FillRule, + /// The svg path data. + pub path: SVGPathData, +} + +impl<B, U> ToAnimatedZero for ClipPath<B, U> { + fn to_animated_zero(&self) -> Result<Self, ()> { + Err(()) + } +} + +impl<B, U> ToAnimatedZero for ShapeOutside<B, U> { + fn to_animated_zero(&self) -> Result<Self, ()> { + Err(()) + } +} + +impl<Length, NonNegativeLength> ToCss for InsetRect<Length, NonNegativeLength> +where + Length: ToCss + PartialEq, + NonNegativeLength: ToCss + PartialEq + Zero, +{ + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + dest.write_str("inset(")?; + self.rect.to_css(dest)?; + if !self.round.is_zero() { + dest.write_str(" round ")?; + self.round.to_css(dest)?; + } + dest.write_str(")") + } +} + +impl<H, V, NonNegativeLengthPercentage> ToCss for Circle<H, V, NonNegativeLengthPercentage> +where + GenericPosition<H, V>: ToCss, + NonNegativeLengthPercentage: ToCss + PartialEq, +{ + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + dest.write_str("circle(")?; + if self.radius != Default::default() { + self.radius.to_css(dest)?; + dest.write_str(" ")?; + } + dest.write_str("at ")?; + self.position.to_css(dest)?; + dest.write_str(")") + } +} + +impl<H, V, NonNegativeLengthPercentage> ToCss for Ellipse<H, V, NonNegativeLengthPercentage> +where + GenericPosition<H, V>: ToCss, + NonNegativeLengthPercentage: ToCss + PartialEq, +{ + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + dest.write_str("ellipse(")?; + if self.semiaxis_x != Default::default() || self.semiaxis_y != Default::default() { + self.semiaxis_x.to_css(dest)?; + dest.write_str(" ")?; + self.semiaxis_y.to_css(dest)?; + dest.write_str(" ")?; + } + dest.write_str("at ")?; + self.position.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<_>, _>>()? + .into(); + 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 is_default<T: Default + PartialEq>(fill: &T) -> bool { + *fill == Default::default() +} |