|
@@ -0,0 +1,4518 @@
|
|
|
+# HG changeset patch
|
|
|
+# User Anthony Ramine <n.oxyde@gmail.com>
|
|
|
+# Date 1503404530 18000
|
|
|
+# Tue Aug 22 07:22:10 2017 -0500
|
|
|
+# Node ID 5a75f509b7ca188588479d8942e1e351bec713c8
|
|
|
+# Parent be8c5948d916693bd83f7b2d877c01084ff00ad5
|
|
|
+servo: Merge #18134 - Introduce values::animated::Animate (from servo:we-are-leaving-babylon); r=emilio
|
|
|
+
|
|
|
+Source-Repo: https://github.com/servo/servo
|
|
|
+Source-Revision: 8ca9542de6838fc485e9e41013d84b396ff216a9
|
|
|
+
|
|
|
+diff --git a/servo/components/style/gecko/wrapper.rs b/servo/components/style/gecko/wrapper.rs
|
|
|
+--- a/servo/components/style/gecko/wrapper.rs
|
|
|
++++ b/servo/components/style/gecko/wrapper.rs
|
|
|
+@@ -1405,25 +1405,25 @@ impl<'le> TElement for GeckoElement<'le>
|
|
|
+
|
|
|
+ // Check if we have to cancel the running transition because this is not a matching
|
|
|
+ // transition-property value.
|
|
|
+ transitions_to_keep.map_or(false, |set| {
|
|
|
+ existing_transitions.keys().any(|property| !set.contains(property))
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+- fn needs_transitions_update_per_property(&self,
|
|
|
+- property: &TransitionProperty,
|
|
|
+- combined_duration: f32,
|
|
|
+- before_change_style: &ComputedValues,
|
|
|
+- after_change_style: &ComputedValues,
|
|
|
+- existing_transitions: &HashMap<TransitionProperty,
|
|
|
+- Arc<AnimationValue>>)
|
|
|
+- -> bool {
|
|
|
+- use properties::animated_properties::Animatable;
|
|
|
++ fn needs_transitions_update_per_property(
|
|
|
++ &self,
|
|
|
++ property: &TransitionProperty,
|
|
|
++ combined_duration: f32,
|
|
|
++ before_change_style: &ComputedValues,
|
|
|
++ after_change_style: &ComputedValues,
|
|
|
++ existing_transitions: &HashMap<TransitionProperty, Arc<AnimationValue>>,
|
|
|
++ ) -> bool {
|
|
|
++ use values::animated::{Animate, Procedure};
|
|
|
+
|
|
|
+ // |property| should be an animatable longhand
|
|
|
+ let animatable_longhand = AnimatableLonghand::from_transition_property(property).unwrap();
|
|
|
+
|
|
|
+ if existing_transitions.contains_key(property) {
|
|
|
+ // If there is an existing transition, update only if the end value differs.
|
|
|
+ // If the end value has not changed, we should leave the currently running
|
|
|
+ // transition as-is since we don't want to interrupt its timing function.
|
|
|
+@@ -1435,17 +1435,17 @@ impl<'le> TElement for GeckoElement<'le>
|
|
|
+
|
|
|
+ let from = AnimationValue::from_computed_values(&animatable_longhand,
|
|
|
+ before_change_style);
|
|
|
+ let to = AnimationValue::from_computed_values(&animatable_longhand,
|
|
|
+ after_change_style);
|
|
|
+
|
|
|
+ combined_duration > 0.0f32 &&
|
|
|
+ from != to &&
|
|
|
+- from.interpolate(&to, 0.5).is_ok()
|
|
|
++ from.animate(&to, Procedure::Interpolate { progress: 0.5 }).is_ok()
|
|
|
+ }
|
|
|
+
|
|
|
+ #[inline]
|
|
|
+ fn lang_attr(&self) -> Option<AttrValue> {
|
|
|
+ let ptr = unsafe { bindings::Gecko_LangValue(self.0) };
|
|
|
+ if ptr.is_null() {
|
|
|
+ None
|
|
|
+ } else {
|
|
|
+diff --git a/servo/components/style/macros.rs b/servo/components/style/macros.rs
|
|
|
+--- a/servo/components/style/macros.rs
|
|
|
++++ b/servo/components/style/macros.rs
|
|
|
+@@ -82,20 +82,23 @@ macro_rules! add_impls_for_keyword_enum
|
|
|
+
|
|
|
+ macro_rules! define_keyword_type {
|
|
|
+ ($name: ident, $css: expr) => {
|
|
|
+ #[allow(missing_docs)]
|
|
|
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
|
+ #[derive(Clone, ComputeSquaredDistance, Copy, PartialEq, ToCss)]
|
|
|
+ pub struct $name;
|
|
|
+
|
|
|
+- impl $crate::properties::animated_properties::Animatable for $name {
|
|
|
++ impl $crate::values::animated::Animate for $name {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, _other: &Self, _self_progress: f64, _other_progress: f64)
|
|
|
+- -> Result<Self, ()> {
|
|
|
++ fn animate(
|
|
|
++ &self,
|
|
|
++ _other: &Self,
|
|
|
++ _procedure: $crate::values::animated::Procedure,
|
|
|
++ ) -> Result<Self, ()> {
|
|
|
+ Ok($name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl fmt::Debug for $name {
|
|
|
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
+ write!(f, $css)
|
|
|
+ }
|
|
|
+diff --git a/servo/components/style/properties/data.py b/servo/components/style/properties/data.py
|
|
|
+--- a/servo/components/style/properties/data.py
|
|
|
++++ b/servo/components/style/properties/data.py
|
|
|
+@@ -206,17 +206,17 @@ class Longhand(object):
|
|
|
+ self.is_animatable_with_computed_value = animation_value_type == "ComputedValue" \
|
|
|
+ or animation_value_type == "discrete"
|
|
|
+ if self.logical:
|
|
|
+ # Logical properties will be animatable (i.e. the animation type is
|
|
|
+ # discrete). For now, it is still non-animatable.
|
|
|
+ self.animatable = False
|
|
|
+ self.transitionable = False
|
|
|
+ self.animation_type = None
|
|
|
+- # NB: Animatable implies clone because a property animation requires a
|
|
|
++ # NB: Animate implies clone because a property animation requires a
|
|
|
+ # copy of the computed value.
|
|
|
+ #
|
|
|
+ # See components/style/helpers/animated_properties.mako.rs.
|
|
|
+ self.need_clone = need_clone or self.animatable
|
|
|
+
|
|
|
+
|
|
|
+ class Shorthand(object):
|
|
|
+ def __init__(self, name, sub_properties, spec=None, experimental=False, internal=False,
|
|
|
+diff --git a/servo/components/style/properties/helpers.mako.rs b/servo/components/style/properties/helpers.mako.rs
|
|
|
+--- a/servo/components/style/properties/helpers.mako.rs
|
|
|
++++ b/servo/components/style/properties/helpers.mako.rs
|
|
|
+@@ -128,27 +128,22 @@
|
|
|
+ % if allow_empty and allow_empty != "NotInitial":
|
|
|
+ pub Vec<single_value::T>,
|
|
|
+ % else:
|
|
|
+ pub SmallVec<[single_value::T; 1]>,
|
|
|
+ % endif
|
|
|
+ );
|
|
|
+
|
|
|
+ % if need_animatable or animation_value_type == "ComputedValue":
|
|
|
+- use properties::animated_properties::Animatable;
|
|
|
+- use values::animated::ToAnimatedZero;
|
|
|
++ use values::animated::{Animate, Procedure, ToAnimatedZero};
|
|
|
+
|
|
|
+- impl Animatable for T {
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
|
|
+- -> Result<Self, ()> {
|
|
|
+- self.0.add_weighted(&other.0, self_portion, other_portion).map(T)
|
|
|
+- }
|
|
|
+-
|
|
|
+- fn add(&self, other: &Self) -> Result<Self, ()> {
|
|
|
+- self.0.add(&other.0).map(T)
|
|
|
++ impl Animate for T {
|
|
|
++ #[inline]
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(T(self.0.animate(&other.0, procedure)?))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for T {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
|
|
|
+ }
|
|
|
+ % endif
|
|
|
+diff --git a/servo/components/style/properties/helpers/animated_properties.mako.rs b/servo/components/style/properties/helpers/animated_properties.mako.rs
|
|
|
+--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
|
|
|
++++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
|
|
|
+@@ -3,46 +3,48 @@
|
|
|
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
+
|
|
|
+ <%namespace name="helpers" file="/helpers.mako.rs" />
|
|
|
+
|
|
|
+ <% from data import to_idl_name, SYSTEM_FONT_LONGHANDS %>
|
|
|
+
|
|
|
+ use app_units::Au;
|
|
|
+ use cssparser::Parser;
|
|
|
+-use euclid::{Point2D, Point3D, Size2D};
|
|
|
++use euclid::Point3D;
|
|
|
+ #[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap;
|
|
|
+ #[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4;
|
|
|
+ #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
|
|
|
+ #[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
|
|
|
+ #[cfg(feature = "gecko")] use gecko_string_cache::Atom;
|
|
|
++use itertools::{EitherOrBoth, Itertools};
|
|
|
+ use properties::{CSSWideKeyword, PropertyDeclaration};
|
|
|
+ use properties::longhands;
|
|
|
+ use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
|
|
|
+ use properties::longhands::border_spacing::computed_value::T as BorderSpacing;
|
|
|
+ use properties::longhands::font_weight::computed_value::T as FontWeight;
|
|
|
+ use properties::longhands::font_stretch::computed_value::T as FontStretch;
|
|
|
+ use properties::longhands::line_height::computed_value::T as LineHeight;
|
|
|
+ use properties::longhands::transform::computed_value::ComputedMatrix;
|
|
|
+ use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
|
|
|
+ use properties::longhands::transform::computed_value::T as TransformList;
|
|
|
+ use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
|
|
|
+ use properties::longhands::visibility::computed_value::T as Visibility;
|
|
|
+ #[cfg(feature = "gecko")] use properties::{PropertyId, PropertyDeclarationId, LonghandId};
|
|
|
+ #[cfg(feature = "gecko")] use properties::{ShorthandId};
|
|
|
+ use selectors::parser::SelectorParseError;
|
|
|
+ use smallvec::SmallVec;
|
|
|
++use std::borrow::Cow;
|
|
|
+ use std::cmp;
|
|
|
+ #[cfg(feature = "gecko")] use fnv::FnvHashMap;
|
|
|
+ use style_traits::ParseError;
|
|
|
+ use super::ComputedValues;
|
|
|
+ #[cfg(feature = "gecko")]
|
|
|
+ use values::Auto;
|
|
|
+ use values::{CSSFloat, CustomIdent, Either};
|
|
|
+-use values::animated::{ToAnimatedValue, ToAnimatedZero};
|
|
|
++use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
|
|
|
+ use values::animated::color::{Color as AnimatedColor, RGBA as AnimatedRGBA};
|
|
|
+ use values::animated::effects::BoxShadowList as AnimatedBoxShadowList;
|
|
|
+ use values::animated::effects::Filter as AnimatedFilter;
|
|
|
+ use values::animated::effects::FilterList as AnimatedFilterList;
|
|
|
+ use values::animated::effects::TextShadowList as AnimatedTextShadowList;
|
|
|
+ use values::computed::{Angle, BorderCornerRadius, CalcLengthOrPercentage};
|
|
|
+ use values::computed::{ClipRect, Context, ComputedUrl, ComputedValueAsSpecified};
|
|
|
+ use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
|
|
|
+@@ -53,49 +55,18 @@ use values::computed::length::{NonNegati
|
|
|
+ use values::computed::length::NonNegativeLengthOrPercentage;
|
|
|
+ use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
|
|
+ use values::generics::{GreaterThanOrEqualToOne, NonNegative};
|
|
|
+ use values::generics::effects::Filter;
|
|
|
+ use values::generics::position as generic_position;
|
|
|
+ use values::generics::svg::{SVGLength, SvgLengthOrPercentageOrNumber, SVGPaint};
|
|
|
+ use values::generics::svg::{SVGPaintKind, SVGStrokeDashArray, SVGOpacity};
|
|
|
+
|
|
|
+-/// A trait used to implement various procedures used during animation.
|
|
|
+-pub trait Animatable: Sized {
|
|
|
+- /// Performs a weighted sum of this value and |other|. This is used for
|
|
|
+- /// interpolation and addition of animation values.
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
|
|
+- -> Result<Self, ()>;
|
|
|
+-
|
|
|
+- /// [Interpolates][interpolation] a value with another for a given property.
|
|
|
+- ///
|
|
|
+- /// [interpolation]: https://w3c.github.io/web-animations/#animation-interpolation
|
|
|
+- fn interpolate(&self, other: &Self, progress: f64) -> Result<Self, ()> {
|
|
|
+- self.add_weighted(other, 1.0 - progress, progress)
|
|
|
+- }
|
|
|
+-
|
|
|
+- /// Returns the [sum][animation-addition] of this value and |other|.
|
|
|
+- ///
|
|
|
+- /// [animation-addition]: https://w3c.github.io/web-animations/#animation-addition
|
|
|
+- fn add(&self, other: &Self) -> Result<Self, ()> {
|
|
|
+- self.add_weighted(other, 1.0, 1.0)
|
|
|
+- }
|
|
|
+-
|
|
|
+- /// [Accumulates][animation-accumulation] this value onto itself (|count| - 1) times then
|
|
|
+- /// accumulates |other| onto the result.
|
|
|
+- /// If |count| is zero, the result will be |other|.
|
|
|
+- ///
|
|
|
+- /// [animation-accumulation]: https://w3c.github.io/web-animations/#animation-accumulation
|
|
|
+- fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> {
|
|
|
+- self.add_weighted(other, count as f64, 1.0)
|
|
|
+- }
|
|
|
+-}
|
|
|
+-
|
|
|
+ /// https://drafts.csswg.org/css-transitions/#animtype-repeatable-list
|
|
|
+-pub trait RepeatableListAnimatable: Animatable {}
|
|
|
++pub trait RepeatableListAnimatable: Animate {}
|
|
|
+
|
|
|
+ /// A longhand property whose animation type is not "none".
|
|
|
+ ///
|
|
|
+ /// NOTE: This includes the 'display' property since it is animatable from SMIL even though it is
|
|
|
+ /// not animatable from CSS animations or Web Animations. CSS transitions also does not allow
|
|
|
+ /// animating 'display', but for CSS transitions we have the separate TransitionProperty type.
|
|
|
+ #[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
|
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
|
+@@ -444,17 +415,17 @@ impl AnimatedProperty {
|
|
|
+ match *self {
|
|
|
+ % for prop in data.longhands:
|
|
|
+ % if prop.animatable:
|
|
|
+ AnimatedProperty::${prop.camel_case}(ref from, ref to) => {
|
|
|
+ // https://w3c.github.io/web-animations/#discrete-animation-type
|
|
|
+ % if prop.animation_value_type == "discrete":
|
|
|
+ let value = if progress < 0.5 { from.clone() } else { to.clone() };
|
|
|
+ % else:
|
|
|
+- let value = match from.interpolate(to, progress) {
|
|
|
++ let value = match from.animate(to, Procedure::Interpolate { progress }) {
|
|
|
+ Ok(value) => value,
|
|
|
+ Err(()) => return,
|
|
|
+ };
|
|
|
+ % endif
|
|
|
+ % if not prop.is_animatable_with_computed_value:
|
|
|
+ let value: longhands::${prop.ident}::computed_value::T =
|
|
|
+ ToAnimatedValue::from_animated_value(value);
|
|
|
+ % endif
|
|
|
+@@ -655,88 +626,48 @@ impl AnimationValue {
|
|
|
+ )
|
|
|
+ }
|
|
|
+ % endif
|
|
|
+ % endfor
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for AnimationValue {
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
|
|
+- -> Result<Self, ()> {
|
|
|
+- match (self, other) {
|
|
|
+- % for prop in data.longhands:
|
|
|
+- % if prop.animatable:
|
|
|
+- (&AnimationValue::${prop.camel_case}(ref from),
|
|
|
+- &AnimationValue::${prop.camel_case}(ref to)) => {
|
|
|
+- % if prop.animation_value_type == "discrete":
|
|
|
+- if self_portion > other_portion {
|
|
|
+- Ok(AnimationValue::${prop.camel_case}(from.clone()))
|
|
|
+- } else {
|
|
|
+- Ok(AnimationValue::${prop.camel_case}(to.clone()))
|
|
|
+- }
|
|
|
+- % else:
|
|
|
+- from.add_weighted(to, self_portion, other_portion)
|
|
|
+- .map(AnimationValue::${prop.camel_case})
|
|
|
+- % endif
|
|
|
+- }
|
|
|
+- % endif
|
|
|
+- % endfor
|
|
|
+- _ => {
|
|
|
+- panic!("Expected weighted addition of computed values of the same \
|
|
|
+- property, got: {:?}, {:?}", self, other);
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- fn add(&self, other: &Self) -> Result<Self, ()> {
|
|
|
++impl Animate for AnimationValue {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ match (self, other) {
|
|
|
+ % for prop in data.longhands:
|
|
|
+- % if prop.animatable:
|
|
|
+- % if prop.animation_value_type == "discrete":
|
|
|
+- (&AnimationValue::${prop.camel_case}(_),
|
|
|
+- &AnimationValue::${prop.camel_case}(_)) => {
|
|
|
+- Err(())
|
|
|
+- }
|
|
|
+- % else:
|
|
|
+- (&AnimationValue::${prop.camel_case}(ref from),
|
|
|
+- &AnimationValue::${prop.camel_case}(ref to)) => {
|
|
|
+- from.add(to).map(AnimationValue::${prop.camel_case})
|
|
|
+- }
|
|
|
+- % endif
|
|
|
+- % endif
|
|
|
++ % if prop.animatable:
|
|
|
++ % if prop.animation_value_type != "discrete":
|
|
|
++ (
|
|
|
++ &AnimationValue::${prop.camel_case}(ref this),
|
|
|
++ &AnimationValue::${prop.camel_case}(ref other),
|
|
|
++ ) => {
|
|
|
++ Ok(AnimationValue::${prop.camel_case}(
|
|
|
++ this.animate(other, procedure)?,
|
|
|
++ ))
|
|
|
++ },
|
|
|
++ % else:
|
|
|
++ (
|
|
|
++ &AnimationValue::${prop.camel_case}(ref this),
|
|
|
++ &AnimationValue::${prop.camel_case}(ref other),
|
|
|
++ ) => {
|
|
|
++ if let Procedure::Interpolate { progress } = procedure {
|
|
|
++ Ok(AnimationValue::${prop.camel_case}(
|
|
|
++ if progress < 0.5 { this.clone() } else { other.clone() },
|
|
|
++ ))
|
|
|
++ } else {
|
|
|
++ Err(())
|
|
|
++ }
|
|
|
++ },
|
|
|
++ % endif
|
|
|
++ % endif
|
|
|
+ % endfor
|
|
|
+ _ => {
|
|
|
+- panic!("Expected addition of computed values of the same \
|
|
|
+- property, got: {:?}, {:?}", self, other);
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> {
|
|
|
+- match (self, other) {
|
|
|
+- % for prop in data.longhands:
|
|
|
+- % if prop.animatable:
|
|
|
+- % if prop.animation_value_type == "discrete":
|
|
|
+- (&AnimationValue::${prop.camel_case}(_),
|
|
|
+- &AnimationValue::${prop.camel_case}(_)) => {
|
|
|
+- Err(())
|
|
|
+- }
|
|
|
+- % else:
|
|
|
+- (&AnimationValue::${prop.camel_case}(ref from),
|
|
|
+- &AnimationValue::${prop.camel_case}(ref to)) => {
|
|
|
+- from.accumulate(to, count).map(AnimationValue::${prop.camel_case})
|
|
|
+- }
|
|
|
+- % endif
|
|
|
+- % endif
|
|
|
+- % endfor
|
|
|
+- _ => {
|
|
|
+- panic!("Expected accumulation of computed values of the same \
|
|
|
+- property, got: {:?}, {:?}", self, other);
|
|
|
++ panic!("Unexpected AnimationValue::animate call, got: {:?}, {:?}", self, other);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for AnimationValue {
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+ match (self, other) {
|
|
|
+@@ -782,27 +713,29 @@ impl ToAnimatedZero for AnimationValue {
|
|
|
+
|
|
|
+ impl RepeatableListAnimatable for LengthOrPercentage {}
|
|
|
+ impl RepeatableListAnimatable for Either<f32, LengthOrPercentage> {}
|
|
|
+ impl RepeatableListAnimatable for Either<NonNegativeNumber, NonNegativeLengthOrPercentage> {}
|
|
|
+ impl RepeatableListAnimatable for SvgLengthOrPercentageOrNumber<NonNegativeLengthOrPercentage, NonNegativeNumber> {}
|
|
|
+
|
|
|
+ macro_rules! repeated_vec_impl {
|
|
|
+ ($($ty:ty),*) => {
|
|
|
+- $(impl<T: RepeatableListAnimatable> Animatable for $ty {
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
|
|
+- -> Result<Self, ()> {
|
|
|
++ $(impl<T> Animate for $ty
|
|
|
++ where
|
|
|
++ T: RepeatableListAnimatable,
|
|
|
++ {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ // If the length of either list is zero, the least common multiple is undefined.
|
|
|
+ if self.is_empty() || other.is_empty() {
|
|
|
+ return Err(());
|
|
|
+ }
|
|
|
+ use num_integer::lcm;
|
|
|
+ let len = lcm(self.len(), other.len());
|
|
|
+- self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(me, you)| {
|
|
|
+- me.add_weighted(you, self_portion, other_portion)
|
|
|
++ self.iter().cycle().zip(other.iter().cycle()).take(len).map(|(this, other)| {
|
|
|
++ this.animate(other, procedure)
|
|
|
+ }).collect()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<T> ComputeSquaredDistance for $ty
|
|
|
+ where
|
|
|
+ T: ComputeSquaredDistance + RepeatableListAnimatable,
|
|
|
+ {
|
|
|
+@@ -818,73 +751,27 @@ macro_rules! repeated_vec_impl {
|
|
|
+ }).sum()
|
|
|
+ }
|
|
|
+ })*
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ repeated_vec_impl!(SmallVec<[T; 1]>, Vec<T>);
|
|
|
+
|
|
|
+-/// https://drafts.csswg.org/css-transitions/#animtype-number
|
|
|
+-impl Animatable for Au {
|
|
|
+- #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- Ok(Au((self.0 as f64 * self_portion + other.0 as f64 * other_portion).round() as i32))
|
|
|
+- }
|
|
|
+-}
|
|
|
+-
|
|
|
+-impl <T> Animatable for Option<T>
|
|
|
+- where T: Animatable,
|
|
|
+-{
|
|
|
+- #[inline]
|
|
|
+- fn add_weighted(&self, other: &Option<T>, self_portion: f64, other_portion: f64) -> Result<Option<T>, ()> {
|
|
|
+- match (self, other) {
|
|
|
+- (&Some(ref this), &Some(ref other)) => {
|
|
|
+- Ok(this.add_weighted(other, self_portion, other_portion).ok())
|
|
|
+- }
|
|
|
+- (&None, &None) => Ok(None),
|
|
|
+- _ => Err(()),
|
|
|
+- }
|
|
|
+- }
|
|
|
+-}
|
|
|
+-
|
|
|
+-/// https://drafts.csswg.org/css-transitions/#animtype-number
|
|
|
+-impl Animatable for f32 {
|
|
|
++/// https://drafts.csswg.org/css-transitions/#animtype-visibility
|
|
|
++impl Animate for Visibility {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &f32, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- Ok((*self as f64 * self_portion + *other as f64 * other_portion) as f32)
|
|
|
+- }
|
|
|
+-}
|
|
|
+-
|
|
|
+-/// https://drafts.csswg.org/css-transitions/#animtype-number
|
|
|
+-impl Animatable for f64 {
|
|
|
+- #[inline]
|
|
|
+- fn add_weighted(&self, other: &f64, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- Ok(*self * self_portion + *other * other_portion)
|
|
|
+- }
|
|
|
+-}
|
|
|
+-
|
|
|
+-/// https://drafts.csswg.org/css-transitions/#animtype-integer
|
|
|
+-impl Animatable for i32 {
|
|
|
+- #[inline]
|
|
|
+- fn add_weighted(&self, other: &i32, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- Ok((*self as f64 * self_portion + *other as f64 * other_portion + 0.5).floor() as i32)
|
|
|
+- }
|
|
|
+-}
|
|
|
+-
|
|
|
+-/// https://drafts.csswg.org/css-transitions/#animtype-visibility
|
|
|
+-impl Animatable for Visibility {
|
|
|
+- #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ let (this_weight, other_weight) = procedure.weights();
|
|
|
+ match (*self, *other) {
|
|
|
+ (Visibility::visible, _) => {
|
|
|
+- Ok(if self_portion > 0.0 { *self } else { *other })
|
|
|
++ Ok(if this_weight > 0.0 { *self } else { *other })
|
|
|
+ },
|
|
|
+ (_, Visibility::visible) => {
|
|
|
+- Ok(if other_portion > 0.0 { *other } else { *self })
|
|
|
++ Ok(if other_weight > 0.0 { *other } else { *self })
|
|
|
+ },
|
|
|
+ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for Visibility {
|
|
|
+ #[inline]
|
|
|
+@@ -895,47 +782,29 @@ impl ComputeSquaredDistance for Visibili
|
|
|
+
|
|
|
+ impl ToAnimatedZero for Visibility {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+ Err(())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<T: Animatable + Copy> Animatable for Size2D<T> {
|
|
|
+- #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- let width = self.width.add_weighted(&other.width, self_portion, other_portion)?;
|
|
|
+- let height = self.height.add_weighted(&other.height, self_portion, other_portion)?;
|
|
|
+-
|
|
|
+- Ok(Size2D::new(width, height))
|
|
|
+- }
|
|
|
+-}
|
|
|
+-
|
|
|
+-impl<T: Animatable + Copy> Animatable for Point2D<T> {
|
|
|
++/// https://drafts.csswg.org/css-transitions/#animtype-length
|
|
|
++impl Animate for VerticalAlign {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- let x = self.x.add_weighted(&other.x, self_portion, other_portion)?;
|
|
|
+- let y = self.y.add_weighted(&other.y, self_portion, other_portion)?;
|
|
|
+-
|
|
|
+- Ok(Point2D::new(x, y))
|
|
|
+- }
|
|
|
+-}
|
|
|
+-
|
|
|
+-/// https://drafts.csswg.org/css-transitions/#animtype-length
|
|
|
+-impl Animatable for VerticalAlign {
|
|
|
+- #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- match (*self, *other) {
|
|
|
+- (VerticalAlign::LengthOrPercentage(ref this),
|
|
|
+- VerticalAlign::LengthOrPercentage(ref other)) => {
|
|
|
+- this.add_weighted(other, self_portion, other_portion).map(|value| {
|
|
|
+- VerticalAlign::LengthOrPercentage(value)
|
|
|
+- })
|
|
|
+- }
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ match (self, other) {
|
|
|
++ (
|
|
|
++ &VerticalAlign::LengthOrPercentage(ref this),
|
|
|
++ &VerticalAlign::LengthOrPercentage(ref other),
|
|
|
++ ) => {
|
|
|
++ Ok(VerticalAlign::LengthOrPercentage(
|
|
|
++ this.animate(other, procedure)?
|
|
|
++ ))
|
|
|
++ },
|
|
|
+ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for VerticalAlign {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+@@ -953,112 +822,114 @@ impl ComputeSquaredDistance for Vertical
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for VerticalAlign {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
|
|
+-impl Animatable for CalcLengthOrPercentage {
|
|
|
++impl Animate for CalcLengthOrPercentage {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- fn add_weighted_half<T>(this: Option<T>,
|
|
|
+- other: Option<T>,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64)
|
|
|
+- -> Result<Option<T>, ()>
|
|
|
+- where T: Default + Animatable,
|
|
|
+- {
|
|
|
+- match (this, other) {
|
|
|
+- (None, None) => Ok(None),
|
|
|
+- (this, other) => {
|
|
|
+- let this = this.unwrap_or(T::default());
|
|
|
+- let other = other.unwrap_or(T::default());
|
|
|
+- this.add_weighted(&other, self_portion, other_portion).map(Some)
|
|
|
+- }
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ let animate_percentage_half = |this: Option<Percentage>, other: Option<Percentage>| {
|
|
|
++ if this.is_none() && other.is_none() {
|
|
|
++ return Ok(None);
|
|
|
+ }
|
|
|
+- }
|
|
|
++ let this = this.unwrap_or_default();
|
|
|
++ let other = other.unwrap_or_default();
|
|
|
++ Ok(Some(this.animate(&other, procedure)?))
|
|
|
++ };
|
|
|
+
|
|
|
+- let length = self.unclamped_length().add_weighted(&other.unclamped_length(), self_portion, other_portion)?;
|
|
|
+- let percentage = add_weighted_half(self.percentage, other.percentage, self_portion, other_portion)?;
|
|
|
++ let length = self.unclamped_length().animate(&other.unclamped_length(), procedure)?;
|
|
|
++ let percentage = animate_percentage_half(self.percentage, other.percentage)?;
|
|
|
+ Ok(CalcLengthOrPercentage::with_clamping_mode(length, percentage, self.clamping_mode))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
|
|
+-impl Animatable for LengthOrPercentage {
|
|
|
++impl Animate for LengthOrPercentage {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- match (*self, *other) {
|
|
|
+- (LengthOrPercentage::Length(ref this),
|
|
|
+- LengthOrPercentage::Length(ref other)) => {
|
|
|
+- this.add_weighted(other, self_portion, other_portion)
|
|
|
+- .map(LengthOrPercentage::Length)
|
|
|
+- }
|
|
|
+- (LengthOrPercentage::Percentage(ref this),
|
|
|
+- LengthOrPercentage::Percentage(ref other)) => {
|
|
|
+- this.add_weighted(other, self_portion, other_portion)
|
|
|
+- .map(LengthOrPercentage::Percentage)
|
|
|
+- }
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ match (self, other) {
|
|
|
++ (
|
|
|
++ &LengthOrPercentage::Length(ref this),
|
|
|
++ &LengthOrPercentage::Length(ref other),
|
|
|
++ ) => {
|
|
|
++ Ok(LengthOrPercentage::Length(this.animate(other, procedure)?))
|
|
|
++ },
|
|
|
++ (
|
|
|
++ &LengthOrPercentage::Percentage(ref this),
|
|
|
++ &LengthOrPercentage::Percentage(ref other),
|
|
|
++ ) => {
|
|
|
++ Ok(LengthOrPercentage::Percentage(this.animate(other, procedure)?))
|
|
|
++ },
|
|
|
+ (this, other) => {
|
|
|
+ // Special handling for zero values since these should not require calc().
|
|
|
+ if this.is_definitely_zero() {
|
|
|
+- return other.add_weighted(&other, 0., other_portion)
|
|
|
+- } else if other.is_definitely_zero() {
|
|
|
+- return this.add_weighted(self, self_portion, 0.)
|
|
|
++ return other.to_animated_zero()?.animate(other, procedure);
|
|
|
++ }
|
|
|
++ if other.is_definitely_zero() {
|
|
|
++ return this.animate(&this.to_animated_zero()?, procedure);
|
|
|
+ }
|
|
|
+
|
|
|
+- let this: CalcLengthOrPercentage = From::from(this);
|
|
|
+- let other: CalcLengthOrPercentage = From::from(other);
|
|
|
+- this.add_weighted(&other, self_portion, other_portion)
|
|
|
+- .map(LengthOrPercentage::Calc)
|
|
|
++ let this = CalcLengthOrPercentage::from(*this);
|
|
|
++ let other = CalcLengthOrPercentage::from(*other);
|
|
|
++ Ok(LengthOrPercentage::Calc(this.animate(&other, procedure)?))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for LengthOrPercentage {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+- match self {
|
|
|
+- &LengthOrPercentage::Length(_) | &LengthOrPercentage::Calc(_) =>
|
|
|
+- Ok(LengthOrPercentage::zero()),
|
|
|
+- &LengthOrPercentage::Percentage(_) =>
|
|
|
+- Ok(LengthOrPercentage::Percentage(Percentage::zero())),
|
|
|
+- }
|
|
|
++ match *self {
|
|
|
++ LengthOrPercentage::Length(ref length) => {
|
|
|
++ Ok(LengthOrPercentage::Length(length.to_animated_zero()?))
|
|
|
++ },
|
|
|
++ LengthOrPercentage::Percentage(ref percentage) => {
|
|
|
++ Ok(LengthOrPercentage::Percentage(percentage.to_animated_zero()?))
|
|
|
++ },
|
|
|
++ LengthOrPercentage::Calc(ref calc) => {
|
|
|
++ Ok(LengthOrPercentage::Calc(calc.to_animated_zero()?))
|
|
|
++ },
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
|
|
+-impl Animatable for LengthOrPercentageOrAuto {
|
|
|
++impl Animate for LengthOrPercentageOrAuto {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- match (*self, *other) {
|
|
|
+- (LengthOrPercentageOrAuto::Length(ref this),
|
|
|
+- LengthOrPercentageOrAuto::Length(ref other)) => {
|
|
|
+- this.add_weighted(other, self_portion, other_portion)
|
|
|
+- .map(LengthOrPercentageOrAuto::Length)
|
|
|
+- }
|
|
|
+- (LengthOrPercentageOrAuto::Percentage(ref this),
|
|
|
+- LengthOrPercentageOrAuto::Percentage(ref other)) => {
|
|
|
+- this.add_weighted(other, self_portion, other_portion)
|
|
|
+- .map(LengthOrPercentageOrAuto::Percentage)
|
|
|
+- }
|
|
|
+- (LengthOrPercentageOrAuto::Auto, LengthOrPercentageOrAuto::Auto) => {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ match (self, other) {
|
|
|
++ (
|
|
|
++ &LengthOrPercentageOrAuto::Length(ref this),
|
|
|
++ &LengthOrPercentageOrAuto::Length(ref other),
|
|
|
++ ) => {
|
|
|
++ Ok(LengthOrPercentageOrAuto::Length(this.animate(other, procedure)?))
|
|
|
++ },
|
|
|
++ (
|
|
|
++ &LengthOrPercentageOrAuto::Percentage(ref this),
|
|
|
++ &LengthOrPercentageOrAuto::Percentage(ref other),
|
|
|
++ ) => {
|
|
|
++ Ok(LengthOrPercentageOrAuto::Percentage(
|
|
|
++ this.animate(other, procedure)?,
|
|
|
++ ))
|
|
|
++ },
|
|
|
++ (&LengthOrPercentageOrAuto::Auto, &LengthOrPercentageOrAuto::Auto) => {
|
|
|
+ Ok(LengthOrPercentageOrAuto::Auto)
|
|
|
+- }
|
|
|
++ },
|
|
|
+ (this, other) => {
|
|
|
+- let this: Option<CalcLengthOrPercentage> = From::from(this);
|
|
|
+- let other: Option<CalcLengthOrPercentage> = From::from(other);
|
|
|
+- match this.add_weighted(&other, self_portion, other_portion) {
|
|
|
+- Ok(Some(result)) => Ok(LengthOrPercentageOrAuto::Calc(result)),
|
|
|
+- _ => Err(()),
|
|
|
+- }
|
|
|
+- }
|
|
|
++ let this: Option<CalcLengthOrPercentage> = From::from(*this);
|
|
|
++ let other: Option<CalcLengthOrPercentage> = From::from(*other);
|
|
|
++ Ok(LengthOrPercentageOrAuto::Calc(
|
|
|
++ this.animate(&other, procedure)?.ok_or(())?,
|
|
|
++ ))
|
|
|
++ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for LengthOrPercentageOrAuto {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+ match *self {
|
|
|
+@@ -1068,40 +939,43 @@ impl ToAnimatedZero for LengthOrPercenta
|
|
|
+ Ok(LengthOrPercentageOrAuto::Length(Au(0)))
|
|
|
+ },
|
|
|
+ LengthOrPercentageOrAuto::Auto => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
|
|
+-impl Animatable for LengthOrPercentageOrNone {
|
|
|
++impl Animate for LengthOrPercentageOrNone {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- match (*self, *other) {
|
|
|
+- (LengthOrPercentageOrNone::Length(ref this),
|
|
|
+- LengthOrPercentageOrNone::Length(ref other)) => {
|
|
|
+- this.add_weighted(other, self_portion, other_portion)
|
|
|
+- .map(LengthOrPercentageOrNone::Length)
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ match (self, other) {
|
|
|
++ (
|
|
|
++ &LengthOrPercentageOrNone::Length(ref this),
|
|
|
++ &LengthOrPercentageOrNone::Length(ref other),
|
|
|
++ ) => {
|
|
|
++ Ok(LengthOrPercentageOrNone::Length(this.animate(other, procedure)?))
|
|
|
++ },
|
|
|
++ (
|
|
|
++ &LengthOrPercentageOrNone::Percentage(ref this),
|
|
|
++ &LengthOrPercentageOrNone::Percentage(ref other),
|
|
|
++ ) => {
|
|
|
++ Ok(LengthOrPercentageOrNone::Percentage(
|
|
|
++ this.animate(other, procedure)?,
|
|
|
++ ))
|
|
|
+ }
|
|
|
+- (LengthOrPercentageOrNone::Percentage(ref this),
|
|
|
+- LengthOrPercentageOrNone::Percentage(ref other)) => {
|
|
|
+- this.add_weighted(other, self_portion, other_portion)
|
|
|
+- .map(LengthOrPercentageOrNone::Percentage)
|
|
|
+- }
|
|
|
+- (LengthOrPercentageOrNone::None, LengthOrPercentageOrNone::None) => {
|
|
|
++ (&LengthOrPercentageOrNone::None, &LengthOrPercentageOrNone::None) => {
|
|
|
+ Ok(LengthOrPercentageOrNone::None)
|
|
|
+- }
|
|
|
++ },
|
|
|
+ (this, other) => {
|
|
|
+- let this = <Option<CalcLengthOrPercentage>>::from(this);
|
|
|
+- let other = <Option<CalcLengthOrPercentage>>::from(other);
|
|
|
+- match this.add_weighted(&other, self_portion, other_portion) {
|
|
|
+- Ok(Some(result)) => Ok(LengthOrPercentageOrNone::Calc(result)),
|
|
|
+- _ => Err(()),
|
|
|
+- }
|
|
|
++ let this = <Option<CalcLengthOrPercentage>>::from(*this);
|
|
|
++ let other = <Option<CalcLengthOrPercentage>>::from(*other);
|
|
|
++ Ok(LengthOrPercentageOrNone::Calc(
|
|
|
++ this.animate(&other, procedure)?.ok_or(())?,
|
|
|
++ ))
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for LengthOrPercentageOrNone {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+@@ -1112,24 +986,27 @@ impl ToAnimatedZero for LengthOrPercenta
|
|
|
+ Ok(LengthOrPercentageOrNone::Length(Au(0)))
|
|
|
+ },
|
|
|
+ LengthOrPercentageOrNone::None => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
|
|
+-impl Animatable for MozLength {
|
|
|
++impl Animate for MozLength {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- match (*self, *other) {
|
|
|
+- (MozLength::LengthOrPercentageOrAuto(ref this),
|
|
|
+- MozLength::LengthOrPercentageOrAuto(ref other)) => {
|
|
|
+- this.add_weighted(other, self_portion, other_portion)
|
|
|
+- .map(MozLength::LengthOrPercentageOrAuto)
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ match (self, other) {
|
|
|
++ (
|
|
|
++ &MozLength::LengthOrPercentageOrAuto(ref this),
|
|
|
++ &MozLength::LengthOrPercentageOrAuto(ref other),
|
|
|
++ ) => {
|
|
|
++ Ok(MozLength::LengthOrPercentageOrAuto(
|
|
|
++ this.animate(other, procedure)?,
|
|
|
++ ))
|
|
|
+ }
|
|
|
+ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for MozLength {
|
|
|
+ #[inline]
|
|
|
+@@ -1139,67 +1016,69 @@ impl ToAnimatedZero for MozLength {
|
|
|
+ Ok(MozLength::LengthOrPercentageOrAuto(length.to_animated_zero()?))
|
|
|
+ },
|
|
|
+ _ => Err(())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// https://drafts.csswg.org/css-transitions/#animtype-lpcalc
|
|
|
+-impl Animatable for MaxLength {
|
|
|
++impl Animate for MaxLength {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- match (*self, *other) {
|
|
|
+- (MaxLength::LengthOrPercentageOrNone(ref this),
|
|
|
+- MaxLength::LengthOrPercentageOrNone(ref other)) => {
|
|
|
+- this.add_weighted(other, self_portion, other_portion)
|
|
|
+- .map(MaxLength::LengthOrPercentageOrNone)
|
|
|
+- }
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ match (self, other) {
|
|
|
++ (
|
|
|
++ &MaxLength::LengthOrPercentageOrNone(ref this),
|
|
|
++ &MaxLength::LengthOrPercentageOrNone(ref other),
|
|
|
++ ) => {
|
|
|
++ Ok(MaxLength::LengthOrPercentageOrNone(
|
|
|
++ this.animate(other, procedure)?,
|
|
|
++ ))
|
|
|
++ },
|
|
|
+ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for MaxLength {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// http://dev.w3.org/csswg/css-transitions/#animtype-font-weight
|
|
|
+-impl Animatable for FontWeight {
|
|
|
++impl Animate for FontWeight {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ let a = self.0 as f64;
|
|
|
+ let b = other.0 as f64;
|
|
|
+ const NORMAL: f64 = 400.;
|
|
|
+- let weight = (a - NORMAL) * self_portion + (b - NORMAL) * other_portion + NORMAL;
|
|
|
++ let (this_weight, other_weight) = procedure.weights();
|
|
|
++ let weight = (a - NORMAL) * this_weight + (b - NORMAL) * other_weight + NORMAL;
|
|
|
+ let weight = (weight.max(100.).min(900.) / 100.).round() * 100.;
|
|
|
+ Ok(FontWeight(weight as u16))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for FontWeight {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+ Ok(FontWeight::normal())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// https://drafts.csswg.org/css-fonts/#font-stretch-prop
|
|
|
+-impl Animatable for FontStretch {
|
|
|
++impl Animate for FontStretch {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
|
|
+- -> Result<Self, ()>
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>
|
|
|
+ {
|
|
|
+ let from = f64::from(*self);
|
|
|
+ let to = f64::from(*other);
|
|
|
+- // FIXME: When `const fn` is available in release rust, make |normal|, below, const.
|
|
|
+ let normal = f64::from(FontStretch::normal);
|
|
|
+- let result = (from - normal) * self_portion + (to - normal) * other_portion + normal;
|
|
|
+-
|
|
|
++ let (this_weight, other_weight) = procedure.weights();
|
|
|
++ let result = (from - normal) * this_weight + (to - normal) * other_weight + normal;
|
|
|
+ Ok(result.into())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for FontStretch {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+ f64::from(*self).compute_squared_distance(&(*other).into())
|
|
|
+@@ -1236,23 +1115,26 @@ impl Into<FontStretch> for f64 {
|
|
|
+ let index = (self + 0.5).floor().min(9.0).max(1.0);
|
|
|
+ static FONT_STRETCH_ENUM_MAP: [FontStretch; 9] =
|
|
|
+ [ ultra_condensed, extra_condensed, condensed, semi_condensed, normal,
|
|
|
+ semi_expanded, expanded, extra_expanded, ultra_expanded ];
|
|
|
+ FONT_STRETCH_ENUM_MAP[(index - 1.0) as usize]
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-/// https://drafts.csswg.org/css-transitions/#animtype-simple-list
|
|
|
+-impl<H: Animatable, V: Animatable> Animatable for generic_position::Position<H, V> {
|
|
|
++impl<H, V> Animate for generic_position::Position<H, V>
|
|
|
++where
|
|
|
++ H: Animate,
|
|
|
++ V: Animate,
|
|
|
++{
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ Ok(generic_position::Position {
|
|
|
+- horizontal: self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?,
|
|
|
+- vertical: self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?,
|
|
|
++ horizontal: self.horizontal.animate(&other.horizontal, procedure)?,
|
|
|
++ vertical: self.vertical.animate(&other.vertical, procedure)?,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<H, V> ToAnimatedZero for generic_position::Position<H, V>
|
|
|
+ where
|
|
|
+ H: ToAnimatedZero,
|
|
|
+ V: ToAnimatedZero,
|
|
|
+@@ -1265,218 +1147,172 @@ where
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<H, V> RepeatableListAnimatable for generic_position::Position<H, V>
|
|
|
+ where H: RepeatableListAnimatable, V: RepeatableListAnimatable {}
|
|
|
+
|
|
|
+ /// https://drafts.csswg.org/css-transitions/#animtype-rect
|
|
|
+-impl Animatable for ClipRect {
|
|
|
++impl Animate for ClipRect {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
|
|
+- -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ Ok(ClipRect {
|
|
|
+- top: self.top.add_weighted(&other.top, self_portion, other_portion)?,
|
|
|
+- right: self.right.add_weighted(&other.right, self_portion, other_portion)?,
|
|
|
+- bottom: self.bottom.add_weighted(&other.bottom, self_portion, other_portion)?,
|
|
|
+- left: self.left.add_weighted(&other.left, self_portion, other_portion)?,
|
|
|
++ top: self.top.animate(&other.top, procedure)?,
|
|
|
++ right: self.right.animate(&other.right, procedure)?,
|
|
|
++ bottom: self.bottom.animate(&other.bottom, procedure)?,
|
|
|
++ left: self.left.animate(&other.left, procedure)?,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for ClipRect {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
|
|
|
+ }
|
|
|
+
|
|
|
+-/// Check if it's possible to do a direct numerical interpolation
|
|
|
+-/// between these two transform lists.
|
|
|
+-/// http://dev.w3.org/csswg/css-transforms/#transform-transform-animation
|
|
|
+-fn can_interpolate_list(from_list: &[TransformOperation],
|
|
|
+- to_list: &[TransformOperation]) -> bool {
|
|
|
+- // Lists must be equal length
|
|
|
+- if from_list.len() != to_list.len() {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- // Each transform operation must match primitive type in other list
|
|
|
+- for (from, to) in from_list.iter().zip(to_list) {
|
|
|
+- match (from, to) {
|
|
|
+- (&TransformOperation::Matrix(..), &TransformOperation::Matrix(..)) |
|
|
|
+- (&TransformOperation::Skew(..), &TransformOperation::Skew(..)) |
|
|
|
+- (&TransformOperation::Translate(..), &TransformOperation::Translate(..)) |
|
|
|
+- (&TransformOperation::Scale(..), &TransformOperation::Scale(..)) |
|
|
|
+- (&TransformOperation::Rotate(..), &TransformOperation::Rotate(..)) |
|
|
|
+- (&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => {}
|
|
|
+- _ => {
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- true
|
|
|
+-}
|
|
|
+-
|
|
|
+ /// Build an equivalent 'identity transform function list' based
|
|
|
+ /// on an existing transform list.
|
|
|
+ /// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
|
|
|
+-fn build_identity_transform_list(list: &[TransformOperation]) -> Vec<TransformOperation> {
|
|
|
+- let mut result = vec!();
|
|
|
+-
|
|
|
+- for operation in list {
|
|
|
+- match *operation {
|
|
|
++impl ToAnimatedZero for TransformOperation {
|
|
|
++ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
++ match *self {
|
|
|
+ TransformOperation::Matrix(..) => {
|
|
|
+- let identity = ComputedMatrix::identity();
|
|
|
+- result.push(TransformOperation::Matrix(identity));
|
|
|
+- }
|
|
|
+- TransformOperation::MatrixWithPercents(..) => {}
|
|
|
+- TransformOperation::Skew(..) => {
|
|
|
+- result.push(TransformOperation::Skew(Angle::zero(), Angle::zero()))
|
|
|
+- }
|
|
|
+- TransformOperation::Translate(..) => {
|
|
|
+- result.push(TransformOperation::Translate(LengthOrPercentage::zero(),
|
|
|
+- LengthOrPercentage::zero(),
|
|
|
+- Au(0)));
|
|
|
+- }
|
|
|
++ Ok(TransformOperation::Matrix(ComputedMatrix::identity()))
|
|
|
++ },
|
|
|
++ TransformOperation::MatrixWithPercents(..) => {
|
|
|
++ // FIXME(nox): Should be MatrixWithPercents value.
|
|
|
++ Ok(TransformOperation::Matrix(ComputedMatrix::identity()))
|
|
|
++ },
|
|
|
++ TransformOperation::Skew(sx, sy) => {
|
|
|
++ Ok(TransformOperation::Skew(
|
|
|
++ sx.to_animated_zero()?,
|
|
|
++ sy.to_animated_zero()?,
|
|
|
++ ))
|
|
|
++ },
|
|
|
++ TransformOperation::Translate(ref tx, ref ty, ref tz) => {
|
|
|
++ Ok(TransformOperation::Translate(
|
|
|
++ tx.to_animated_zero()?,
|
|
|
++ ty.to_animated_zero()?,
|
|
|
++ tz.to_animated_zero()?,
|
|
|
++ ))
|
|
|
++ },
|
|
|
+ TransformOperation::Scale(..) => {
|
|
|
+- result.push(TransformOperation::Scale(1.0, 1.0, 1.0));
|
|
|
+- }
|
|
|
++ Ok(TransformOperation::Scale(1.0, 1.0, 1.0))
|
|
|
++ },
|
|
|
+ TransformOperation::Rotate(x, y, z, a) => {
|
|
|
+ let (x, y, z, _) = get_normalized_vector_and_angle(x, y, z, a);
|
|
|
+- result.push(TransformOperation::Rotate(x, y, z, Angle::zero()));
|
|
|
+- }
|
|
|
++ Ok(TransformOperation::Rotate(x, y, z, Angle::zero()))
|
|
|
++ },
|
|
|
+ TransformOperation::Perspective(..) |
|
|
|
+ TransformOperation::AccumulateMatrix { .. } |
|
|
|
+ TransformOperation::InterpolateMatrix { .. } => {
|
|
|
+ // Perspective: We convert a perspective function into an equivalent
|
|
|
+ // ComputedMatrix, and then decompose/interpolate/recompose these matrices.
|
|
|
+ // AccumulateMatrix/InterpolateMatrix: We do interpolation on
|
|
|
+ // AccumulateMatrix/InterpolateMatrix by reading it as a ComputedMatrix
|
|
|
+ // (with layout information), and then do matrix interpolation.
|
|
|
+ //
|
|
|
+ // Therefore, we use an identity matrix to represent the identity transform list.
|
|
|
+ // http://dev.w3.org/csswg/css-transforms/#identity-transform-function
|
|
|
+- let identity = ComputedMatrix::identity();
|
|
|
+- result.push(TransformOperation::Matrix(identity));
|
|
|
+- }
|
|
|
++ //
|
|
|
++ // FIXME(nox): This does not actually work, given the impl of
|
|
|
++ // Animate for TransformOperation bails out if the two given
|
|
|
++ // values are dissimilar.
|
|
|
++ Ok(TransformOperation::Matrix(ComputedMatrix::identity()))
|
|
|
++ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+-
|
|
|
+- result
|
|
|
+ }
|
|
|
+
|
|
|
+-/// A wrapper for calling add_weighted that interpolates the distance of the two values from
|
|
|
+-/// an initial_value and uses that to produce an interpolated value.
|
|
|
+-/// This is used for values such as 'scale' where the initial value is 1 and where if we interpolate
|
|
|
+-/// the absolute values, we will produce odd results for accumulation.
|
|
|
+-fn add_weighted_with_initial_val<T: Animatable>(a: &T,
|
|
|
+- b: &T,
|
|
|
+- a_portion: f64,
|
|
|
+- b_portion: f64,
|
|
|
+- initial_val: &T) -> Result<T, ()> {
|
|
|
+- let a = a.add_weighted(&initial_val, 1.0, -1.0)?;
|
|
|
+- let b = b.add_weighted(&initial_val, 1.0, -1.0)?;
|
|
|
+- let result = a.add_weighted(&b, a_portion, b_portion)?;
|
|
|
+- result.add_weighted(&initial_val, 1.0, 1.0)
|
|
|
++fn animate_multiplicative_factor(
|
|
|
++ this: CSSFloat,
|
|
|
++ other: CSSFloat,
|
|
|
++ procedure: Procedure,
|
|
|
++) -> Result<CSSFloat, ()> {
|
|
|
++ Ok((this - 1.).animate(&(other - 1.), procedure)? + 1.)
|
|
|
+ }
|
|
|
+
|
|
|
+-/// Add two transform lists.
|
|
|
+ /// http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
|
|
|
+-fn add_weighted_transform_lists(from_list: &[TransformOperation],
|
|
|
+- to_list: &[TransformOperation],
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64) -> TransformList {
|
|
|
+- let mut result = vec![];
|
|
|
+-
|
|
|
+- if can_interpolate_list(from_list, to_list) {
|
|
|
+- for (from, to) in from_list.iter().zip(to_list) {
|
|
|
+- match (from, to) {
|
|
|
+- (&TransformOperation::Matrix(from),
|
|
|
+- &TransformOperation::Matrix(_to)) => {
|
|
|
+- let sum = from.add_weighted(&_to, self_portion, other_portion).unwrap();
|
|
|
+- result.push(TransformOperation::Matrix(sum));
|
|
|
+- }
|
|
|
+- (&TransformOperation::MatrixWithPercents(_),
|
|
|
+- &TransformOperation::MatrixWithPercents(_)) => {
|
|
|
+- // We don't add_weighted `-moz-transform` matrices yet.
|
|
|
+- // They contain percentage values.
|
|
|
+- {}
|
|
|
+- }
|
|
|
+- (&TransformOperation::Skew(fx, fy),
|
|
|
+- &TransformOperation::Skew(tx, ty)) => {
|
|
|
+- let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap();
|
|
|
+- let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap();
|
|
|
+- result.push(TransformOperation::Skew(ix, iy));
|
|
|
+- }
|
|
|
+- (&TransformOperation::Translate(fx, fy, fz),
|
|
|
+- &TransformOperation::Translate(tx, ty, tz)) => {
|
|
|
+- let ix = fx.add_weighted(&tx, self_portion, other_portion).unwrap();
|
|
|
+- let iy = fy.add_weighted(&ty, self_portion, other_portion).unwrap();
|
|
|
+- let iz = fz.add_weighted(&tz, self_portion, other_portion).unwrap();
|
|
|
+- result.push(TransformOperation::Translate(ix, iy, iz));
|
|
|
+- }
|
|
|
+- (&TransformOperation::Scale(fx, fy, fz),
|
|
|
+- &TransformOperation::Scale(tx, ty, tz)) => {
|
|
|
+- let ix = add_weighted_with_initial_val(&fx, &tx, self_portion,
|
|
|
+- other_portion, &1.0).unwrap();
|
|
|
+- let iy = add_weighted_with_initial_val(&fy, &ty, self_portion,
|
|
|
+- other_portion, &1.0).unwrap();
|
|
|
+- let iz = add_weighted_with_initial_val(&fz, &tz, self_portion,
|
|
|
+- other_portion, &1.0).unwrap();
|
|
|
+- result.push(TransformOperation::Scale(ix, iy, iz));
|
|
|
++impl Animate for TransformOperation {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ match (self, other) {
|
|
|
++ (
|
|
|
++ &TransformOperation::Matrix(ref this),
|
|
|
++ &TransformOperation::Matrix(ref other),
|
|
|
++ ) => {
|
|
|
++ Ok(TransformOperation::Matrix(
|
|
|
++ this.animate(other, procedure)?,
|
|
|
++ ))
|
|
|
++ },
|
|
|
++ (
|
|
|
++ &TransformOperation::Skew(ref fx, ref fy),
|
|
|
++ &TransformOperation::Skew(ref tx, ref ty),
|
|
|
++ ) => {
|
|
|
++ Ok(TransformOperation::Skew(
|
|
|
++ fx.animate(tx, procedure)?,
|
|
|
++ fy.animate(ty, procedure)?,
|
|
|
++ ))
|
|
|
++ },
|
|
|
++ (
|
|
|
++ &TransformOperation::Translate(ref fx, ref fy, ref fz),
|
|
|
++ &TransformOperation::Translate(ref tx, ref ty, ref tz),
|
|
|
++ ) => {
|
|
|
++ Ok(TransformOperation::Translate(
|
|
|
++ fx.animate(tx, procedure)?,
|
|
|
++ fy.animate(ty, procedure)?,
|
|
|
++ fz.animate(tz, procedure)?,
|
|
|
++ ))
|
|
|
++ },
|
|
|
++ (
|
|
|
++ &TransformOperation::Scale(ref fx, ref fy, ref fz),
|
|
|
++ &TransformOperation::Scale(ref tx, ref ty, ref tz),
|
|
|
++ ) => {
|
|
|
++ Ok(TransformOperation::Scale(
|
|
|
++ animate_multiplicative_factor(*fx, *tx, procedure)?,
|
|
|
++ animate_multiplicative_factor(*fy, *ty, procedure)?,
|
|
|
++ animate_multiplicative_factor(*fz, *tz, procedure)?,
|
|
|
++ ))
|
|
|
++ },
|
|
|
++ (
|
|
|
++ &TransformOperation::Rotate(fx, fy, fz, fa),
|
|
|
++ &TransformOperation::Rotate(tx, ty, tz, ta),
|
|
|
++ ) => {
|
|
|
++ let (fx, fy, fz, fa) = get_normalized_vector_and_angle(fx, fy, fz, fa);
|
|
|
++ let (tx, ty, tz, ta) = get_normalized_vector_and_angle(tx, ty, tz, ta);
|
|
|
++ if (fx, fy, fz) == (tx, ty, tz) {
|
|
|
++ let ia = fa.animate(&ta, procedure)?;
|
|
|
++ Ok(TransformOperation::Rotate(fx, fy, fz, ia))
|
|
|
++ } else {
|
|
|
++ let matrix_f = rotate_to_matrix(fx, fy, fz, fa);
|
|
|
++ let matrix_t = rotate_to_matrix(tx, ty, tz, ta);
|
|
|
++ Ok(TransformOperation::Matrix(
|
|
|
++ matrix_f.animate(&matrix_t, procedure)?,
|
|
|
++ ))
|
|
|
+ }
|
|
|
+- (&TransformOperation::Rotate(fx, fy, fz, fa),
|
|
|
+- &TransformOperation::Rotate(tx, ty, tz, ta)) => {
|
|
|
+- let (fx, fy, fz, fa) = get_normalized_vector_and_angle(fx, fy, fz, fa);
|
|
|
+- let (tx, ty, tz, ta) = get_normalized_vector_and_angle(tx, ty, tz, ta);
|
|
|
+- if (fx, fy, fz) == (tx, ty, tz) {
|
|
|
+- let ia = fa.add_weighted(&ta, self_portion, other_portion).unwrap();
|
|
|
+- result.push(TransformOperation::Rotate(fx, fy, fz, ia));
|
|
|
+- } else {
|
|
|
+- let matrix_f = rotate_to_matrix(fx, fy, fz, fa);
|
|
|
+- let matrix_t = rotate_to_matrix(tx, ty, tz, ta);
|
|
|
+- let sum = matrix_f.add_weighted(&matrix_t, self_portion, other_portion)
|
|
|
+- .unwrap();
|
|
|
+-
|
|
|
+- result.push(TransformOperation::Matrix(sum));
|
|
|
+- }
|
|
|
++ },
|
|
|
++ (
|
|
|
++ &TransformOperation::Perspective(ref fd),
|
|
|
++ &TransformOperation::Perspective(ref td),
|
|
|
++ ) => {
|
|
|
++ let mut fd_matrix = ComputedMatrix::identity();
|
|
|
++ let mut td_matrix = ComputedMatrix::identity();
|
|
|
++ if fd.0 > 0 {
|
|
|
++ fd_matrix.m34 = -1. / fd.to_f32_px();
|
|
|
+ }
|
|
|
+- (&TransformOperation::Perspective(fd),
|
|
|
+- &TransformOperation::Perspective(td)) => {
|
|
|
+- let mut fd_matrix = ComputedMatrix::identity();
|
|
|
+- let mut td_matrix = ComputedMatrix::identity();
|
|
|
+- if fd.0 > 0 {
|
|
|
+- fd_matrix.m34 = -1. / fd.to_f32_px();
|
|
|
+- }
|
|
|
+-
|
|
|
+- if td.0 > 0 {
|
|
|
+- td_matrix.m34 = -1. / td.to_f32_px();
|
|
|
+- }
|
|
|
+-
|
|
|
+- let sum = fd_matrix.add_weighted(&td_matrix, self_portion, other_portion)
|
|
|
+- .unwrap();
|
|
|
+- result.push(TransformOperation::Matrix(sum));
|
|
|
++ if td.0 > 0 {
|
|
|
++ td_matrix.m34 = -1. / td.to_f32_px();
|
|
|
+ }
|
|
|
+- _ => {
|
|
|
+- // This should be unreachable due to the can_interpolate_list() call.
|
|
|
+- unreachable!();
|
|
|
+- }
|
|
|
+- }
|
|
|
++ Ok(TransformOperation::Matrix(
|
|
|
++ fd_matrix.animate(&td_matrix, procedure)?,
|
|
|
++ ))
|
|
|
++ },
|
|
|
++ _ => Err(()),
|
|
|
+ }
|
|
|
+- } else {
|
|
|
+- let from_transform_list = TransformList(Some(from_list.to_vec()));
|
|
|
+- let to_transform_list = TransformList(Some(to_list.to_vec()));
|
|
|
+- result.push(
|
|
|
+- TransformOperation::InterpolateMatrix { from_list: from_transform_list,
|
|
|
+- to_list: to_transform_list,
|
|
|
+- progress: Percentage(other_portion as f32) });
|
|
|
+ }
|
|
|
+-
|
|
|
+- TransformList(Some(result))
|
|
|
+ }
|
|
|
+
|
|
|
+ /// https://www.w3.org/TR/css-transforms-1/#Rotate3dDefined
|
|
|
+ fn rotate_to_matrix(x: f32, y: f32, z: f32, a: Angle) -> ComputedMatrix {
|
|
|
+ let half_rad = a.radians() / 2.0;
|
|
|
+ let sc = (half_rad).sin() * (half_rad).cos();
|
|
|
+ let sq = (half_rad).sin().powi(2);
|
|
|
+
|
|
|
+@@ -1535,50 +1371,48 @@ pub struct MatrixDecomposed2D {
|
|
|
+ /// The scale function.
|
|
|
+ pub scale: Scale2D,
|
|
|
+ /// The rotation angle.
|
|
|
+ pub angle: f32,
|
|
|
+ /// The inner matrix.
|
|
|
+ pub matrix: InnerMatrix2D,
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for InnerMatrix2D {
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++impl Animate for InnerMatrix2D {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ Ok(InnerMatrix2D {
|
|
|
+- m11: add_weighted_with_initial_val(&self.m11, &other.m11,
|
|
|
+- self_portion, other_portion, &1.0)?,
|
|
|
+- m12: self.m12.add_weighted(&other.m12, self_portion, other_portion)?,
|
|
|
+- m21: self.m21.add_weighted(&other.m21, self_portion, other_portion)?,
|
|
|
+- m22: add_weighted_with_initial_val(&self.m22, &other.m22,
|
|
|
+- self_portion, other_portion, &1.0)?,
|
|
|
++ m11: animate_multiplicative_factor(self.m11, other.m11, procedure)?,
|
|
|
++ m12: self.m12.animate(&other.m12, procedure)?,
|
|
|
++ m21: self.m21.animate(&other.m21, procedure)?,
|
|
|
++ m22: animate_multiplicative_factor(self.m22, other.m22, procedure)?,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for Translate2D {
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++impl Animate for Translate2D {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ Ok(Translate2D(
|
|
|
+- self.0.add_weighted(&other.0, self_portion, other_portion)?,
|
|
|
+- self.1.add_weighted(&other.1, self_portion, other_portion)?,
|
|
|
++ self.0.animate(&other.0, procedure)?,
|
|
|
++ self.1.animate(&other.1, procedure)?,
|
|
|
+ ))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for Scale2D {
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++impl Animate for Scale2D {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ Ok(Scale2D(
|
|
|
+- add_weighted_with_initial_val(&self.0, &other.0, self_portion, other_portion, &1.0)?,
|
|
|
+- add_weighted_with_initial_val(&self.1, &other.1, self_portion, other_portion, &1.0)?,
|
|
|
++ animate_multiplicative_factor(self.0, other.0, procedure)?,
|
|
|
++ animate_multiplicative_factor(self.1, other.1, procedure)?,
|
|
|
+ ))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for MatrixDecomposed2D {
|
|
|
++impl Animate for MatrixDecomposed2D {
|
|
|
+ /// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-2d-matrix-values
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ // If x-axis of one is flipped, and y-axis of the other,
|
|
|
+ // convert to an unflipped rotation.
|
|
|
+ let mut scale = self.scale;
|
|
|
+ let mut angle = self.angle;
|
|
|
+ let mut other_angle = other.angle;
|
|
|
+ if (scale.0 < 0.0 && other.scale.1 < 0.0) || (scale.1 < 0.0 && other.scale.0 < 0.0) {
|
|
|
+ scale.0 = -scale.0;
|
|
|
+ scale.1 = -scale.1;
|
|
|
+@@ -1598,20 +1432,20 @@ impl Animatable for MatrixDecomposed2D {
|
|
|
+ angle -= 360.
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ other_angle -= 360.
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Interpolate all values.
|
|
|
+- let translate = self.translate.add_weighted(&other.translate, self_portion, other_portion)?;
|
|
|
+- let scale = scale.add_weighted(&other.scale, self_portion, other_portion)?;
|
|
|
+- let angle = angle.add_weighted(&other_angle, self_portion, other_portion)?;
|
|
|
+- let matrix = self.matrix.add_weighted(&other.matrix, self_portion, other_portion)?;
|
|
|
++ let translate = self.translate.animate(&other.translate, procedure)?;
|
|
|
++ let scale = scale.animate(&other.scale, procedure)?;
|
|
|
++ let angle = angle.animate(&other_angle, procedure)?;
|
|
|
++ let matrix = self.matrix.animate(&other.matrix, procedure)?;
|
|
|
+
|
|
|
+ Ok(MatrixDecomposed2D {
|
|
|
+ translate: translate,
|
|
|
+ scale: scale,
|
|
|
+ angle: angle,
|
|
|
+ matrix: matrix,
|
|
|
+ })
|
|
|
+ }
|
|
|
+@@ -1626,36 +1460,35 @@ impl ComputeSquaredDistance for MatrixDe
|
|
|
+ let angle2 = other.angle as f64 * RAD_PER_DEG;
|
|
|
+ Ok(self.translate.compute_squared_distance(&other.translate)? +
|
|
|
+ self.scale.compute_squared_distance(&other.scale)? +
|
|
|
+ angle1.compute_squared_distance(&angle2)? +
|
|
|
+ self.matrix.compute_squared_distance(&other.matrix)?)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for ComputedMatrix {
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++impl Animate for ComputedMatrix {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ if self.is_3d() || other.is_3d() {
|
|
|
+ let decomposed_from = decompose_3d_matrix(*self);
|
|
|
+ let decomposed_to = decompose_3d_matrix(*other);
|
|
|
+ match (decomposed_from, decomposed_to) {
|
|
|
+- (Ok(from), Ok(to)) => {
|
|
|
+- let sum = from.add_weighted(&to, self_portion, other_portion)?;
|
|
|
+- Ok(ComputedMatrix::from(sum))
|
|
|
++ (Ok(this), Ok(other)) => {
|
|
|
++ Ok(ComputedMatrix::from(this.animate(&other, procedure)?))
|
|
|
+ },
|
|
|
+ _ => {
|
|
|
+- let result = if self_portion > other_portion {*self} else {*other};
|
|
|
++ let (this_weight, other_weight) = procedure.weights();
|
|
|
++ let result = if this_weight > other_weight { *self } else { *other };
|
|
|
+ Ok(result)
|
|
|
+- }
|
|
|
++ },
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+- let decomposed_from = MatrixDecomposed2D::from(*self);
|
|
|
+- let decomposed_to = MatrixDecomposed2D::from(*other);
|
|
|
+- let sum = decomposed_from.add_weighted(&decomposed_to, self_portion, other_portion)?;
|
|
|
+- Ok(ComputedMatrix::from(sum))
|
|
|
++ let this = MatrixDecomposed2D::from(*self);
|
|
|
++ let other = MatrixDecomposed2D::from(*other);
|
|
|
++ Ok(ComputedMatrix::from(this.animate(&other, procedure)?))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for ComputedMatrix {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+ if self.is_3d() || other.is_3d() {
|
|
|
+@@ -2082,103 +1915,104 @@ fn dot(a: [f32; 3], b: [f32; 3]) -> f32
|
|
|
+ fn cross(row1: [f32; 3], row2: [f32; 3]) -> [f32; 3] {
|
|
|
+ [
|
|
|
+ row1[1] * row2[2] - row1[2] * row2[1],
|
|
|
+ row1[2] * row2[0] - row1[0] * row2[2],
|
|
|
+ row1[0] * row2[1] - row1[1] * row2[0]
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for Translate3D {
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++impl Animate for Translate3D {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ Ok(Translate3D(
|
|
|
+- self.0.add_weighted(&other.0, self_portion, other_portion)?,
|
|
|
+- self.1.add_weighted(&other.1, self_portion, other_portion)?,
|
|
|
+- self.2.add_weighted(&other.2, self_portion, other_portion)?,
|
|
|
++ self.0.animate(&other.0, procedure)?,
|
|
|
++ self.1.animate(&other.1, procedure)?,
|
|
|
++ self.2.animate(&other.2, procedure)?,
|
|
|
+ ))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for Scale3D {
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++impl Animate for Scale3D {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ Ok(Scale3D(
|
|
|
+- add_weighted_with_initial_val(&self.0, &other.0, self_portion, other_portion, &1.0)?,
|
|
|
+- add_weighted_with_initial_val(&self.1, &other.1, self_portion, other_portion, &1.0)?,
|
|
|
+- add_weighted_with_initial_val(&self.2, &other.2, self_portion, other_portion, &1.0)?,
|
|
|
++ animate_multiplicative_factor(self.0, other.0, procedure)?,
|
|
|
++ animate_multiplicative_factor(self.1, other.1, procedure)?,
|
|
|
++ animate_multiplicative_factor(self.2, other.2, procedure)?,
|
|
|
+ ))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for Skew {
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++impl Animate for Skew {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ Ok(Skew(
|
|
|
+- self.0.add_weighted(&other.0, self_portion, other_portion)?,
|
|
|
+- self.1.add_weighted(&other.1, self_portion, other_portion)?,
|
|
|
+- self.2.add_weighted(&other.2, self_portion, other_portion)?,
|
|
|
++ self.0.animate(&other.0, procedure)?,
|
|
|
++ self.1.animate(&other.1, procedure)?,
|
|
|
++ self.2.animate(&other.2, procedure)?,
|
|
|
+ ))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for Skew {
|
|
|
+ // We have to use atan() to convert the skew factors into skew angles, so implement
|
|
|
+ // ComputeSquaredDistance manually.
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+ Ok(self.0.atan().compute_squared_distance(&other.0.atan())? +
|
|
|
+ self.1.atan().compute_squared_distance(&other.1.atan())? +
|
|
|
+ self.2.atan().compute_squared_distance(&other.2.atan())?)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for Perspective {
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++impl Animate for Perspective {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ Ok(Perspective(
|
|
|
+- self.0.add_weighted(&other.0, self_portion, other_portion)?,
|
|
|
+- self.1.add_weighted(&other.1, self_portion, other_portion)?,
|
|
|
+- self.2.add_weighted(&other.2, self_portion, other_portion)?,
|
|
|
+- add_weighted_with_initial_val(&self.3, &other.3, self_portion, other_portion, &1.0)?,
|
|
|
++ self.0.animate(&other.0, procedure)?,
|
|
|
++ self.1.animate(&other.1, procedure)?,
|
|
|
++ self.2.animate(&other.2, procedure)?,
|
|
|
++ animate_multiplicative_factor(self.3, other.3, procedure)?,
|
|
|
+ ))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for MatrixDecomposed3D {
|
|
|
++impl Animate for MatrixDecomposed3D {
|
|
|
+ /// https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-3d-matrix-values
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
|
|
+- -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ use std::f64;
|
|
|
+
|
|
|
+- debug_assert!((self_portion + other_portion - 1.0f64).abs() <= f64::EPSILON ||
|
|
|
+- other_portion == 1.0f64 || other_portion == 0.0f64,
|
|
|
+- "add_weighted should only be used for interpolating or accumulating transforms");
|
|
|
++ let (this_weight, other_weight) = procedure.weights();
|
|
|
++
|
|
|
++ debug_assert!((this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON ||
|
|
|
++ other_weight == 1.0f64 || other_weight == 0.0f64,
|
|
|
++ "animate should only be used for interpolating or accumulating transforms");
|
|
|
+
|
|
|
+ let mut sum = *self;
|
|
|
+
|
|
|
+ // Add translate, scale, skew and perspective components.
|
|
|
+- sum.translate = self.translate.add_weighted(&other.translate, self_portion, other_portion)?;
|
|
|
+- sum.scale = self.scale.add_weighted(&other.scale, self_portion, other_portion)?;
|
|
|
+- sum.skew = self.skew.add_weighted(&other.skew, self_portion, other_portion)?;
|
|
|
+- sum.perspective = self.perspective.add_weighted(&other.perspective, self_portion, other_portion)?;
|
|
|
++ sum.translate = self.translate.animate(&other.translate, procedure)?;
|
|
|
++ sum.scale = self.scale.animate(&other.scale, procedure)?;
|
|
|
++ sum.skew = self.skew.animate(&other.skew, procedure)?;
|
|
|
++ sum.perspective = self.perspective.animate(&other.perspective, procedure)?;
|
|
|
+
|
|
|
+ // Add quaternions using spherical linear interpolation (Slerp).
|
|
|
+ //
|
|
|
+- // We take a specialized code path for accumulation (where other_portion is 1)
|
|
|
+- if other_portion == 1.0 {
|
|
|
+- if self_portion == 0.0 {
|
|
|
++ // We take a specialized code path for accumulation (where other_weight is 1)
|
|
|
++ if other_weight == 1.0 {
|
|
|
++ if this_weight == 0.0 {
|
|
|
+ return Ok(*other)
|
|
|
+ }
|
|
|
+
|
|
|
+ let clamped_w = self.quaternion.3.min(1.0).max(-1.0);
|
|
|
+
|
|
|
+ // Determine the scale factor.
|
|
|
+ let mut theta = clamped_w.acos();
|
|
|
+ let mut scale = if theta == 0.0 { 0.0 } else { 1.0 / theta.sin() };
|
|
|
+- theta *= self_portion;
|
|
|
++ theta *= this_weight;
|
|
|
+ scale *= theta.sin();
|
|
|
+
|
|
|
+- // Scale the self matrix by self_portion.
|
|
|
++ // Scale the self matrix by this_weight.
|
|
|
+ let mut scaled_self = *self;
|
|
|
+ % for i in range(3):
|
|
|
+ scaled_self.quaternion.${i} *= scale;
|
|
|
+ % endfor
|
|
|
+ scaled_self.quaternion.3 = theta.cos();
|
|
|
+
|
|
|
+ // Multiply scaled-self by other.
|
|
|
+ let a = &scaled_self.quaternion;
|
|
|
+@@ -2199,22 +2033,22 @@ impl Animatable for MatrixDecomposed3D {
|
|
|
+ product = product.min(1.0);
|
|
|
+ product = product.max(-1.0);
|
|
|
+
|
|
|
+ if product == 1.0 {
|
|
|
+ return Ok(sum);
|
|
|
+ }
|
|
|
+
|
|
|
+ let theta = product.acos();
|
|
|
+- let w = (other_portion * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
|
|
|
++ let w = (other_weight * theta).sin() * 1.0 / (1.0 - product * product).sqrt();
|
|
|
+
|
|
|
+ let mut a = *self;
|
|
|
+ let mut b = *other;
|
|
|
+ % for i in range(4):
|
|
|
+- a.quaternion.${i} *= (other_portion * theta).cos() - product * w;
|
|
|
++ a.quaternion.${i} *= (other_weight * theta).cos() - product * w;
|
|
|
+ b.quaternion.${i} *= w;
|
|
|
+ sum.quaternion.${i} = a.quaternion.${i} + b.quaternion.${i};
|
|
|
+ % endfor
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(sum)
|
|
|
+ }
|
|
|
+ }
|
|
|
+@@ -2412,233 +2246,242 @@ impl ComputedMatrix {
|
|
|
+ self.m12*self.m21*self.m33 + self.m11*self.m22*self.m33),
|
|
|
+ };
|
|
|
+
|
|
|
+ Some(x)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// https://drafts.csswg.org/css-transforms/#interpolation-of-transforms
|
|
|
+-impl Animatable for TransformList {
|
|
|
++impl Animate for TransformList {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &TransformList, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- // http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms
|
|
|
+- let result = match (&self.0, &other.0) {
|
|
|
+- (&Some(ref from_list), &Some(ref to_list)) => {
|
|
|
+- // Two lists of transforms
|
|
|
+- add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion)
|
|
|
+- }
|
|
|
+- (&Some(ref from_list), &None) => {
|
|
|
+- // http://dev.w3.org/csswg/css-transforms/#none-transform-animation
|
|
|
+- let to_list = build_identity_transform_list(from_list);
|
|
|
+- add_weighted_transform_lists(from_list, &to_list, self_portion, other_portion)
|
|
|
+- }
|
|
|
+- (&None, &Some(ref to_list)) => {
|
|
|
+- // http://dev.w3.org/csswg/css-transforms/#none-transform-animation
|
|
|
+- let from_list = build_identity_transform_list(to_list);
|
|
|
+- add_weighted_transform_lists(&from_list, to_list, self_portion, other_portion)
|
|
|
+- }
|
|
|
+- _ => {
|
|
|
+- // http://dev.w3.org/csswg/css-transforms/#none-none-animation
|
|
|
+- TransformList(None)
|
|
|
+- }
|
|
|
++ fn animate(
|
|
|
++ &self,
|
|
|
++ other: &Self,
|
|
|
++ procedure: Procedure,
|
|
|
++ ) -> Result<Self, ()> {
|
|
|
++ if self.0.is_none() && other.0.is_none() {
|
|
|
++ return Ok(TransformList(None));
|
|
|
++ }
|
|
|
++
|
|
|
++ if procedure == Procedure::Add {
|
|
|
++ let this = self.0.as_ref().map_or(&[][..], |l| l);
|
|
|
++ let other = other.0.as_ref().map_or(&[][..], |l| l);
|
|
|
++ let result = this.iter().chain(other).cloned().collect::<Vec<_>>();
|
|
|
++ return Ok(TransformList(if result.is_empty() {
|
|
|
++ None
|
|
|
++ } else {
|
|
|
++ Some(result)
|
|
|
++ }));
|
|
|
++ }
|
|
|
++
|
|
|
++ let this = if self.0.is_some() {
|
|
|
++ Cow::Borrowed(self)
|
|
|
++ } else {
|
|
|
++ Cow::Owned(other.to_animated_zero()?)
|
|
|
++ };
|
|
|
++ let other = if other.0.is_some() {
|
|
|
++ Cow::Borrowed(other)
|
|
|
++ } else {
|
|
|
++ Cow::Owned(self.to_animated_zero()?)
|
|
|
+ };
|
|
|
+
|
|
|
+- Ok(result)
|
|
|
+- }
|
|
|
+-
|
|
|
+- fn add(&self, other: &Self) -> Result<Self, ()> {
|
|
|
+- match (&self.0, &other.0) {
|
|
|
+- (&Some(ref from_list), &Some(ref to_list)) => {
|
|
|
+- Ok(TransformList(Some([&from_list[..], &to_list[..]].concat())))
|
|
|
+- }
|
|
|
+- (&Some(_), &None) => {
|
|
|
+- Ok(self.clone())
|
|
|
+- }
|
|
|
+- (&None, &Some(_)) => {
|
|
|
+- Ok(other.clone())
|
|
|
+- }
|
|
|
+- _ => {
|
|
|
+- Ok(TransformList(None))
|
|
|
++ {
|
|
|
++ let this = (*this).0.as_ref().map_or(&[][..], |l| l);
|
|
|
++ let other = (*other).0.as_ref().map_or(&[][..], |l| l);
|
|
|
++ if this.len() == other.len() {
|
|
|
++ let result = this.iter().zip(other).map(|(this, other)| {
|
|
|
++ this.animate(other, procedure)
|
|
|
++ }).collect::<Result<Vec<_>, _>>();
|
|
|
++ if let Ok(list) = result {
|
|
|
++ return Ok(TransformList(if list.is_empty() {
|
|
|
++ None
|
|
|
++ } else {
|
|
|
++ Some(list)
|
|
|
++ }));
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+- }
|
|
|
+
|
|
|
+- #[inline]
|
|
|
+- fn accumulate(&self, other: &Self, count: u64) -> Result<Self, ()> {
|
|
|
+- match (&self.0, &other.0) {
|
|
|
+- (&Some(ref from_list), &Some(ref to_list)) => {
|
|
|
+- if can_interpolate_list(from_list, to_list) {
|
|
|
+- Ok(add_weighted_transform_lists(from_list, &to_list, count as f64, 1.0))
|
|
|
+- } else {
|
|
|
+- use std::i32;
|
|
|
+- let result = vec![TransformOperation::AccumulateMatrix {
|
|
|
+- from_list: self.clone(),
|
|
|
+- to_list: other.clone(),
|
|
|
+- count: cmp::min(count, i32::MAX as u64) as i32
|
|
|
+- }];
|
|
|
+- Ok(TransformList(Some(result)))
|
|
|
+- }
|
|
|
+- }
|
|
|
+- (&Some(ref from_list), &None) => {
|
|
|
+- Ok(add_weighted_transform_lists(from_list, from_list, count as f64, 0.0))
|
|
|
+- }
|
|
|
+- (&None, &Some(_)) => {
|
|
|
+- // If |self| is 'none' then we are calculating:
|
|
|
+- //
|
|
|
+- // none * |count| + |other|
|
|
|
+- // = none + |other|
|
|
|
+- // = |other|
|
|
|
+- //
|
|
|
+- // Hence the result is just |other|.
|
|
|
+- Ok(other.clone())
|
|
|
+- }
|
|
|
+- _ => {
|
|
|
+- Ok(TransformList(None))
|
|
|
+- }
|
|
|
++ match procedure {
|
|
|
++ Procedure::Add => Err(()),
|
|
|
++ Procedure::Interpolate { progress } => {
|
|
|
++ Ok(TransformList(Some(vec![TransformOperation::InterpolateMatrix {
|
|
|
++ from_list: this.into_owned(),
|
|
|
++ to_list: other.into_owned(),
|
|
|
++ progress: Percentage(progress as f32),
|
|
|
++ }])))
|
|
|
++ },
|
|
|
++ Procedure::Accumulate { count } => {
|
|
|
++ Ok(TransformList(Some(vec![TransformOperation::AccumulateMatrix {
|
|
|
++ from_list: this.into_owned(),
|
|
|
++ to_list: other.into_owned(),
|
|
|
++ count: cmp::min(count, i32::max_value() as u64) as i32,
|
|
|
++ }])))
|
|
|
++ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-/// A helper function to retrieve the pixel length and percentage value.
|
|
|
+-fn extract_pixel_calc_value(lop: &LengthOrPercentage) -> (f64, CSSFloat) {
|
|
|
+- match lop {
|
|
|
+- &LengthOrPercentage::Length(au) => (au.to_f64_px(), 0.),
|
|
|
+- &LengthOrPercentage::Percentage(percent) => (0., percent.0),
|
|
|
+- &LengthOrPercentage::Calc(calc) => (calc.length().to_f64_px(), calc.percentage())
|
|
|
+- }
|
|
|
+-}
|
|
|
+-
|
|
|
+-/// Compute the squared distance of two transform lists.
|
|
|
+ // This might not be the most useful definition of distance. It might be better, for example,
|
|
|
+ // to trace the distance travelled by a point as its transform is interpolated between the two
|
|
|
+ // lists. That, however, proves to be quite complicated so we take a simple approach for now.
|
|
|
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1318591#c0.
|
|
|
+-fn compute_transform_lists_squared_distance(from_list: &[TransformOperation],
|
|
|
+- to_list: &[TransformOperation])
|
|
|
+- -> Result<SquaredDistance, ()> {
|
|
|
+- let zero_distance = SquaredDistance::Value(0.);
|
|
|
+- let squared_distance = from_list.iter().zip(to_list.iter()).map(|(from, to)| {
|
|
|
+- match (from, to) {
|
|
|
+- (&TransformOperation::Matrix(from),
|
|
|
+- &TransformOperation::Matrix(to)) => {
|
|
|
+- from.compute_squared_distance(&to).unwrap_or(zero_distance)
|
|
|
+- }
|
|
|
+- (&TransformOperation::Skew(fx, fy),
|
|
|
+- &TransformOperation::Skew(tx, ty)) => {
|
|
|
+- fx.compute_squared_distance(&tx).unwrap_or(zero_distance) +
|
|
|
+- fy.compute_squared_distance(&ty).unwrap_or(zero_distance)
|
|
|
+- }
|
|
|
+- (&TransformOperation::Translate(fx, fy, fz),
|
|
|
+- &TransformOperation::Translate(tx, ty, tz)) => {
|
|
|
++impl ComputeSquaredDistance for TransformOperation {
|
|
|
++ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
++ match (self, other) {
|
|
|
++ (
|
|
|
++ &TransformOperation::Matrix(ref this),
|
|
|
++ &TransformOperation::Matrix(ref other),
|
|
|
++ ) => {
|
|
|
++ this.compute_squared_distance(other)
|
|
|
++ },
|
|
|
++ (
|
|
|
++ &TransformOperation::Skew(ref fx, ref fy),
|
|
|
++ &TransformOperation::Skew(ref tx, ref ty),
|
|
|
++ ) => {
|
|
|
++ Ok(
|
|
|
++ fx.compute_squared_distance(&tx)? +
|
|
|
++ fy.compute_squared_distance(&ty)?,
|
|
|
++ )
|
|
|
++ },
|
|
|
++ (
|
|
|
++ &TransformOperation::Translate(ref fx, ref fy, ref fz),
|
|
|
++ &TransformOperation::Translate(ref tx, ref ty, ref tz),
|
|
|
++ ) => {
|
|
|
+ // We don't want to require doing layout in order to calculate the result, so
|
|
|
+ // drop the percentage part. However, dropping percentage makes us impossible to
|
|
|
+ // compute the distance for the percentage-percentage case, but Gecko uses the
|
|
|
+ // same formula, so it's fine for now.
|
|
|
+ // Note: We use pixel value to compute the distance for translate, so we have to
|
|
|
+ // convert Au into px.
|
|
|
+- let diff_x = fx.add_weighted(&tx, 1., -1.).unwrap_or(LengthOrPercentage::zero());
|
|
|
+- let diff_y = fy.add_weighted(&ty, 1., -1.).unwrap_or(LengthOrPercentage::zero());
|
|
|
+- let (diff_x_length, _) = extract_pixel_calc_value(&diff_x);
|
|
|
+- let (diff_y_length, _) = extract_pixel_calc_value(&diff_y);
|
|
|
+- SquaredDistance::Value(diff_x_length * diff_x_length) +
|
|
|
+- SquaredDistance::Value(diff_y_length * diff_y_length) +
|
|
|
+- fz.to_f64_px().compute_squared_distance(&tz.to_f64_px()).unwrap_or(zero_distance)
|
|
|
+- }
|
|
|
+- (&TransformOperation::Scale(fx, fy, fz),
|
|
|
+- &TransformOperation::Scale(tx, ty, tz)) => {
|
|
|
+- fx.compute_squared_distance(&tx).unwrap_or(zero_distance) +
|
|
|
+- fy.compute_squared_distance(&ty).unwrap_or(zero_distance) +
|
|
|
+- fz.compute_squared_distance(&tz).unwrap_or(zero_distance)
|
|
|
+- }
|
|
|
+- (&TransformOperation::Rotate(fx, fy, fz, fa),
|
|
|
+- &TransformOperation::Rotate(tx, ty, tz, ta)) => {
|
|
|
++ let extract_pixel_length = |lop: &LengthOrPercentage| {
|
|
|
++ match *lop {
|
|
|
++ LengthOrPercentage::Length(au) => au.to_f64_px(),
|
|
|
++ LengthOrPercentage::Percentage(_) => 0.,
|
|
|
++ LengthOrPercentage::Calc(calc) => calc.length().to_f64_px(),
|
|
|
++ }
|
|
|
++ };
|
|
|
++
|
|
|
++ let fx = extract_pixel_length(&fx);
|
|
|
++ let fy = extract_pixel_length(&fy);
|
|
|
++ let tx = extract_pixel_length(&tx);
|
|
|
++ let ty = extract_pixel_length(&ty);
|
|
|
++
|
|
|
++ Ok(
|
|
|
++ fx.compute_squared_distance(&tx)? +
|
|
|
++ fy.compute_squared_distance(&ty)? +
|
|
|
++ fz.to_f64_px().compute_squared_distance(&tz.to_f64_px())?,
|
|
|
++ )
|
|
|
++ },
|
|
|
++ (
|
|
|
++ &TransformOperation::Scale(ref fx, ref fy, ref fz),
|
|
|
++ &TransformOperation::Scale(ref tx, ref ty, ref tz),
|
|
|
++ ) => {
|
|
|
++ Ok(
|
|
|
++ fx.compute_squared_distance(&tx)? +
|
|
|
++ fy.compute_squared_distance(&ty)? +
|
|
|
++ fz.compute_squared_distance(&tz)?,
|
|
|
++ )
|
|
|
++ },
|
|
|
++ (
|
|
|
++ &TransformOperation::Rotate(fx, fy, fz, fa),
|
|
|
++ &TransformOperation::Rotate(tx, ty, tz, ta),
|
|
|
++ ) => {
|
|
|
+ let (fx, fy, fz, angle1) = get_normalized_vector_and_angle(fx, fy, fz, fa);
|
|
|
+ let (tx, ty, tz, angle2) = get_normalized_vector_and_angle(tx, ty, tz, ta);
|
|
|
+ if (fx, fy, fz) == (tx, ty, tz) {
|
|
|
+- angle1.compute_squared_distance(&angle2).unwrap_or(zero_distance)
|
|
|
++ angle1.compute_squared_distance(&angle2)
|
|
|
+ } else {
|
|
|
+ let v1 = DirectionVector::new(fx, fy, fz);
|
|
|
+ let v2 = DirectionVector::new(tx, ty, tz);
|
|
|
+ let q1 = Quaternion::from_direction_and_angle(&v1, angle1.radians64());
|
|
|
+ let q2 = Quaternion::from_direction_and_angle(&v2, angle2.radians64());
|
|
|
+- q1.compute_squared_distance(&q2).unwrap_or(zero_distance)
|
|
|
++ q1.compute_squared_distance(&q2)
|
|
|
+ }
|
|
|
+ }
|
|
|
+- (&TransformOperation::Perspective(fd),
|
|
|
+- &TransformOperation::Perspective(td)) => {
|
|
|
++ (
|
|
|
++ &TransformOperation::Perspective(ref fd),
|
|
|
++ &TransformOperation::Perspective(ref td),
|
|
|
++ ) => {
|
|
|
+ let mut fd_matrix = ComputedMatrix::identity();
|
|
|
+ let mut td_matrix = ComputedMatrix::identity();
|
|
|
+ if fd.0 > 0 {
|
|
|
+ fd_matrix.m34 = -1. / fd.to_f32_px();
|
|
|
+ }
|
|
|
+
|
|
|
+ if td.0 > 0 {
|
|
|
+ td_matrix.m34 = -1. / td.to_f32_px();
|
|
|
+ }
|
|
|
+- fd_matrix.compute_squared_distance(&td_matrix).unwrap_or(zero_distance)
|
|
|
++ fd_matrix.compute_squared_distance(&td_matrix)
|
|
|
+ }
|
|
|
+- (&TransformOperation::Perspective(p), &TransformOperation::Matrix(m)) |
|
|
|
+- (&TransformOperation::Matrix(m), &TransformOperation::Perspective(p)) => {
|
|
|
++ (
|
|
|
++ &TransformOperation::Perspective(ref p),
|
|
|
++ &TransformOperation::Matrix(ref m),
|
|
|
++ ) | (
|
|
|
++ &TransformOperation::Matrix(ref m),
|
|
|
++ &TransformOperation::Perspective(ref p),
|
|
|
++ ) => {
|
|
|
+ let mut p_matrix = ComputedMatrix::identity();
|
|
|
+ if p.0 > 0 {
|
|
|
+ p_matrix.m34 = -1. / p.to_f32_px();
|
|
|
+ }
|
|
|
+- p_matrix.compute_squared_distance(&m).unwrap_or(zero_distance)
|
|
|
++ p_matrix.compute_squared_distance(&m)
|
|
|
+ }
|
|
|
+- _ => {
|
|
|
+- // We don't support computation of distance for InterpolateMatrix and
|
|
|
+- // AccumulateMatrix.
|
|
|
+- zero_distance
|
|
|
+- }
|
|
|
++ _ => Err(()),
|
|
|
+ }
|
|
|
+- }).sum();
|
|
|
+-
|
|
|
+- Ok(squared_distance)
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for TransformList {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+- match (self.0.as_ref(), other.0.as_ref()) {
|
|
|
+- (Some(from_list), Some(to_list)) => {
|
|
|
+- if can_interpolate_list(from_list, to_list) {
|
|
|
+- compute_transform_lists_squared_distance(from_list, to_list)
|
|
|
+- } else {
|
|
|
+- // Bug 1390039: we don't handle mismatch transform lists for now.
|
|
|
+- Err(())
|
|
|
+- }
|
|
|
+- },
|
|
|
+- (Some(list), None) | (None, Some(list)) => {
|
|
|
+- let none = build_identity_transform_list(list);
|
|
|
+- compute_transform_lists_squared_distance(list, &none)
|
|
|
++ let this = self.0.as_ref().map_or(&[][..], |l| l);
|
|
|
++ let other = other.0.as_ref().map_or(&[][..], |l| l);
|
|
|
++
|
|
|
++ this.iter().zip_longest(other).map(|it| {
|
|
|
++ match it {
|
|
|
++ EitherOrBoth::Both(this, other) => {
|
|
|
++ this.compute_squared_distance(other)
|
|
|
++ },
|
|
|
++ EitherOrBoth::Left(list) | EitherOrBoth::Right(list) => {
|
|
|
++ list.to_animated_zero()?.compute_squared_distance(list)
|
|
|
++ },
|
|
|
+ }
|
|
|
+- _ => Ok(SquaredDistance::Value(0.))
|
|
|
+- }
|
|
|
++ }).sum()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for TransformList {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+- Ok(TransformList(None))
|
|
|
++ match self.0 {
|
|
|
++ None => Ok(TransformList(None)),
|
|
|
++ Some(ref list) => {
|
|
|
++ Ok(TransformList(Some(
|
|
|
++ list.iter().map(|op| op.to_animated_zero()).collect::<Result<Vec<_>, _>>()?
|
|
|
++ )))
|
|
|
++ },
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<T, U> Animatable for Either<T, U>
|
|
|
+- where T: Animatable + Copy, U: Animatable + Copy,
|
|
|
++impl<T, U> Animate for Either<T, U>
|
|
|
++where
|
|
|
++ T: Animate,
|
|
|
++ U: Animate,
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- match (*self, *other) {
|
|
|
+- (Either::First(ref this), Either::First(ref other)) => {
|
|
|
+- this.add_weighted(&other, self_portion, other_portion).map(Either::First)
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ match (self, other) {
|
|
|
++ (&Either::First(ref this), &Either::First(ref other)) => {
|
|
|
++ Ok(Either::First(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+- (Either::Second(ref this), Either::Second(ref other)) => {
|
|
|
+- this.add_weighted(&other, self_portion, other_portion).map(Either::Second)
|
|
|
++ (&Either::Second(ref this), &Either::Second(ref other)) => {
|
|
|
++ Ok(Either::Second(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<A, B> ToAnimatedZero for Either<A, B>
|
|
|
+ where
|
|
|
+@@ -2653,35 +2496,36 @@ where
|
|
|
+ },
|
|
|
+ Either::Second(ref second) => {
|
|
|
+ Ok(Either::Second(second.to_animated_zero()?))
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-/// Animatable SVGPaint
|
|
|
++/// Animated SVGPaint
|
|
|
+ pub type IntermediateSVGPaint = SVGPaint<AnimatedRGBA, ComputedUrl>;
|
|
|
+
|
|
|
+-/// Animatable SVGPaintKind
|
|
|
++/// Animated SVGPaintKind
|
|
|
+ pub type IntermediateSVGPaintKind = SVGPaintKind<AnimatedRGBA, ComputedUrl>;
|
|
|
+
|
|
|
+-impl Animatable for IntermediateSVGPaint {
|
|
|
++impl Animate for IntermediateSVGPaint {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ Ok(IntermediateSVGPaint {
|
|
|
+- kind: self.kind.add_weighted(&other.kind, self_portion, other_portion)?,
|
|
|
+- fallback: self.fallback.add_weighted(&other.fallback, self_portion, other_portion)?,
|
|
|
++ kind: self.kind.animate(&other.kind, procedure)?,
|
|
|
++ fallback: self.fallback.animate(&other.fallback, procedure)?,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for IntermediateSVGPaint {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
++ // FIXME(nox): This should be derived.
|
|
|
+ Ok(
|
|
|
+ self.kind.compute_squared_distance(&other.kind)? +
|
|
|
+ self.fallback.compute_squared_distance(&other.fallback)?,
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for IntermediateSVGPaint {
|
|
|
+@@ -2689,28 +2533,30 @@ impl ToAnimatedZero for IntermediateSVGP
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+ Ok(IntermediateSVGPaint {
|
|
|
+ kind: self.kind.to_animated_zero()?,
|
|
|
+ fallback: self.fallback.and_then(|v| v.to_animated_zero().ok()),
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for IntermediateSVGPaintKind {
|
|
|
++impl Animate for IntermediateSVGPaintKind {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ match (self, other) {
|
|
|
+- (&SVGPaintKind::Color(ref self_color), &SVGPaintKind::Color(ref other_color)) => {
|
|
|
+- Ok(SVGPaintKind::Color(self_color.add_weighted(other_color, self_portion, other_portion)?))
|
|
|
+- }
|
|
|
+- // FIXME context values should be interpolable with colors
|
|
|
+- // Gecko doesn't implement this behavior either.
|
|
|
++ (&SVGPaintKind::Color(ref this), &SVGPaintKind::Color(ref other)) => {
|
|
|
++ Ok(SVGPaintKind::Color(this.animate(other, procedure)?))
|
|
|
++ },
|
|
|
+ (&SVGPaintKind::ContextFill, &SVGPaintKind::ContextFill) => Ok(SVGPaintKind::ContextFill),
|
|
|
+ (&SVGPaintKind::ContextStroke, &SVGPaintKind::ContextStroke) => Ok(SVGPaintKind::ContextStroke),
|
|
|
+- _ => Err(())
|
|
|
++ _ => {
|
|
|
++ // FIXME: Context values should be interpolable with colors,
|
|
|
++ // Gecko doesn't implement this behavior either.
|
|
|
++ Err(())
|
|
|
++ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for IntermediateSVGPaintKind {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+ match (self, other) {
|
|
|
+@@ -2798,152 +2644,171 @@ fn convert_from_number_or_percentage<Len
|
|
|
+ NumberOrPercentage::Number(num) =>
|
|
|
+ SvgLengthOrPercentageOrNumber::Number(num.into()),
|
|
|
+ NumberOrPercentage::Percentage(p) =>
|
|
|
+ SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
|
|
+ (LengthOrPercentage::Percentage(p)).into())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl <LengthOrPercentageType, NumberType> Animatable for
|
|
|
+- SvgLengthOrPercentageOrNumber<LengthOrPercentageType, NumberType>
|
|
|
+- where LengthOrPercentageType: Animatable + Into<NumberOrPercentage> + From<LengthOrPercentage> + Copy,
|
|
|
+- NumberType: Animatable + Into<NumberOrPercentage> + From<Number>,
|
|
|
+- SvgLengthOrPercentageOrNumber<LengthOrPercentageType, NumberType>: Copy,
|
|
|
+- LengthOrPercentage: From<LengthOrPercentageType>
|
|
|
++impl <L, N> Animate for SvgLengthOrPercentageOrNumber<L, N>
|
|
|
++where
|
|
|
++ L: Animate + From<LengthOrPercentage> + Into<NumberOrPercentage> + Copy,
|
|
|
++ N: Animate + From<Number> + Into<NumberOrPercentage>,
|
|
|
++ LengthOrPercentage: From<L>,
|
|
|
++ Self: Copy,
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ if self.has_calc() || other.has_calc() {
|
|
|
+ // TODO: We need to treat calc value.
|
|
|
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1386967
|
|
|
+ return Err(());
|
|
|
+ }
|
|
|
+
|
|
|
+- let from_value = convert_to_number_or_percentage(*self);
|
|
|
+- let to_value = convert_to_number_or_percentage(*other);
|
|
|
++ let this = convert_to_number_or_percentage(*self);
|
|
|
++ let other = convert_to_number_or_percentage(*other);
|
|
|
+
|
|
|
+- match (from_value, to_value) {
|
|
|
+- (NumberOrPercentage::Number(from),
|
|
|
+- NumberOrPercentage::Number(to)) => {
|
|
|
+- from.add_weighted(&to, self_portion, other_portion)
|
|
|
+- .map(|num| NumberOrPercentage::Number(num))
|
|
|
+- .map(|nop| convert_from_number_or_percentage(nop))
|
|
|
++ match (this, other) {
|
|
|
++ (
|
|
|
++ NumberOrPercentage::Number(ref this),
|
|
|
++ NumberOrPercentage::Number(ref other),
|
|
|
++ ) => {
|
|
|
++ Ok(convert_from_number_or_percentage(
|
|
|
++ NumberOrPercentage::Number(this.animate(other, procedure)?)
|
|
|
++ ))
|
|
|
+ },
|
|
|
+- (NumberOrPercentage::Percentage(from),
|
|
|
+- NumberOrPercentage::Percentage(to)) => {
|
|
|
+- from.add_weighted(&to, self_portion, other_portion)
|
|
|
+- .map(|p| NumberOrPercentage::Percentage(p))
|
|
|
+- .map(|nop| convert_from_number_or_percentage(nop))
|
|
|
++ (
|
|
|
++ NumberOrPercentage::Percentage(ref this),
|
|
|
++ NumberOrPercentage::Percentage(ref other),
|
|
|
++ ) => {
|
|
|
++ Ok(convert_from_number_or_percentage(
|
|
|
++ NumberOrPercentage::Percentage(this.animate(other, procedure)?)
|
|
|
++ ))
|
|
|
+ },
|
|
|
+ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl <LengthOrPercentageType, NumberType> ToAnimatedZero for
|
|
|
+- SvgLengthOrPercentageOrNumber<LengthOrPercentageType, NumberType>
|
|
|
+- where LengthOrPercentageType: ToAnimatedZero, NumberType: ToAnimatedZero
|
|
|
++impl<L, N> ToAnimatedZero for SvgLengthOrPercentageOrNumber<L, N>
|
|
|
++where
|
|
|
++ L: ToAnimatedZero,
|
|
|
++ N: ToAnimatedZero,
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+- match self {
|
|
|
+- &SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref lop) =>
|
|
|
+- lop.to_animated_zero()
|
|
|
+- .map(SvgLengthOrPercentageOrNumber::LengthOrPercentage),
|
|
|
+- &SvgLengthOrPercentageOrNumber::Number(ref num) =>
|
|
|
+- num.to_animated_zero()
|
|
|
+- .map(SvgLengthOrPercentageOrNumber::Number),
|
|
|
++ match *self {
|
|
|
++ SvgLengthOrPercentageOrNumber::LengthOrPercentage(ref lop) => {
|
|
|
++ Ok(SvgLengthOrPercentageOrNumber::LengthOrPercentage(
|
|
|
++ lop.to_animated_zero()?,
|
|
|
++ ))
|
|
|
++ },
|
|
|
++ SvgLengthOrPercentageOrNumber::Number(ref num) => {
|
|
|
++ Ok(SvgLengthOrPercentageOrNumber::Number(
|
|
|
++ num.to_animated_zero()?,
|
|
|
++ ))
|
|
|
++ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<LengthType> Animatable for SVGLength<LengthType>
|
|
|
+- where LengthType: Animatable + Clone
|
|
|
++impl<L> Animate for SVGLength<L>
|
|
|
++where
|
|
|
++ L: Animate + Clone,
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ match (self, other) {
|
|
|
+ (&SVGLength::Length(ref this), &SVGLength::Length(ref other)) => {
|
|
|
+- this.add_weighted(&other, self_portion, other_portion).map(SVGLength::Length)
|
|
|
+- }
|
|
|
++ Ok(SVGLength::Length(this.animate(other, procedure)?))
|
|
|
++ },
|
|
|
+ _ => {
|
|
|
+- Ok(if self_portion > other_portion { self.clone() } else { other.clone() })
|
|
|
+- }
|
|
|
++ // FIXME(nox): Is this correct for addition and accumulation?
|
|
|
++ // I think an error should be returned if it's not
|
|
|
++ // an interpolation.
|
|
|
++ let (this_weight, other_weight) = procedure.weights();
|
|
|
++ Ok(if this_weight > other_weight { self.clone() } else { other.clone() })
|
|
|
++ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<LengthType> ToAnimatedZero for SVGLength<LengthType> where LengthType : ToAnimatedZero {
|
|
|
++impl<L> ToAnimatedZero for SVGLength<L>
|
|
|
++where
|
|
|
++ L: ToAnimatedZero,
|
|
|
++{
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+- match self {
|
|
|
+- &SVGLength::Length(ref length) => length.to_animated_zero().map(SVGLength::Length),
|
|
|
+- &SVGLength::ContextValue => Ok(SVGLength::ContextValue),
|
|
|
++ match *self {
|
|
|
++ SVGLength::Length(ref length) => {
|
|
|
++ Ok(SVGLength::Length(length.to_animated_zero()?))
|
|
|
++ },
|
|
|
++ SVGLength::ContextValue => Ok(SVGLength::ContextValue),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty
|
|
|
+-impl<LengthType> Animatable for SVGStrokeDashArray<LengthType>
|
|
|
+- where LengthType : RepeatableListAnimatable + Clone
|
|
|
++impl<L> Animate for SVGStrokeDashArray<L>
|
|
|
++where
|
|
|
++ L: Clone + RepeatableListAnimatable,
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- match (self, other) {
|
|
|
+- (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other))=> {
|
|
|
+- this.add_weighted(other, self_portion, other_portion)
|
|
|
+- .map(SVGStrokeDashArray::Values)
|
|
|
+- }
|
|
|
+- _ => {
|
|
|
+- Ok(if self_portion > other_portion { self.clone() } else { other.clone() })
|
|
|
+- }
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ if matches!(procedure, Procedure::Add | Procedure::Accumulate { .. }) {
|
|
|
++ // Non-additive.
|
|
|
++ return Err(());
|
|
|
+ }
|
|
|
+- }
|
|
|
+-
|
|
|
+- /// stroke-dasharray is non-additive
|
|
|
+- #[inline]
|
|
|
+- fn add(&self, _other: &Self) -> Result<Self, ()> {
|
|
|
+- Err(())
|
|
|
+- }
|
|
|
+-
|
|
|
+- /// stroke-dasharray is non-additive
|
|
|
+- #[inline]
|
|
|
+- fn accumulate(&self, _other: &Self, _count: u64) -> Result<Self, ()> {
|
|
|
+- Err(())
|
|
|
+- }
|
|
|
+-}
|
|
|
+-
|
|
|
+-impl<LengthType> ToAnimatedZero for SVGStrokeDashArray<LengthType>
|
|
|
+- where LengthType : ToAnimatedZero + Clone
|
|
|
+-{
|
|
|
+- #[inline]
|
|
|
+- fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+- match self {
|
|
|
+- &SVGStrokeDashArray::Values(ref values) => {
|
|
|
+- values.iter().map(ToAnimatedZero::to_animated_zero)
|
|
|
+- .collect::<Result<Vec<_>, ()>>().map(SVGStrokeDashArray::Values)
|
|
|
+- }
|
|
|
+- &SVGStrokeDashArray::ContextValue => Ok(SVGStrokeDashArray::ContextValue),
|
|
|
++ match (self, other) {
|
|
|
++ (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => {
|
|
|
++ Ok(SVGStrokeDashArray::Values(this.animate(other, procedure)?))
|
|
|
++ },
|
|
|
++ _ => {
|
|
|
++ let (this_weight, other_weight) = procedure.weights();
|
|
|
++ Ok(if this_weight > other_weight { self.clone() } else { other.clone() })
|
|
|
++ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<OpacityType> Animatable for SVGOpacity<OpacityType>
|
|
|
+- where OpacityType: Animatable + Clone
|
|
|
++impl<L> ToAnimatedZero for SVGStrokeDashArray<L>
|
|
|
++where
|
|
|
++ L: Clone + ToAnimatedZero
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
++ match *self {
|
|
|
++ SVGStrokeDashArray::Values(ref values) => {
|
|
|
++ Ok(SVGStrokeDashArray::Values(
|
|
|
++ values.iter().map(ToAnimatedZero::to_animated_zero).collect::<Result<Vec<_>, _>>()?,
|
|
|
++ ))
|
|
|
++ }
|
|
|
++ SVGStrokeDashArray::ContextValue => Ok(SVGStrokeDashArray::ContextValue),
|
|
|
++ }
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++impl<O> Animate for SVGOpacity<O>
|
|
|
++where
|
|
|
++ O: Animate + Clone,
|
|
|
++{
|
|
|
++ #[inline]
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ match (self, other) {
|
|
|
+ (&SVGOpacity::Opacity(ref this), &SVGOpacity::Opacity(ref other)) => {
|
|
|
+- this.add_weighted(other, self_portion, other_portion).map(SVGOpacity::Opacity)
|
|
|
+- }
|
|
|
++ Ok(SVGOpacity::Opacity(this.animate(other, procedure)?))
|
|
|
++ },
|
|
|
+ _ => {
|
|
|
+- Ok(if self_portion > other_portion { self.clone() } else { other.clone() })
|
|
|
+- }
|
|
|
++ // FIXME(nox): Is this correct for addition and accumulation?
|
|
|
++ // I think an error should be returned if it's not
|
|
|
++ // an interpolation.
|
|
|
++ let (this_weight, other_weight) = procedure.weights();
|
|
|
++ Ok(if this_weight > other_weight { self.clone() } else { other.clone() })
|
|
|
++ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<OpacityType> ToAnimatedZero for SVGOpacity<OpacityType>
|
|
|
+ where OpacityType: ToAnimatedZero + Clone
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+@@ -2958,155 +2823,123 @@ impl<OpacityType> ToAnimatedZero for SVG
|
|
|
+
|
|
|
+ <%
|
|
|
+ FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale',
|
|
|
+ 'HueRotate', 'Invert', 'Opacity', 'Saturate',
|
|
|
+ 'Sepia' ]
|
|
|
+ %>
|
|
|
+
|
|
|
+ /// https://drafts.fxtf.org/filters/#animation-of-filters
|
|
|
+-fn add_weighted_filter_function_impl(from: &AnimatedFilter,
|
|
|
+- to: &AnimatedFilter,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64)
|
|
|
+- -> Result<AnimatedFilter, ()> {
|
|
|
+- match (from, to) {
|
|
|
+- % for func in [ 'Blur', 'HueRotate' ]:
|
|
|
+- (&Filter::${func}(from_value), &Filter::${func}(to_value)) => {
|
|
|
+- Ok(Filter::${func}(from_value.add_weighted(
|
|
|
+- &to_value,
|
|
|
+- self_portion,
|
|
|
+- other_portion,
|
|
|
+- )?))
|
|
|
+- },
|
|
|
+- % endfor
|
|
|
+- % for func in [ 'Grayscale', 'Invert', 'Sepia' ]:
|
|
|
+- (&Filter::${func}(from_value), &Filter::${func}(to_value)) => {
|
|
|
+- Ok(Filter::${func}(add_weighted_with_initial_val(
|
|
|
+- &from_value,
|
|
|
+- &to_value,
|
|
|
+- self_portion,
|
|
|
+- other_portion,
|
|
|
+- &NonNegative::<CSSFloat>(0.0),
|
|
|
+- )?))
|
|
|
++impl Animate for AnimatedFilter {
|
|
|
++ fn animate(
|
|
|
++ &self,
|
|
|
++ other: &Self,
|
|
|
++ procedure: Procedure,
|
|
|
++ ) -> Result<Self, ()> {
|
|
|
++ match (self, other) {
|
|
|
++ % for func in ['Blur', 'Grayscale', 'HueRotate', 'Invert', 'Sepia']:
|
|
|
++ (&Filter::${func}(ref this), &Filter::${func}(ref other)) => {
|
|
|
++ Ok(Filter::${func}(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+- % endfor
|
|
|
+- % for func in [ 'Brightness', 'Contrast', 'Opacity', 'Saturate' ]:
|
|
|
+- (&Filter::${func}(from_value), &Filter::${func}(to_value)) => {
|
|
|
+- Ok(Filter::${func}(add_weighted_with_initial_val(
|
|
|
+- &from_value,
|
|
|
+- &to_value,
|
|
|
+- self_portion,
|
|
|
+- other_portion,
|
|
|
+- &NonNegative::<CSSFloat>(1.0),
|
|
|
+- )?))
|
|
|
+- },
|
|
|
+- % endfor
|
|
|
+- % if product == "gecko":
|
|
|
+- (&Filter::DropShadow(ref from_value), &Filter::DropShadow(ref to_value)) => {
|
|
|
+- Ok(Filter::DropShadow(from_value.add_weighted(
|
|
|
+- &to_value,
|
|
|
+- self_portion,
|
|
|
+- other_portion,
|
|
|
+- )?))
|
|
|
+- },
|
|
|
+- (&Filter::Url(_), &Filter::Url(_)) => {
|
|
|
+- Err(())
|
|
|
+- },
|
|
|
+- % endif
|
|
|
+- _ => {
|
|
|
+- // If specified the different filter functions,
|
|
|
+- // we will need to interpolate as discreate.
|
|
|
+- Err(())
|
|
|
+- },
|
|
|
+- }
|
|
|
+-}
|
|
|
+-
|
|
|
+-/// https://drafts.fxtf.org/filters/#animation-of-filters
|
|
|
+-fn add_weighted_filter_function(from: Option<<&AnimatedFilter>,
|
|
|
+- to: Option<<&AnimatedFilter>,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64) -> Result<AnimatedFilter, ()> {
|
|
|
+- match (from, to) {
|
|
|
+- (Some(f), Some(t)) => {
|
|
|
+- add_weighted_filter_function_impl(f, t, self_portion, other_portion)
|
|
|
+- },
|
|
|
+- (Some(f), None) => {
|
|
|
+- add_weighted_filter_function_impl(f, f, self_portion, 0.0)
|
|
|
+- },
|
|
|
+- (None, Some(t)) => {
|
|
|
+- add_weighted_filter_function_impl(t, t, other_portion, 0.0)
|
|
|
+- },
|
|
|
+- _ => { Err(()) }
|
|
|
+- }
|
|
|
+-}
|
|
|
+-
|
|
|
+-fn compute_filter_square_distance(from: &AnimatedFilter, to: &AnimatedFilter) -> Result<SquaredDistance, ()> {
|
|
|
+- match (from, to) {
|
|
|
+- % for func in FILTER_FUNCTIONS :
|
|
|
+- (&Filter::${func}(f),
|
|
|
+- &Filter::${func}(t)) => {
|
|
|
+- Ok(try!(f.compute_squared_distance(&t)))
|
|
|
++ % endfor
|
|
|
++ % for func in ['Brightness', 'Contrast', 'Opacity', 'Saturate']:
|
|
|
++ (&Filter::${func}(ref this), &Filter::${func}(ref other)) => {
|
|
|
++ Ok(Filter::${func}(NonNegative(animate_multiplicative_factor(
|
|
|
++ this.0,
|
|
|
++ other.0,
|
|
|
++ procedure,
|
|
|
++ )?)))
|
|
|
+ },
|
|
|
+- % endfor
|
|
|
+- % if product == "gecko":
|
|
|
+- (&Filter::DropShadow(ref f), &Filter::DropShadow(ref t)) => {
|
|
|
+- Ok(try!(f.compute_squared_distance(&t)))
|
|
|
++ % endfor
|
|
|
++ % if product == "gecko":
|
|
|
++ (&Filter::DropShadow(ref this), &Filter::DropShadow(ref other)) => {
|
|
|
++ Ok(Filter::DropShadow(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+- % endif
|
|
|
+- _ => {
|
|
|
+- Err(())
|
|
|
++ % endif
|
|
|
++ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for AnimatedFilterList {
|
|
|
+- #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self,
|
|
|
+- self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- let mut filters = vec![];
|
|
|
+- let mut from_iter = self.0.iter();
|
|
|
+- let mut to_iter = other.0.iter();
|
|
|
++/// http://dev.w3.org/csswg/css-transforms/#none-transform-animation
|
|
|
++impl ToAnimatedZero for AnimatedFilter {
|
|
|
++ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
++ match *self {
|
|
|
++ % for func in ['Blur', 'Grayscale', 'HueRotate', 'Invert', 'Sepia']:
|
|
|
++ Filter::${func}(ref this) => Ok(Filter::${func}(this.to_animated_zero()?)),
|
|
|
++ % endfor
|
|
|
++ % for func in ['Brightness', 'Contrast', 'Opacity', 'Saturate']:
|
|
|
++ Filter::${func}(_) => Ok(Filter::${func}(NonNegative(1.))),
|
|
|
++ % endfor
|
|
|
++ % if product == "gecko":
|
|
|
++ Filter::DropShadow(ref this) => Ok(Filter::DropShadow(this.to_animated_zero()?)),
|
|
|
++ % endif
|
|
|
++ _ => Err(()),
|
|
|
++ }
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
+
|
|
|
+- let mut from = from_iter.next();
|
|
|
+- let mut to = to_iter.next();
|
|
|
+- while from.is_some() || to.is_some() {
|
|
|
+- filters.push(try!(add_weighted_filter_function(from,
|
|
|
+- to,
|
|
|
+- self_portion,
|
|
|
+- other_portion)));
|
|
|
+- if from.is_some() {
|
|
|
+- from = from_iter.next();
|
|
|
++// FIXME(nox): This should be derived.
|
|
|
++impl ComputeSquaredDistance for AnimatedFilter {
|
|
|
++ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
++ match (self, other) {
|
|
|
++ % for func in FILTER_FUNCTIONS:
|
|
|
++ (&Filter::${func}(ref this), &Filter::${func}(ref other)) => {
|
|
|
++ this.compute_squared_distance(other)
|
|
|
++ },
|
|
|
++ % endfor
|
|
|
++ % if product == "gecko":
|
|
|
++ (&Filter::DropShadow(ref this), &Filter::DropShadow(ref other)) => {
|
|
|
++ this.compute_squared_distance(other)
|
|
|
++ },
|
|
|
++ % endif
|
|
|
++ _ => Err(()),
|
|
|
++ }
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++impl Animate for AnimatedFilterList {
|
|
|
++ #[inline]
|
|
|
++ fn animate(
|
|
|
++ &self,
|
|
|
++ other: &Self,
|
|
|
++ procedure: Procedure,
|
|
|
++ ) -> Result<Self, ()> {
|
|
|
++ if procedure == Procedure::Add {
|
|
|
++ return Ok(AnimatedFilterList(
|
|
|
++ self.0.iter().chain(other.0.iter()).cloned().collect(),
|
|
|
++ ));
|
|
|
++ }
|
|
|
++ Ok(AnimatedFilterList(self.0.iter().zip_longest(other.0.iter()).map(|it| {
|
|
|
++ match it {
|
|
|
++ EitherOrBoth::Both(this, other) => {
|
|
|
++ this.animate(other, procedure)
|
|
|
++ },
|
|
|
++ EitherOrBoth::Left(this) => {
|
|
|
++ this.animate(&this.to_animated_zero()?, procedure)
|
|
|
++ },
|
|
|
++ EitherOrBoth::Right(other) => {
|
|
|
++ other.to_animated_zero()?.animate(other, procedure)
|
|
|
++ },
|
|
|
+ }
|
|
|
+- if to.is_some() {
|
|
|
+- to = to_iter.next();
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- Ok(AnimatedFilterList(filters))
|
|
|
+- }
|
|
|
+-
|
|
|
+- fn add(&self, other: &Self) -> Result<Self, ()> {
|
|
|
+- Ok(AnimatedFilterList(self.0.iter().chain(other.0.iter()).cloned().collect()))
|
|
|
++ }).collect::<Result<Vec<_>, _>>()?))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for AnimatedFilterList {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+- use itertools::{EitherOrBoth, Itertools};
|
|
|
+-
|
|
|
+ self.0.iter().zip_longest(other.0.iter()).map(|it| {
|
|
|
+ match it {
|
|
|
+- EitherOrBoth::Both(from, to) => {
|
|
|
+- compute_filter_square_distance(&from, &to)
|
|
|
++ EitherOrBoth::Both(this, other) => {
|
|
|
++ this.compute_squared_distance(other)
|
|
|
+ },
|
|
|
+- EitherOrBoth::Left(list) | EitherOrBoth::Right(list)=> {
|
|
|
+- let none = add_weighted_filter_function(Some(list), Some(list), 0.0, 0.0)?;
|
|
|
+- compute_filter_square_distance(&none, &list)
|
|
|
++ EitherOrBoth::Left(list) | EitherOrBoth::Right(list) => {
|
|
|
++ list.to_animated_zero()?.compute_squared_distance(list)
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }).sum()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// A comparator to sort PropertyIds such that longhands are sorted before shorthands,
|
|
|
+ /// shorthands with fewer components are sorted before shorthands with more components,
|
|
|
+@@ -3156,40 +2989,42 @@ sorted_shorthands = [(p, position) for p
|
|
|
+ %>
|
|
|
+ match *shorthand {
|
|
|
+ % for property, position in sorted_shorthands:
|
|
|
+ ShorthandId::${property.camel_case} => ${position},
|
|
|
+ % endfor
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<T> Animatable for NonNegative<T>
|
|
|
+- where T: Animatable + Clone
|
|
|
++impl<T> Animate for NonNegative<T>
|
|
|
++where
|
|
|
++ T: Animate,
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- self.0.add_weighted(&other.0, self_portion, other_portion).map(NonNegative::<T>)
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(NonNegative(self.0.animate(&other.0, procedure)?))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<T> ToAnimatedZero for NonNegative<T>
|
|
|
+ where T: ToAnimatedZero
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+ self.0.to_animated_zero().map(NonNegative::<T>)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<T> Animatable for GreaterThanOrEqualToOne<T>
|
|
|
+- where T: Animatable + Clone
|
|
|
++impl<T> Animate for GreaterThanOrEqualToOne<T>
|
|
|
++where
|
|
|
++ T: Animate,
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- self.0.add_weighted(&other.0, self_portion, other_portion).map(GreaterThanOrEqualToOne::<T>)
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(GreaterThanOrEqualToOne(self.0.animate(&other.0, procedure)?))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<T> ToAnimatedZero for GreaterThanOrEqualToOne<T>
|
|
|
+ where T: ToAnimatedZero
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+diff --git a/servo/components/style/properties/longhand/font.mako.rs b/servo/components/style/properties/longhand/font.mako.rs
|
|
|
+--- a/servo/components/style/properties/longhand/font.mako.rs
|
|
|
++++ b/servo/components/style/properties/longhand/font.mako.rs
|
|
|
+@@ -1110,19 +1110,18 @@ macro_rules! impl_gecko_keyword_conversi
|
|
|
+ Some(s)
|
|
|
+ } else {
|
|
|
+ None
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pub mod computed_value {
|
|
|
+- use properties::animated_properties::Animatable;
|
|
|
+ use values::CSSFloat;
|
|
|
+- use values::animated::{ToAnimatedValue, ToAnimatedZero};
|
|
|
++ use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
|
|
|
+ use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
|
|
+
|
|
|
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
|
+ #[derive(Copy, Clone, Debug, PartialEq, ToCss)]
|
|
|
+ pub enum T {
|
|
|
+ None,
|
|
|
+ Number(CSSFloat),
|
|
|
+ }
|
|
|
+@@ -1132,22 +1131,22 @@ macro_rules! impl_gecko_keyword_conversi
|
|
|
+ if gecko == -1.0 {
|
|
|
+ T::None
|
|
|
+ } else {
|
|
|
+ T::Number(gecko)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+- impl Animatable for T {
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
|
|
+- -> Result<Self, ()> {
|
|
|
+- match (*self, *other) {
|
|
|
+- (T::Number(ref number), T::Number(ref other)) =>
|
|
|
+- Ok(T::Number(number.add_weighted(other, self_portion, other_portion)?)),
|
|
|
++ impl Animate for T {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ match (self, other) {
|
|
|
++ (&T::Number(ref number), &T::Number(ref other)) => {
|
|
|
++ Ok(T::Number(number.animate(other, procedure)?))
|
|
|
++ },
|
|
|
+ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for T {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+diff --git a/servo/components/style/properties/longhand/inherited_table.mako.rs b/servo/components/style/properties/longhand/inherited_table.mako.rs
|
|
|
+--- a/servo/components/style/properties/longhand/inherited_table.mako.rs
|
|
|
++++ b/servo/components/style/properties/longhand/inherited_table.mako.rs
|
|
|
+@@ -21,37 +21,33 @@
|
|
|
+ spec="https://drafts.csswg.org/css-tables/#propdef-caption-side")}
|
|
|
+
|
|
|
+ <%helpers:longhand name="border-spacing" animation_value_type="BorderSpacing" boxed="True"
|
|
|
+ spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing">
|
|
|
+ use values::specified::{AllowQuirks, Length};
|
|
|
+ use values::specified::length::NonNegativeLength;
|
|
|
+
|
|
|
+ pub mod computed_value {
|
|
|
+- use properties::animated_properties::Animatable;
|
|
|
+- use values::animated::{ToAnimatedValue, ToAnimatedZero};
|
|
|
++ use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
|
|
|
+ use values::computed::NonNegativeAu;
|
|
|
+
|
|
|
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
|
+ #[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq, ToCss)]
|
|
|
+ pub struct T {
|
|
|
+ pub horizontal: NonNegativeAu,
|
|
|
+ pub vertical: NonNegativeAu,
|
|
|
+ }
|
|
|
+
|
|
|
+ /// https://drafts.csswg.org/css-transitions/#animtype-simple-list
|
|
|
+- impl Animatable for T {
|
|
|
++ impl Animate for T {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64)
|
|
|
+- -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ Ok(T {
|
|
|
+- horizontal: self.horizontal.add_weighted(&other.horizontal,
|
|
|
+- self_portion, other_portion)?,
|
|
|
+- vertical: self.vertical.add_weighted(&other.vertical,
|
|
|
+- self_portion, other_portion)?,
|
|
|
++ horizontal: self.horizontal.animate(&other.horizontal, procedure)?,
|
|
|
++ vertical: self.vertical.animate(&other.vertical, procedure)?,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for T {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
|
|
|
+ }
|
|
|
+diff --git a/servo/components/style/values/animated/color.rs b/servo/components/style/values/animated/color.rs
|
|
|
+--- a/servo/components/style/values/animated/color.rs
|
|
|
++++ b/servo/components/style/values/animated/color.rs
|
|
|
+@@ -1,16 +1,15 @@
|
|
|
+ /* 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/. */
|
|
|
+
|
|
|
+ //! Animated types for CSS colors.
|
|
|
+
|
|
|
+-use properties::animated_properties::Animatable;
|
|
|
+-use values::animated::ToAnimatedZero;
|
|
|
++use values::animated::{Animate, Procedure, ToAnimatedZero};
|
|
|
+ use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
|
|
+
|
|
|
+ /// An animated RGBA color.
|
|
|
+ ///
|
|
|
+ /// Unlike in computed values, each component value may exceed the
|
|
|
+ /// range `[0.0, 1.0]`.
|
|
|
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
|
+ #[derive(Clone, Copy, Debug, PartialEq)]
|
|
|
+@@ -34,39 +33,33 @@ impl RGBA {
|
|
|
+
|
|
|
+ /// Returns a new color.
|
|
|
+ #[inline]
|
|
|
+ pub fn new(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
|
|
|
+ RGBA { red: red, green: green, blue: blue, alpha: alpha }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-/// Unlike Animatable for computed colors, we don't clamp any component values.
|
|
|
++/// Unlike Animate for computed colors, we don't clamp any component values.
|
|
|
+ ///
|
|
|
+-/// FIXME(nox): Why do computed colors even implement Animatable?
|
|
|
+-impl Animatable for RGBA {
|
|
|
++/// FIXME(nox): Why do computed colors even implement Animate?
|
|
|
++impl Animate for RGBA {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- let mut alpha = self.alpha.add_weighted(&other.alpha, self_portion, other_portion)?;
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ let mut alpha = self.alpha.animate(&other.alpha, procedure)?;
|
|
|
+ if alpha <= 0. {
|
|
|
+ // Ideally we should return color value that only alpha component is
|
|
|
+ // 0, but this is what current gecko does.
|
|
|
+ return Ok(RGBA::transparent());
|
|
|
+ }
|
|
|
+
|
|
|
+ alpha = alpha.min(1.);
|
|
|
+- let red = (self.red * self.alpha).add_weighted(
|
|
|
+- &(other.red * other.alpha), self_portion, other_portion
|
|
|
+- )? * 1. / alpha;
|
|
|
+- let green = (self.green * self.alpha).add_weighted(
|
|
|
+- &(other.green * other.alpha), self_portion, other_portion
|
|
|
+- )? * 1. / alpha;
|
|
|
+- let blue = (self.blue * self.alpha).add_weighted(
|
|
|
+- &(other.blue * other.alpha), self_portion, other_portion
|
|
|
+- )? * 1. / alpha;
|
|
|
++ let red = (self.red * self.alpha).animate(&(other.red * other.alpha), procedure)? * 1. / alpha;
|
|
|
++ let green = (self.green * self.alpha).animate(&(other.green * other.alpha), procedure)? * 1. / alpha;
|
|
|
++ let blue = (self.blue * self.alpha).animate(&(other.blue * other.alpha), procedure)? * 1. / alpha;
|
|
|
+
|
|
|
+ Ok(RGBA::new(red, green, blue, alpha))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for RGBA {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+@@ -118,71 +111,71 @@ impl Color {
|
|
|
+ fn effective_intermediate_rgba(&self) -> RGBA {
|
|
|
+ RGBA {
|
|
|
+ alpha: self.color.alpha * (1. - self.foreground_ratio),
|
|
|
+ .. self.color
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for Color {
|
|
|
++impl Animate for Color {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ // Common cases are interpolating between two numeric colors,
|
|
|
+ // two currentcolors, and a numeric color and a currentcolor.
|
|
|
+ //
|
|
|
+ // Note: this algorithm assumes self_portion + other_portion
|
|
|
+ // equals to one, so it may be broken for additive operation.
|
|
|
+ // To properly support additive color interpolation, we would
|
|
|
+ // need two ratio fields in computed color types.
|
|
|
++ let (this_weight, other_weight) = procedure.weights();
|
|
|
+ if self.foreground_ratio == other.foreground_ratio {
|
|
|
+ if self.is_currentcolor() {
|
|
|
+ Ok(Color::currentcolor())
|
|
|
+ } else {
|
|
|
+ Ok(Color {
|
|
|
+- color: self.color.add_weighted(&other.color, self_portion, other_portion)?,
|
|
|
++ color: self.color.animate(&other.color, procedure)?,
|
|
|
+ foreground_ratio: self.foreground_ratio,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } else if self.is_currentcolor() && other.is_numeric() {
|
|
|
+ Ok(Color {
|
|
|
+ color: other.color,
|
|
|
+- foreground_ratio: self_portion as f32,
|
|
|
++ foreground_ratio: this_weight as f32,
|
|
|
+ })
|
|
|
+ } else if self.is_numeric() && other.is_currentcolor() {
|
|
|
+ Ok(Color {
|
|
|
+ color: self.color,
|
|
|
+- foreground_ratio: other_portion as f32,
|
|
|
++ foreground_ratio: other_weight as f32,
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // For interpolating between two complex colors, we need to
|
|
|
+ // generate colors with effective alpha value.
|
|
|
+ let self_color = self.effective_intermediate_rgba();
|
|
|
+ let other_color = other.effective_intermediate_rgba();
|
|
|
+- let color = self_color.add_weighted(&other_color, self_portion, other_portion)?;
|
|
|
++ let color = self_color.animate(&other_color, procedure)?;
|
|
|
+ // Then we compute the final foreground ratio, and derive
|
|
|
+ // the final alpha value from the effective alpha value.
|
|
|
+- let foreground_ratio = self.foreground_ratio
|
|
|
+- .add_weighted(&other.foreground_ratio, self_portion, other_portion)?;
|
|
|
++ let foreground_ratio = self.foreground_ratio.animate(&other.foreground_ratio, procedure)?;
|
|
|
+ let alpha = color.alpha / (1. - foreground_ratio);
|
|
|
+ Ok(Color {
|
|
|
+ color: RGBA {
|
|
|
+ alpha: alpha,
|
|
|
+ .. color
|
|
|
+ },
|
|
|
+ foreground_ratio: foreground_ratio,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for Color {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+- // All comments in add_weighted also applies here.
|
|
|
++ // All comments from the Animate impl also applies here.
|
|
|
+ if self.foreground_ratio == other.foreground_ratio {
|
|
|
+ if self.is_currentcolor() {
|
|
|
+ Ok(SquaredDistance::Value(0.))
|
|
|
+ } else {
|
|
|
+ self.color.compute_squared_distance(&other.color)
|
|
|
+ }
|
|
|
+ } else if self.is_currentcolor() && other.is_numeric() {
|
|
|
+ Ok(
|
|
|
+diff --git a/servo/components/style/values/animated/effects.rs b/servo/components/style/values/animated/effects.rs
|
|
|
+--- a/servo/components/style/values/animated/effects.rs
|
|
|
++++ b/servo/components/style/values/animated/effects.rs
|
|
|
+@@ -1,22 +1,21 @@
|
|
|
+ /* 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/. */
|
|
|
+
|
|
|
+ //! Animated types for CSS values related to effects.
|
|
|
+
|
|
|
+-use properties::animated_properties::Animatable;
|
|
|
+ use properties::longhands::box_shadow::computed_value::T as ComputedBoxShadowList;
|
|
|
+ use properties::longhands::filter::computed_value::T as ComputedFilterList;
|
|
|
+ use properties::longhands::text_shadow::computed_value::T as ComputedTextShadowList;
|
|
|
+ use std::cmp;
|
|
|
+ #[cfg(not(feature = "gecko"))]
|
|
|
+ use values::Impossible;
|
|
|
+-use values::animated::{ToAnimatedValue, ToAnimatedZero};
|
|
|
++use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
|
|
|
+ use values::animated::color::RGBA;
|
|
|
+ use values::computed::{Angle, NonNegativeNumber};
|
|
|
+ use values::computed::length::{Length, NonNegativeLength};
|
|
|
+ use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
|
|
+ use values::generics::effects::BoxShadow as GenericBoxShadow;
|
|
|
+ use values::generics::effects::Filter as GenericFilter;
|
|
|
+ use values::generics::effects::SimpleShadow as GenericSimpleShadow;
|
|
|
+
|
|
|
+@@ -61,52 +60,46 @@ impl ToAnimatedValue for ComputedBoxShad
|
|
|
+ }
|
|
|
+
|
|
|
+ #[inline]
|
|
|
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
|
|
+ ComputedBoxShadowList(ToAnimatedValue::from_animated_value(animated.0))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<S> Animatable for ShadowList<S>
|
|
|
++impl<S> Animate for ShadowList<S>
|
|
|
+ where
|
|
|
+- S: Animatable + Clone + ToAnimatedZero,
|
|
|
++ S: Animate + Clone + ToAnimatedZero,
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(
|
|
|
+- &self,
|
|
|
+- other: &Self,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64,
|
|
|
+- ) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ if procedure == Procedure::Add {
|
|
|
++ return Ok(ShadowList(
|
|
|
++ self.0.iter().chain(&other.0).cloned().collect(),
|
|
|
++ ));
|
|
|
++ }
|
|
|
++ // FIXME(nox): Use itertools here, to avoid the need for `unreachable!`.
|
|
|
+ let max_len = cmp::max(self.0.len(), other.0.len());
|
|
|
+ let mut shadows = Vec::with_capacity(max_len);
|
|
|
+ for i in 0..max_len {
|
|
|
+ shadows.push(match (self.0.get(i), other.0.get(i)) {
|
|
|
+ (Some(shadow), Some(other)) => {
|
|
|
+- shadow.add_weighted(other, self_portion, other_portion)?
|
|
|
++ shadow.animate(other, procedure)?
|
|
|
+ },
|
|
|
+ (Some(shadow), None) => {
|
|
|
+- shadow.add_weighted(&shadow.to_animated_zero()?, self_portion, other_portion)?
|
|
|
++ shadow.animate(&shadow.to_animated_zero()?, procedure)?
|
|
|
+ },
|
|
|
+ (None, Some(shadow)) => {
|
|
|
+- shadow.to_animated_zero()?.add_weighted(&shadow, self_portion, other_portion)?
|
|
|
++ shadow.to_animated_zero()?.animate(shadow, procedure)?
|
|
|
+ },
|
|
|
+ (None, None) => unreachable!(),
|
|
|
+ });
|
|
|
+ }
|
|
|
+ Ok(ShadowList(shadows))
|
|
|
+ }
|
|
|
+-
|
|
|
+- #[inline]
|
|
|
+- fn add(&self, other: &Self) -> Result<Self, ()> {
|
|
|
+- Ok(ShadowList(
|
|
|
+- self.0.iter().cloned().chain(other.0.iter().cloned()).collect(),
|
|
|
+- ))
|
|
|
+- }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<S> ComputeSquaredDistance for ShadowList<S>
|
|
|
+ where
|
|
|
+ S: ComputeSquaredDistance + ToAnimatedZero,
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+@@ -141,30 +134,25 @@ impl ToAnimatedValue for ComputedTextSha
|
|
|
+ }
|
|
|
+
|
|
|
+ #[inline]
|
|
|
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
|
|
+ ComputedTextShadowList(ToAnimatedValue::from_animated_value(animated.0))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for BoxShadow {
|
|
|
++impl Animate for BoxShadow {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(
|
|
|
+- &self,
|
|
|
+- other: &Self,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64,
|
|
|
+- ) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ if self.inset != other.inset {
|
|
|
+ return Err(());
|
|
|
+ }
|
|
|
+ Ok(BoxShadow {
|
|
|
+- base: self.base.add_weighted(&other.base, self_portion, other_portion)?,
|
|
|
+- spread: self.spread.add_weighted(&other.spread, self_portion, other_portion)?,
|
|
|
++ base: self.base.animate(&other.base, procedure)?,
|
|
|
++ spread: self.spread.animate(&other.spread, procedure)?,
|
|
|
+ inset: self.inset,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for BoxShadow {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+@@ -219,36 +207,31 @@ impl ToAnimatedValue for ComputedFilterL
|
|
|
+
|
|
|
+ impl ToAnimatedZero for FilterList {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+ Ok(FilterList(vec![]))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for SimpleShadow {
|
|
|
++impl Animate for SimpleShadow {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- let color = self.color.add_weighted(&other.color, self_portion, other_portion)?;
|
|
|
+- let horizontal = self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?;
|
|
|
+- let vertical = self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?;
|
|
|
+- let blur = self.blur.add_weighted(&other.blur, self_portion, other_portion)?;
|
|
|
+-
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ Ok(SimpleShadow {
|
|
|
+- color: color,
|
|
|
+- horizontal: horizontal,
|
|
|
+- vertical: vertical,
|
|
|
+- blur: blur,
|
|
|
++ color: self.color.animate(&other.color, procedure)?,
|
|
|
++ horizontal: self.horizontal.animate(&other.horizontal, procedure)?,
|
|
|
++ vertical: self.vertical.animate(&other.vertical, procedure)?,
|
|
|
++ blur: self.blur.animate(&other.blur, procedure)?,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for SimpleShadow {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+ Ok(SimpleShadow {
|
|
|
+- color: Some(RGBA::transparent()),
|
|
|
++ color: self.color.to_animated_zero()?,
|
|
|
+ horizontal: self.horizontal.to_animated_zero()?,
|
|
|
+ vertical: self.vertical.to_animated_zero()?,
|
|
|
+ blur: self.blur.to_animated_zero()?,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+diff --git a/servo/components/style/values/animated/mod.rs b/servo/components/style/values/animated/mod.rs
|
|
|
+--- a/servo/components/style/values/animated/mod.rs
|
|
|
++++ b/servo/components/style/values/animated/mod.rs
|
|
|
+@@ -4,16 +4,17 @@
|
|
|
+
|
|
|
+ //! Animated values.
|
|
|
+ //!
|
|
|
+ //! Some values, notably colors, cannot be interpolated directly with their
|
|
|
+ //! computed values and need yet another intermediate representation. This
|
|
|
+ //! module's raison d'être is to ultimately contain all these types.
|
|
|
+
|
|
|
+ use app_units::Au;
|
|
|
++use euclid::{Point2D, Size2D};
|
|
|
+ use smallvec::SmallVec;
|
|
|
+ use std::cmp::max;
|
|
|
+ use values::computed::Angle as ComputedAngle;
|
|
|
+ use values::computed::BorderCornerRadius as ComputedBorderCornerRadius;
|
|
|
+ #[cfg(feature = "servo")]
|
|
|
+ use values::computed::ComputedUrl;
|
|
|
+ use values::computed::GreaterThanOrEqualToOneNumber as ComputedGreaterThanOrEqualToOneNumber;
|
|
|
+ use values::computed::MaxLength as ComputedMaxLength;
|
|
|
+@@ -22,30 +23,152 @@ use values::computed::NonNegativeAu;
|
|
|
+ use values::computed::NonNegativeLengthOrPercentage as ComputedNonNegativeLengthOrPercentage;
|
|
|
+ use values::computed::NonNegativeNumber as ComputedNonNegativeNumber;
|
|
|
+ use values::computed::PositiveInteger as ComputedPositiveInteger;
|
|
|
+ use values::specified::url::SpecifiedUrl;
|
|
|
+
|
|
|
+ pub mod color;
|
|
|
+ pub mod effects;
|
|
|
+
|
|
|
++/// Animating from one value to another.
|
|
|
++pub trait Animate: Sized {
|
|
|
++ /// Animate a value towards another one, given an animation procedure.
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>;
|
|
|
++}
|
|
|
++
|
|
|
++/// An animation procedure.
|
|
|
++///
|
|
|
++/// https://w3c.github.io/web-animations/#procedures-for-animating-properties
|
|
|
++#[allow(missing_docs)]
|
|
|
++#[derive(Clone, Copy, Debug, PartialEq)]
|
|
|
++pub enum Procedure {
|
|
|
++ /// https://w3c.github.io/web-animations/#animation-interpolation
|
|
|
++ Interpolate { progress: f64 },
|
|
|
++ /// https://w3c.github.io/web-animations/#animation-addition
|
|
|
++ Add,
|
|
|
++ /// https://w3c.github.io/web-animations/#animation-accumulation
|
|
|
++ Accumulate { count: u64 },
|
|
|
++}
|
|
|
++
|
|
|
+ /// Conversion between computed values and intermediate values for animations.
|
|
|
+ ///
|
|
|
+ /// Notably, colors are represented as four floats during animations.
|
|
|
+ pub trait ToAnimatedValue {
|
|
|
+ /// The type of the animated value.
|
|
|
+ type AnimatedValue;
|
|
|
+
|
|
|
+ /// Converts this value to an animated value.
|
|
|
+ fn to_animated_value(self) -> Self::AnimatedValue;
|
|
|
+
|
|
|
+ /// Converts back an animated value into a computed value.
|
|
|
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self;
|
|
|
+ }
|
|
|
+
|
|
|
++/// Marker trait for computed values with the same representation during animations.
|
|
|
++pub trait AnimatedValueAsComputed {}
|
|
|
++
|
|
|
++/// Returns a value similar to `self` that represents zero.
|
|
|
++pub trait ToAnimatedZero: Sized {
|
|
|
++ /// Returns a value that, when added with an underlying value, will produce the underlying
|
|
|
++ /// value. This is used for SMIL animation's "by-animation" where SMIL first interpolates from
|
|
|
++ /// the zero value to the 'by' value, and then adds the result to the underlying value.
|
|
|
++ ///
|
|
|
++ /// This is not the necessarily the same as the initial value of a property. For example, the
|
|
|
++ /// initial value of 'stroke-width' is 1, but the zero value is 0, since adding 1 to the
|
|
|
++ /// underlying value will not produce the underlying value.
|
|
|
++ fn to_animated_zero(&self) -> Result<Self, ()>;
|
|
|
++}
|
|
|
++
|
|
|
++impl Procedure {
|
|
|
++ /// Returns this procedure as a pair of weights.
|
|
|
++ ///
|
|
|
++ /// This is useful for animations that don't animate differently
|
|
|
++ /// depending on the used procedure.
|
|
|
++ #[inline]
|
|
|
++ pub fn weights(self) -> (f64, f64) {
|
|
|
++ match self {
|
|
|
++ Procedure::Interpolate { progress } => (1. - progress, progress),
|
|
|
++ Procedure::Add => (1., 1.),
|
|
|
++ Procedure::Accumulate { count } => (count as f64, 1.),
|
|
|
++ }
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++/// https://drafts.csswg.org/css-transitions/#animtype-number
|
|
|
++impl Animate for i32 {
|
|
|
++ #[inline]
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(((*self as f64).animate(&(*other as f64), procedure)? + 0.5).floor() as i32)
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++/// https://drafts.csswg.org/css-transitions/#animtype-number
|
|
|
++impl Animate for f32 {
|
|
|
++ #[inline]
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok((*self as f64).animate(&(*other as f64), procedure)? as f32)
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++/// https://drafts.csswg.org/css-transitions/#animtype-number
|
|
|
++impl Animate for f64 {
|
|
|
++ #[inline]
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ let (self_weight, other_weight) = procedure.weights();
|
|
|
++ Ok(*self * self_weight + *other * other_weight)
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++impl<T> Animate for Option<T>
|
|
|
++where
|
|
|
++ T: Animate,
|
|
|
++{
|
|
|
++ #[inline]
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ match (self.as_ref(), other.as_ref()) {
|
|
|
++ (Some(ref this), Some(ref other)) => Ok(Some(this.animate(other, procedure)?)),
|
|
|
++ (None, None) => Ok(None),
|
|
|
++ _ => Err(()),
|
|
|
++ }
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++impl Animate for Au {
|
|
|
++ #[inline]
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(Au(self.0.animate(&other.0, procedure)?))
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++impl<T> Animate for Size2D<T>
|
|
|
++where
|
|
|
++ T: Animate + Copy,
|
|
|
++{
|
|
|
++ #[inline]
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(Size2D::new(
|
|
|
++ self.width.animate(&other.width, procedure)?,
|
|
|
++ self.height.animate(&other.height, procedure)?,
|
|
|
++ ))
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++impl<T> Animate for Point2D<T>
|
|
|
++where
|
|
|
++ T: Animate + Copy,
|
|
|
++{
|
|
|
++ #[inline]
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(Point2D::new(
|
|
|
++ self.x.animate(&other.x, procedure)?,
|
|
|
++ self.y.animate(&other.y, procedure)?,
|
|
|
++ ))
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
+ impl<T> ToAnimatedValue for Option<T>
|
|
|
+ where
|
|
|
+ T: ToAnimatedValue,
|
|
|
+ {
|
|
|
+ type AnimatedValue = Option<<T as ToAnimatedValue>::AnimatedValue>;
|
|
|
+
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_value(self) -> Self::AnimatedValue {
|
|
|
+@@ -87,19 +210,16 @@ where
|
|
|
+ }
|
|
|
+
|
|
|
+ #[inline]
|
|
|
+ fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
|
|
+ animated.into_iter().map(T::from_animated_value).collect()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-/// Marker trait for computed values with the same representation during animations.
|
|
|
+-pub trait AnimatedValueAsComputed {}
|
|
|
+-
|
|
|
+ impl AnimatedValueAsComputed for Au {}
|
|
|
+ impl AnimatedValueAsComputed for ComputedAngle {}
|
|
|
+ impl AnimatedValueAsComputed for SpecifiedUrl {}
|
|
|
+ #[cfg(feature = "servo")]
|
|
|
+ impl AnimatedValueAsComputed for ComputedUrl {}
|
|
|
+ impl AnimatedValueAsComputed for bool {}
|
|
|
+ impl AnimatedValueAsComputed for f32 {}
|
|
|
+
|
|
|
+@@ -258,28 +378,16 @@ impl ToAnimatedValue for ComputedMozLeng
|
|
|
+ };
|
|
|
+ ComputedMozLength::LengthOrPercentageOrAuto(result)
|
|
|
+ },
|
|
|
+ _ => animated
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-/// Returns a value similar to `self` that represents zero.
|
|
|
+-pub trait ToAnimatedZero: Sized {
|
|
|
+- /// Returns a value that, when added with an underlying value, will produce the underlying
|
|
|
+- /// value. This is used for SMIL animation's "by-animation" where SMIL first interpolates from
|
|
|
+- /// the zero value to the 'by' value, and then adds the result to the underlying value.
|
|
|
+- ///
|
|
|
+- /// This is not the necessarily the same as the initial value of a property. For example, the
|
|
|
+- /// initial value of 'stroke-width' is 1, but the zero value is 0, since adding 1 to the
|
|
|
+- /// underlying value will not produce the underlying value.
|
|
|
+- fn to_animated_zero(&self) -> Result<Self, ()>;
|
|
|
+-}
|
|
|
+-
|
|
|
+ impl ToAnimatedZero for Au {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> { Ok(Au(0)) }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for f32 {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> { Ok(0.) }
|
|
|
+@@ -289,8 +397,21 @@ impl ToAnimatedZero for f64 {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> { Ok(0.) }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for i32 {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> { Ok(0) }
|
|
|
+ }
|
|
|
++
|
|
|
++impl<T> ToAnimatedZero for Option<T>
|
|
|
++where
|
|
|
++ T: ToAnimatedZero,
|
|
|
++{
|
|
|
++ #[inline]
|
|
|
++ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
++ match *self {
|
|
|
++ Some(ref value) => Ok(Some(value.to_animated_zero()?)),
|
|
|
++ None => Ok(None),
|
|
|
++ }
|
|
|
++ }
|
|
|
++}
|
|
|
+diff --git a/servo/components/style/values/computed/angle.rs b/servo/components/style/values/computed/angle.rs
|
|
|
+--- a/servo/components/style/values/computed/angle.rs
|
|
|
++++ b/servo/components/style/values/computed/angle.rs
|
|
|
+@@ -1,19 +1,19 @@
|
|
|
+ /* 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/. */
|
|
|
+
|
|
|
+ //! Computed angles.
|
|
|
+
|
|
|
+-use properties::animated_properties::Animatable;
|
|
|
+ use std::{f32, f64, fmt};
|
|
|
+ use std::f64::consts::PI;
|
|
|
+ use style_traits::ToCss;
|
|
|
+ use values::CSSFloat;
|
|
|
++use values::animated::{Animate, Procedure, ToAnimatedZero};
|
|
|
+ use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
|
|
+
|
|
|
+ /// A computed angle.
|
|
|
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
|
|
|
+ #[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, PartialOrd)]
|
|
|
+ pub enum Angle {
|
|
|
+ /// An angle with degree unit.
|
|
|
+ Degree(CSSFloat),
|
|
|
+@@ -60,34 +60,44 @@ impl Angle {
|
|
|
+
|
|
|
+ /// Returns an angle that represents a rotation of zero radians.
|
|
|
+ pub fn zero() -> Self {
|
|
|
+ Angle::Radian(0.0)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// https://drafts.csswg.org/css-transitions/#animtype-number
|
|
|
+-impl Animatable for Angle {
|
|
|
++impl Animate for Angle {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ match (self, other) {
|
|
|
+ (&Angle::Degree(ref this), &Angle::Degree(ref other)) => {
|
|
|
+- Ok(Angle::Degree(this.add_weighted(other, self_portion, other_portion)?))
|
|
|
++ Ok(Angle::Degree(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+ (&Angle::Gradian(ref this), &Angle::Gradian(ref other)) => {
|
|
|
+- Ok(Angle::Gradian(this.add_weighted(other, self_portion, other_portion)?))
|
|
|
++ Ok(Angle::Gradian(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+ (&Angle::Turn(ref this), &Angle::Turn(ref other)) => {
|
|
|
+- Ok(Angle::Turn(this.add_weighted(other, self_portion, other_portion)?))
|
|
|
++ Ok(Angle::Turn(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+ _ => {
|
|
|
+- self.radians()
|
|
|
+- .add_weighted(&other.radians(), self_portion, other_portion)
|
|
|
+- .map(Angle::from_radians)
|
|
|
+- }
|
|
|
++ Ok(Angle::from_radians(self.radians().animate(&other.radians(), procedure)?))
|
|
|
++ },
|
|
|
++ }
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++impl ToAnimatedZero for Angle {
|
|
|
++ #[inline]
|
|
|
++ fn to_animated_zero(&self) -> Result<Angle, ()> {
|
|
|
++ match *self {
|
|
|
++ Angle::Degree(ref this) => Ok(Angle::Degree(this.to_animated_zero()?)),
|
|
|
++ Angle::Gradian(ref this) => Ok(Angle::Gradian(this.to_animated_zero()?)),
|
|
|
++ Angle::Radian(ref this) => Ok(Angle::Radian(this.to_animated_zero()?)),
|
|
|
++ Angle::Turn(ref this) => Ok(Angle::Turn(this.to_animated_zero()?)),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ComputeSquaredDistance for Angle {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+ // Use the formula for calculating the distance between angles defined in SVG:
|
|
|
+diff --git a/servo/components/style/values/computed/background.rs b/servo/components/style/values/computed/background.rs
|
|
|
+--- a/servo/components/style/values/computed/background.rs
|
|
|
++++ b/servo/components/style/values/computed/background.rs
|
|
|
+@@ -1,35 +1,35 @@
|
|
|
+ /* 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/. */
|
|
|
+
|
|
|
+ //! Computed types for CSS values related to backgrounds.
|
|
|
+
|
|
|
+-use properties::animated_properties::{Animatable, RepeatableListAnimatable};
|
|
|
++use properties::animated_properties::RepeatableListAnimatable;
|
|
|
+ use properties::longhands::background_size::computed_value::T as BackgroundSizeList;
|
|
|
+-use values::animated::{ToAnimatedValue, ToAnimatedZero};
|
|
|
++use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
|
|
|
+ use values::computed::length::LengthOrPercentageOrAuto;
|
|
|
+ use values::generics::background::BackgroundSize as GenericBackgroundSize;
|
|
|
+
|
|
|
+ /// A computed value for the `background-size` property.
|
|
|
+ pub type BackgroundSize = GenericBackgroundSize<LengthOrPercentageOrAuto>;
|
|
|
+
|
|
|
+ impl RepeatableListAnimatable for BackgroundSize {}
|
|
|
+
|
|
|
+-impl Animatable for BackgroundSize {
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++impl Animate for BackgroundSize {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ match (self, other) {
|
|
|
+ (
|
|
|
+ &GenericBackgroundSize::Explicit { width: self_width, height: self_height },
|
|
|
+ &GenericBackgroundSize::Explicit { width: other_width, height: other_height },
|
|
|
+ ) => {
|
|
|
+ Ok(GenericBackgroundSize::Explicit {
|
|
|
+- width: self_width.add_weighted(&other_width, self_portion, other_portion)?,
|
|
|
+- height: self_height.add_weighted(&other_height, self_portion, other_portion)?,
|
|
|
++ width: self_width.animate(&other_width, procedure)?,
|
|
|
++ height: self_height.animate(&other_height, procedure)?,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for BackgroundSize {
|
|
|
+diff --git a/servo/components/style/values/computed/length.rs b/servo/components/style/values/computed/length.rs
|
|
|
+--- a/servo/components/style/values/computed/length.rs
|
|
|
++++ b/servo/components/style/values/computed/length.rs
|
|
|
+@@ -6,16 +6,17 @@
|
|
|
+
|
|
|
+ use app_units::{Au, AU_PER_PX};
|
|
|
+ use ordered_float::NotNaN;
|
|
|
+ use std::fmt;
|
|
|
+ use style_traits::ToCss;
|
|
|
+ use style_traits::values::specified::AllowedLengthType;
|
|
|
+ use super::{Number, ToComputedValue, Context, Percentage};
|
|
|
+ use values::{Auto, CSSFloat, Either, ExtremumLength, None_, Normal, specified};
|
|
|
++use values::animated::ToAnimatedZero;
|
|
|
+ use values::computed::{NonNegativeAu, NonNegativeNumber};
|
|
|
+ use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
|
|
+ use values::generics::NonNegative;
|
|
|
+ use values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength};
|
|
|
+ use values::specified::length::ViewportPercentageLength;
|
|
|
+
|
|
|
+ pub use super::image::Image;
|
|
|
+ pub use values::specified::{Angle, BorderStyle, Time, UrlOrNone};
|
|
|
+@@ -67,16 +68,27 @@ impl ToComputedValue for specified::Leng
|
|
|
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
|
+ #[allow(missing_docs)]
|
|
|
+ pub struct CalcLengthOrPercentage {
|
|
|
+ pub clamping_mode: AllowedLengthType,
|
|
|
+ length: Au,
|
|
|
+ pub percentage: Option<Percentage>,
|
|
|
+ }
|
|
|
+
|
|
|
++impl ToAnimatedZero for CalcLengthOrPercentage {
|
|
|
++ #[inline]
|
|
|
++ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
++ Ok(CalcLengthOrPercentage {
|
|
|
++ clamping_mode: self.clamping_mode,
|
|
|
++ length: self.length.to_animated_zero()?,
|
|
|
++ percentage: self.percentage.to_animated_zero()?,
|
|
|
++ })
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
+ impl ComputeSquaredDistance for CalcLengthOrPercentage {
|
|
|
+ #[inline]
|
|
|
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
|
|
|
+ // FIXME(nox): This looks incorrect to me, to add a distance between lengths
|
|
|
+ // with a distance between percentages.
|
|
|
+ Ok(
|
|
|
+ self.unclamped_length().to_f64_px().compute_squared_distance(
|
|
|
+ &other.unclamped_length().to_f64_px())? +
|
|
|
+diff --git a/servo/components/style/values/computed/percentage.rs b/servo/components/style/values/computed/percentage.rs
|
|
|
+--- a/servo/components/style/values/computed/percentage.rs
|
|
|
++++ b/servo/components/style/values/computed/percentage.rs
|
|
|
+@@ -1,19 +1,18 @@
|
|
|
+ /* 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/. */
|
|
|
+
|
|
|
+ //! Computed percentages.
|
|
|
+
|
|
|
+-use properties::animated_properties::Animatable;
|
|
|
+ use std::fmt;
|
|
|
+ use style_traits::ToCss;
|
|
|
+ use values::{CSSFloat, serialize_percentage};
|
|
|
+-use values::animated::ToAnimatedZero;
|
|
|
++use values::animated::{Animate, Procedure, ToAnimatedZero};
|
|
|
+
|
|
|
+ /// A computed percentage.
|
|
|
+ #[derive(Clone, ComputeSquaredDistance, Copy, Debug, Default, HasViewportPercentage, PartialEq, PartialOrd)]
|
|
|
+ #[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
|
|
|
+ pub struct Percentage(pub CSSFloat);
|
|
|
+
|
|
|
+ impl Percentage {
|
|
|
+ /// 0%
|
|
|
+@@ -31,20 +30,20 @@ impl Percentage {
|
|
|
+ /// Returns the absolute value for this percentage.
|
|
|
+ #[inline]
|
|
|
+ pub fn abs(&self) -> Self {
|
|
|
+ Percentage(self.0.abs())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// https://drafts.csswg.org/css-transitions/#animtype-percentage
|
|
|
+-impl Animatable for Percentage {
|
|
|
++impl Animate for Percentage {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- Ok(Percentage((self.0 as f64 * self_portion + other.0 as f64 * other_portion) as f32))
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(Percentage(self.0.animate(&other.0, procedure)?))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for Percentage {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+ Ok(Percentage(0.))
|
|
|
+ }
|
|
|
+diff --git a/servo/components/style/values/computed/text.rs b/servo/components/style/values/computed/text.rs
|
|
|
+--- a/servo/components/style/values/computed/text.rs
|
|
|
++++ b/servo/components/style/values/computed/text.rs
|
|
|
+@@ -1,17 +1,16 @@
|
|
|
+ /* 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/. */
|
|
|
+
|
|
|
+ //! Computed types for text properties.
|
|
|
+
|
|
|
+-use properties::animated_properties::Animatable;
|
|
|
+ use values::{CSSInteger, CSSFloat};
|
|
|
+-use values::animated::ToAnimatedZero;
|
|
|
++use values::animated::{Animate, Procedure, ToAnimatedZero};
|
|
|
+ use values::computed::{NonNegativeAu, NonNegativeNumber};
|
|
|
+ use values::computed::length::{Length, LengthOrPercentage};
|
|
|
+ use values::generics::text::InitialLetter as GenericInitialLetter;
|
|
|
+ use values::generics::text::LineHeight as GenericLineHeight;
|
|
|
+ use values::generics::text::Spacing;
|
|
|
+
|
|
|
+ /// A computed value for the `initial-letter` property.
|
|
|
+ pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
|
|
|
+@@ -20,31 +19,31 @@ pub type InitialLetter = GenericInitialL
|
|
|
+ pub type LetterSpacing = Spacing<Length>;
|
|
|
+
|
|
|
+ /// A computed value for the `word-spacing` property.
|
|
|
+ pub type WordSpacing = Spacing<LengthOrPercentage>;
|
|
|
+
|
|
|
+ /// A computed value for the `line-height` property.
|
|
|
+ pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeAu>;
|
|
|
+
|
|
|
+-impl Animatable for LineHeight {
|
|
|
++impl Animate for LineHeight {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
+- match (*self, *other) {
|
|
|
+- (GenericLineHeight::Length(ref this), GenericLineHeight::Length(ref other)) => {
|
|
|
+- this.add_weighted(other, self_portion, other_portion).map(GenericLineHeight::Length)
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ match (self, other) {
|
|
|
++ (&GenericLineHeight::Length(ref this), &GenericLineHeight::Length(ref other)) => {
|
|
|
++ Ok(GenericLineHeight::Length(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+- (GenericLineHeight::Number(ref this), GenericLineHeight::Number(ref other)) => {
|
|
|
+- this.add_weighted(other, self_portion, other_portion).map(GenericLineHeight::Number)
|
|
|
++ (&GenericLineHeight::Number(ref this), &GenericLineHeight::Number(ref other)) => {
|
|
|
++ Ok(GenericLineHeight::Number(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+- (GenericLineHeight::Normal, GenericLineHeight::Normal) => {
|
|
|
++ (&GenericLineHeight::Normal, &GenericLineHeight::Normal) => {
|
|
|
+ Ok(GenericLineHeight::Normal)
|
|
|
+ },
|
|
|
+ #[cfg(feature = "gecko")]
|
|
|
+- (GenericLineHeight::MozBlockHeight, GenericLineHeight::MozBlockHeight) => {
|
|
|
++ (&GenericLineHeight::MozBlockHeight, &GenericLineHeight::MozBlockHeight) => {
|
|
|
+ Ok(GenericLineHeight::MozBlockHeight)
|
|
|
+ },
|
|
|
+ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for LineHeight {
|
|
|
+diff --git a/servo/components/style/values/computed/transform.rs b/servo/components/style/values/computed/transform.rs
|
|
|
+--- a/servo/components/style/values/computed/transform.rs
|
|
|
++++ b/servo/components/style/values/computed/transform.rs
|
|
|
+@@ -1,16 +1,15 @@
|
|
|
+ /* 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/. */
|
|
|
+
|
|
|
+ //! Computed types for CSS values that are related to transformations.
|
|
|
+
|
|
|
+-use properties::animated_properties::Animatable;
|
|
|
+-use values::animated::ToAnimatedZero;
|
|
|
++use values::animated::{Animate, Procedure, ToAnimatedZero};
|
|
|
+ use values::computed::{Length, LengthOrPercentage, Number, Percentage};
|
|
|
+ use values::generics::transform::TimingFunction as GenericTimingFunction;
|
|
|
+ use values::generics::transform::TransformOrigin as GenericTransformOrigin;
|
|
|
+
|
|
|
+ /// The computed value of a CSS `<transform-origin>`
|
|
|
+ pub type TransformOrigin = GenericTransformOrigin<LengthOrPercentage, LengthOrPercentage, Length>;
|
|
|
+
|
|
|
+ /// A computed timing function.
|
|
|
+@@ -23,23 +22,23 @@ impl TransformOrigin {
|
|
|
+ Self::new(
|
|
|
+ LengthOrPercentage::Percentage(Percentage(0.5)),
|
|
|
+ LengthOrPercentage::Percentage(Percentage(0.5)),
|
|
|
+ Length::from_px(0),
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl Animatable for TransformOrigin {
|
|
|
++impl Animate for TransformOrigin {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ Ok(Self::new(
|
|
|
+- self.horizontal.add_weighted(&other.horizontal, self_portion, other_portion)?,
|
|
|
+- self.vertical.add_weighted(&other.vertical, self_portion, other_portion)?,
|
|
|
+- self.depth.add_weighted(&other.depth, self_portion, other_portion)?,
|
|
|
++ self.horizontal.animate(&other.horizontal, procedure)?,
|
|
|
++ self.vertical.animate(&other.vertical, procedure)?,
|
|
|
++ self.depth.animate(&other.depth, procedure)?,
|
|
|
+ ))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl ToAnimatedZero for TransformOrigin {
|
|
|
+ #[inline]
|
|
|
+ fn to_animated_zero(&self) -> Result<Self, ()> {
|
|
|
+ Ok(Self::new(
|
|
|
+diff --git a/servo/components/style/values/generics/basic_shape.rs b/servo/components/style/values/generics/basic_shape.rs
|
|
|
+--- a/servo/components/style/values/generics/basic_shape.rs
|
|
|
++++ b/servo/components/style/values/generics/basic_shape.rs
|
|
|
+@@ -1,19 +1,18 @@
|
|
|
+ /* 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 properties::animated_properties::Animatable;
|
|
|
+ use std::fmt;
|
|
|
+ use style_traits::{HasViewportPercentage, ToCss};
|
|
|
+-use values::animated::ToAnimatedZero;
|
|
|
++use values::animated::{Animate, Procedure, ToAnimatedZero};
|
|
|
+ use values::computed::ComputedValueAsSpecified;
|
|
|
+ use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
|
|
+ use values::generics::border::BorderRadius;
|
|
|
+ use values::generics::position::Position;
|
|
|
+ use values::generics::rect::Rect;
|
|
|
+
|
|
|
+ /// A clipping shape, for `clip-path`.
|
|
|
+ pub type ClippingShape<BasicShape, Url> = ShapeSource<BasicShape, GeometryBox, Url>;
|
|
|
+@@ -118,34 +117,31 @@ pub struct Polygon<LengthOrPercentage> {
|
|
|
+ // https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
|
|
|
+ // says that it can also be `inherit`
|
|
|
+ define_css_keyword_enum!(FillRule:
|
|
|
+ "nonzero" => NonZero,
|
|
|
+ "evenodd" => EvenOdd
|
|
|
+ );
|
|
|
+ add_impls_for_keyword_enum!(FillRule);
|
|
|
+
|
|
|
+-impl<B, T, U> Animatable for ShapeSource<B, T, U>
|
|
|
++impl<B, T, U> Animate for ShapeSource<B, T, U>
|
|
|
+ where
|
|
|
+- B: Animatable,
|
|
|
++ B: Animate,
|
|
|
+ T: Clone + PartialEq,
|
|
|
+ {
|
|
|
+- fn add_weighted(
|
|
|
+- &self,
|
|
|
+- other: &Self,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64,
|
|
|
+- ) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ match (self, other) {
|
|
|
+ (
|
|
|
+ &ShapeSource::Shape(ref this, ref this_box),
|
|
|
+ &ShapeSource::Shape(ref other, ref other_box),
|
|
|
+ ) if this_box == other_box => {
|
|
|
+- let shape = this.add_weighted(other, self_portion, other_portion)?;
|
|
|
+- Ok(ShapeSource::Shape(shape, this_box.clone()))
|
|
|
++ Ok(ShapeSource::Shape(
|
|
|
++ this.animate(other, procedure)?,
|
|
|
++ this_box.clone(),
|
|
|
++ ))
|
|
|
+ },
|
|
|
+ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // FIXME(nox): Implement ComputeSquaredDistance for T types and stop
|
|
|
+ // using PartialEq here, this will let us derive this impl.
|
|
|
+@@ -173,59 +169,50 @@ impl<B, T, U> ToAnimatedZero for ShapeSo
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<B, T, U> HasViewportPercentage for ShapeSource<B, T, U> {
|
|
|
+ #[inline]
|
|
|
+ fn has_viewport_percentage(&self) -> bool { false }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<H, V, L> Animatable for BasicShape<H, V, L>
|
|
|
++impl<H, V, L> Animate for BasicShape<H, V, L>
|
|
|
+ where
|
|
|
+- H: Animatable,
|
|
|
+- V: Animatable,
|
|
|
+- L: Animatable + Copy,
|
|
|
++ H: Animate,
|
|
|
++ V: Animate,
|
|
|
++ L: Animate + Copy,
|
|
|
+ {
|
|
|
+- fn add_weighted(
|
|
|
+- &self,
|
|
|
+- other: &Self,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64,
|
|
|
+- ) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ match (self, other) {
|
|
|
+ (&BasicShape::Circle(ref this), &BasicShape::Circle(ref other)) => {
|
|
|
+- Ok(BasicShape::Circle(this.add_weighted(other, self_portion, other_portion)?))
|
|
|
++ Ok(BasicShape::Circle(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+ (&BasicShape::Ellipse(ref this), &BasicShape::Ellipse(ref other)) => {
|
|
|
+- Ok(BasicShape::Ellipse(this.add_weighted(other, self_portion, other_portion)?))
|
|
|
++ Ok(BasicShape::Ellipse(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+ (&BasicShape::Inset(ref this), &BasicShape::Inset(ref other)) => {
|
|
|
+- Ok(BasicShape::Inset(this.add_weighted(other, self_portion, other_portion)?))
|
|
|
++ Ok(BasicShape::Inset(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+ (&BasicShape::Polygon(ref this), &BasicShape::Polygon(ref other)) => {
|
|
|
+- Ok(BasicShape::Polygon(this.add_weighted(other, self_portion, other_portion)?))
|
|
|
++ Ok(BasicShape::Polygon(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<L> Animatable for InsetRect<L>
|
|
|
++impl<L> Animate for InsetRect<L>
|
|
|
+ where
|
|
|
+- L: Animatable + Copy,
|
|
|
++ L: Animate + Copy,
|
|
|
+ {
|
|
|
+- fn add_weighted(
|
|
|
+- &self,
|
|
|
+- other: &Self,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64,
|
|
|
+- ) -> Result<Self, ()> {
|
|
|
+- let rect = self.rect.add_weighted(&other.rect, self_portion, other_portion)?;
|
|
|
+- let round = self.round.add_weighted(&other.round, self_portion, other_portion)?;
|
|
|
+- Ok(InsetRect { rect, round })
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(InsetRect {
|
|
|
++ rect: self.rect.animate(&other.rect, procedure)?,
|
|
|
++ round: self.round.animate(&other.round, procedure)?,
|
|
|
++ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<L> ToCss for InsetRect<L>
|
|
|
+ where L: ToCss + PartialEq
|
|
|
+ {
|
|
|
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
|
+ dest.write_str("inset(")?;
|
|
|
+@@ -233,97 +220,80 @@ impl<L> ToCss for InsetRect<L>
|
|
|
+ if let Some(ref radius) = self.round {
|
|
|
+ dest.write_str(" round ")?;
|
|
|
+ radius.to_css(dest)?;
|
|
|
+ }
|
|
|
+ dest.write_str(")")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<H, V, L> Animatable for Circle<H, V, L>
|
|
|
++impl<H, V, L> Animate for Circle<H, V, L>
|
|
|
+ where
|
|
|
+- H: Animatable,
|
|
|
+- V: Animatable,
|
|
|
+- L: Animatable,
|
|
|
++ H: Animate,
|
|
|
++ V: Animate,
|
|
|
++ L: Animate,
|
|
|
+ {
|
|
|
+- fn add_weighted(
|
|
|
+- &self,
|
|
|
+- other: &Self,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64,
|
|
|
+- ) -> Result<Self, ()> {
|
|
|
+- let position = self.position.add_weighted(&other.position, self_portion, other_portion)?;
|
|
|
+- let radius = self.radius.add_weighted(&other.radius, self_portion, other_portion)?;
|
|
|
+- Ok(Circle { position, radius })
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(Circle {
|
|
|
++ position: self.position.animate(&other.position, procedure)?,
|
|
|
++ radius: self.radius.animate(&other.radius, procedure)?,
|
|
|
++ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<H, V, L> Animatable for Ellipse<H, V, L>
|
|
|
++impl<H, V, L> Animate for Ellipse<H, V, L>
|
|
|
+ where
|
|
|
+- H: Animatable,
|
|
|
+- V: Animatable,
|
|
|
+- L: Animatable,
|
|
|
++ H: Animate,
|
|
|
++ V: Animate,
|
|
|
++ L: Animate,
|
|
|
+ {
|
|
|
+- fn add_weighted(
|
|
|
+- &self,
|
|
|
+- other: &Self,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64,
|
|
|
+- ) -> Result<Self, ()> {
|
|
|
+- let position = self.position.add_weighted(&other.position, self_portion, other_portion)?;
|
|
|
+- let semiaxis_x = self.semiaxis_x.add_weighted(&other.semiaxis_x, self_portion, other_portion)?;
|
|
|
+- let semiaxis_y = self.semiaxis_y.add_weighted(&other.semiaxis_y, self_portion, other_portion)?;
|
|
|
+- Ok(Ellipse { position, semiaxis_x, semiaxis_y })
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(Ellipse {
|
|
|
++ position: self.position.animate(&other.position, procedure)?,
|
|
|
++ semiaxis_x: self.semiaxis_x.animate(&other.semiaxis_x, procedure)?,
|
|
|
++ semiaxis_y: self.semiaxis_y.animate(&other.semiaxis_y, procedure)?,
|
|
|
++ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<L> Animatable for ShapeRadius<L>
|
|
|
++impl<L> Animate for ShapeRadius<L>
|
|
|
+ where
|
|
|
+- L: Animatable,
|
|
|
++ L: Animate,
|
|
|
+ {
|
|
|
+- fn add_weighted(
|
|
|
+- &self,
|
|
|
+- other: &Self,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64,
|
|
|
+- ) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ match (self, other) {
|
|
|
+ (&ShapeRadius::Length(ref this), &ShapeRadius::Length(ref other)) => {
|
|
|
+- Ok(ShapeRadius::Length(this.add_weighted(other, self_portion, other_portion)?))
|
|
|
++ Ok(ShapeRadius::Length(this.animate(other, procedure)?))
|
|
|
+ },
|
|
|
+ _ => Err(()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<L> Default for ShapeRadius<L> {
|
|
|
+ #[inline]
|
|
|
+ fn default() -> Self { ShapeRadius::ClosestSide }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<L> Animatable for Polygon<L>
|
|
|
++impl<L> Animate for Polygon<L>
|
|
|
+ where
|
|
|
+- L: Animatable,
|
|
|
++ L: Animate,
|
|
|
+ {
|
|
|
+- fn add_weighted(
|
|
|
+- &self,
|
|
|
+- other: &Self,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64,
|
|
|
+- ) -> Result<Self, ()> {
|
|
|
++ 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)| {
|
|
|
+- let x = this.0.add_weighted(&other.0, self_portion, other_portion)?;
|
|
|
+- let y = this.1.add_weighted(&other.1, self_portion, other_portion)?;
|
|
|
+- Ok((x, y))
|
|
|
++ Ok((
|
|
|
++ 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,
|
|
|
+diff --git a/servo/components/style/values/generics/border.rs b/servo/components/style/values/generics/border.rs
|
|
|
+--- a/servo/components/style/values/generics/border.rs
|
|
|
++++ b/servo/components/style/values/generics/border.rs
|
|
|
+@@ -1,18 +1,18 @@
|
|
|
+ /* 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/. */
|
|
|
+
|
|
|
+ //! Generic types for CSS values related to borders.
|
|
|
+
|
|
|
+ use euclid::Size2D;
|
|
|
+-use properties::animated_properties::Animatable;
|
|
|
+ use std::fmt;
|
|
|
+ use style_traits::ToCss;
|
|
|
++use values::animated::{Animate, Procedure};
|
|
|
+ use values::generics::rect::Rect;
|
|
|
+
|
|
|
+ /// A generic value for a single side of a `border-image-width` property.
|
|
|
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
|
+ #[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
|
|
|
+ pub enum BorderImageSideWidth<LengthOrPercentage, Number> {
|
|
|
+ /// `<length-or-percentage>`
|
|
|
+ Length(LengthOrPercentage),
|
|
|
+@@ -108,31 +108,27 @@ impl<L> BorderRadius<L>
|
|
|
+ if widths.0 != heights.0 || widths.1 != heights.1 || widths.2 != heights.2 || widths.3 != heights.3 {
|
|
|
+ dest.write_str(" / ")?;
|
|
|
+ heights.to_css(dest)?;
|
|
|
+ }
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<L> Animatable for BorderRadius<L>
|
|
|
++impl<L> Animate for BorderRadius<L>
|
|
|
+ where
|
|
|
+- L: Animatable + Copy,
|
|
|
++ L: Animate + Copy,
|
|
|
+ {
|
|
|
+- fn add_weighted(
|
|
|
+- &self,
|
|
|
+- other: &Self,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64,
|
|
|
+- ) -> Result<Self, ()> {
|
|
|
+- let tl = self.top_left.add_weighted(&other.top_left, self_portion, other_portion)?;
|
|
|
+- let tr = self.top_right.add_weighted(&other.top_right, self_portion, other_portion)?;
|
|
|
+- let br = self.bottom_right.add_weighted(&other.bottom_right, self_portion, other_portion)?;
|
|
|
+- let bl = self.bottom_left.add_weighted(&other.bottom_left, self_portion, other_portion)?;
|
|
|
+- Ok(BorderRadius::new(tl, tr, br, bl))
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(BorderRadius::new(
|
|
|
++ self.top_left.animate(&other.top_left, procedure)?,
|
|
|
++ self.top_right.animate(&other.top_right, procedure)?,
|
|
|
++ self.bottom_right.animate(&other.bottom_right, procedure)?,
|
|
|
++ self.bottom_left.animate(&other.bottom_left, procedure)?,
|
|
|
++ ))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<L> ToCss for BorderRadius<L>
|
|
|
+ where L: PartialEq + ToCss
|
|
|
+ {
|
|
|
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
|
+ let BorderRadius {
|
|
|
+@@ -158,28 +154,23 @@ impl<L> BorderCornerRadius<L> {
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<L: Clone> From<L> for BorderCornerRadius<L> {
|
|
|
+ fn from(radius: L) -> Self {
|
|
|
+ Self::new(radius.clone(), radius)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<L> Animatable for BorderCornerRadius<L>
|
|
|
++impl<L> Animate for BorderCornerRadius<L>
|
|
|
+ where
|
|
|
+- L: Animatable + Copy,
|
|
|
++ L: Animate + Copy,
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(
|
|
|
+- &self,
|
|
|
+- other: &Self,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64,
|
|
|
+- ) -> Result<Self, ()> {
|
|
|
+- Ok(BorderCornerRadius(self.0.add_weighted(&other.0, self_portion, other_portion)?))
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(BorderCornerRadius(self.0.animate(&other.0, procedure)?))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<L> ToCss for BorderCornerRadius<L>
|
|
|
+ where L: ToCss,
|
|
|
+ {
|
|
|
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
|
|
+ where W: fmt::Write
|
|
|
+diff --git a/servo/components/style/values/generics/rect.rs b/servo/components/style/values/generics/rect.rs
|
|
|
+--- a/servo/components/style/values/generics/rect.rs
|
|
|
++++ b/servo/components/style/values/generics/rect.rs
|
|
|
+@@ -1,19 +1,19 @@
|
|
|
+ /* 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/. */
|
|
|
+
|
|
|
+ //! Generic types for CSS values that are composed of four sides.
|
|
|
+
|
|
|
+ use cssparser::Parser;
|
|
|
+ use parser::{Parse, ParserContext};
|
|
|
+-use properties::animated_properties::Animatable;
|
|
|
+ use std::fmt;
|
|
|
+ use style_traits::{ToCss, ParseError};
|
|
|
++use values::animated::{Animate, Procedure};
|
|
|
+
|
|
|
+ /// A CSS value made of four components, where its `ToCss` impl will try to
|
|
|
+ /// serialize as few components as possible, like for example in `border-width`.
|
|
|
+ #[derive(Clone, ComputeSquaredDistance, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue)]
|
|
|
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
|
+ pub struct Rect<T>(pub T, pub T, pub T, pub T);
|
|
|
+
|
|
|
+ impl<T> Rect<T> {
|
|
|
+@@ -47,31 +47,27 @@ impl<T> Rect<T>
|
|
|
+ // <first> <second> <third>
|
|
|
+ return Ok(Self::new(first, second.clone(), third, second));
|
|
|
+ };
|
|
|
+ // <first> <second> <third> <fourth>
|
|
|
+ Ok(Self::new(first, second, third, fourth))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<L> Animatable for Rect<L>
|
|
|
++impl<L> Animate for Rect<L>
|
|
|
+ where
|
|
|
+- L: Animatable,
|
|
|
++ L: Animate,
|
|
|
+ {
|
|
|
+- fn add_weighted(
|
|
|
+- &self,
|
|
|
+- other: &Self,
|
|
|
+- self_portion: f64,
|
|
|
+- other_portion: f64,
|
|
|
+- ) -> Result<Self, ()> {
|
|
|
+- let first = self.0.add_weighted(&other.0, self_portion, other_portion)?;
|
|
|
+- let second = self.1.add_weighted(&other.1, self_portion, other_portion)?;
|
|
|
+- let third = self.2.add_weighted(&other.2, self_portion, other_portion)?;
|
|
|
+- let fourth = self.3.add_weighted(&other.3, self_portion, other_portion)?;
|
|
|
+- Ok(Rect(first, second, third, fourth))
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
++ Ok(Rect(
|
|
|
++ self.0.animate(&other.0, procedure)?,
|
|
|
++ self.1.animate(&other.1, procedure)?,
|
|
|
++ self.2.animate(&other.2, procedure)?,
|
|
|
++ self.3.animate(&other.3, procedure)?,
|
|
|
++ ))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<T> From<T> for Rect<T>
|
|
|
+ where T: Clone
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+ fn from(value: T) -> Self {
|
|
|
+diff --git a/servo/components/style/values/generics/text.rs b/servo/components/style/values/generics/text.rs
|
|
|
+--- a/servo/components/style/values/generics/text.rs
|
|
|
++++ b/servo/components/style/values/generics/text.rs
|
|
|
+@@ -2,19 +2,18 @@
|
|
|
+ * 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/. */
|
|
|
+
|
|
|
+ //! Generic types for text properties.
|
|
|
+
|
|
|
+ use app_units::Au;
|
|
|
+ use cssparser::Parser;
|
|
|
+ use parser::ParserContext;
|
|
|
+-use properties::animated_properties::Animatable;
|
|
|
+ use style_traits::ParseError;
|
|
|
+-use values::animated::ToAnimatedZero;
|
|
|
++use values::animated::{Animate, Procedure, ToAnimatedZero};
|
|
|
+ use values::distance::{ComputeSquaredDistance, SquaredDistance};
|
|
|
+
|
|
|
+ /// A generic value for the `initial-letter` property.
|
|
|
+ #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
|
+ #[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
|
|
|
+ pub enum InitialLetter<Number, Integer> {
|
|
|
+ /// `normal`
|
|
|
+ Normal,
|
|
|
+@@ -67,28 +66,29 @@ impl<Value> Spacing<Value> {
|
|
|
+ pub fn value(&self) -> Option<&Value> {
|
|
|
+ match *self {
|
|
|
+ Spacing::Normal => None,
|
|
|
+ Spacing::Value(ref value) => Some(value),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-impl<Value> Animatable for Spacing<Value>
|
|
|
+- where Value: Animatable + From<Au>,
|
|
|
++impl<Value> Animate for Spacing<Value>
|
|
|
++where
|
|
|
++ Value: Animate + From<Au>,
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+- fn add_weighted(&self, other: &Self, self_portion: f64, other_portion: f64) -> Result<Self, ()> {
|
|
|
++ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
|
|
+ if let (&Spacing::Normal, &Spacing::Normal) = (self, other) {
|
|
|
+ return Ok(Spacing::Normal);
|
|
|
+ }
|
|
|
+ let zero = Value::from(Au(0));
|
|
|
+ let this = self.value().unwrap_or(&zero);
|
|
|
+ let other = other.value().unwrap_or(&zero);
|
|
|
+- this.add_weighted(other, self_portion, other_portion).map(Spacing::Value)
|
|
|
++ Ok(Spacing::Value(this.animate(other, procedure)?))
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<V> ComputeSquaredDistance for Spacing<V>
|
|
|
+ where
|
|
|
+ V: ComputeSquaredDistance + From<Au>,
|
|
|
+ {
|
|
|
+ #[inline]
|
|
|
+diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs
|
|
|
+--- a/servo/ports/geckolib/glue.rs
|
|
|
++++ b/servo/ports/geckolib/glue.rs
|
|
|
+@@ -100,17 +100,17 @@ use style::invalidation::element::restyl
|
|
|
+ use style::media_queries::{MediaList, parse_media_query_list};
|
|
|
+ use style::parallel;
|
|
|
+ use style::parser::{ParserContext, self};
|
|
|
+ use style::properties::{CascadeFlags, ComputedValues, Importance};
|
|
|
+ use style::properties::{IS_FIELDSET_CONTENT, IS_LINK, IS_VISITED_LINK, LonghandIdSet};
|
|
|
+ use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyId, ShorthandId};
|
|
|
+ use style::properties::{SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, SourcePropertyDeclaration, StyleBuilder};
|
|
|
+ use style::properties::PROHIBIT_DISPLAY_CONTENTS;
|
|
|
+-use style::properties::animated_properties::{Animatable, AnimatableLonghand, AnimationValue};
|
|
|
++use style::properties::animated_properties::{AnimatableLonghand, AnimationValue};
|
|
|
+ use style::properties::animated_properties::compare_property_priority;
|
|
|
+ use style::properties::parse_one_declaration_into;
|
|
|
+ use style::rule_tree::StyleSource;
|
|
|
+ use style::selector_parser::PseudoElementCascadeType;
|
|
|
+ use style::sequential;
|
|
|
+ use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
|
|
|
+ use style::string_cache::Atom;
|
|
|
+ use style::style_adjuster::StyleAdjuster;
|
|
|
+@@ -123,17 +123,17 @@ use style::stylesheets::keyframes_rule::
|
|
|
+ use style::stylesheets::supports_rule::parse_condition_or_declaration;
|
|
|
+ use style::stylist::RuleInclusion;
|
|
|
+ use style::thread_state;
|
|
|
+ use style::timer::Timer;
|
|
|
+ use style::traversal::{DomTraversal, TraversalDriver};
|
|
|
+ use style::traversal::resolve_style;
|
|
|
+ use style::traversal_flags::{TraversalFlags, self};
|
|
|
+ use style::values::{CustomIdent, KeyframesName};
|
|
|
+-use style::values::animated::ToAnimatedZero;
|
|
|
++use style::values::animated::{Animate, Procedure, ToAnimatedZero};
|
|
|
+ use style::values::computed::Context;
|
|
|
+ use style::values::distance::ComputeSquaredDistance;
|
|
|
+ use style_traits::{PARSING_MODE_DEFAULT, ToCss};
|
|
|
+ use super::error_reporter::ErrorReporter;
|
|
|
+ use super::stylesheet_loader::StylesheetLoader;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * For Gecko->Servo function calls, we need to redeclare the same signature that was declared in
|
|
|
+@@ -314,55 +314,55 @@ pub extern "C" fn Servo_MaybeGCRuleTree(
|
|
|
+ #[no_mangle]
|
|
|
+ pub extern "C" fn Servo_AnimationValues_Interpolate(from: RawServoAnimationValueBorrowed,
|
|
|
+ to: RawServoAnimationValueBorrowed,
|
|
|
+ progress: f64)
|
|
|
+ -> RawServoAnimationValueStrong
|
|
|
+ {
|
|
|
+ let from_value = AnimationValue::as_arc(&from);
|
|
|
+ let to_value = AnimationValue::as_arc(&to);
|
|
|
+- if let Ok(value) = from_value.interpolate(to_value, progress) {
|
|
|
++ if let Ok(value) = from_value.animate(to_value, Procedure::Interpolate { progress }) {
|
|
|
+ Arc::new(value).into_strong()
|
|
|
+ } else {
|
|
|
+ RawServoAnimationValueStrong::null()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #[no_mangle]
|
|
|
+ pub extern "C" fn Servo_AnimationValues_IsInterpolable(from: RawServoAnimationValueBorrowed,
|
|
|
+ to: RawServoAnimationValueBorrowed)
|
|
|
+ -> bool {
|
|
|
+ let from_value = AnimationValue::as_arc(&from);
|
|
|
+ let to_value = AnimationValue::as_arc(&to);
|
|
|
+- from_value.interpolate(to_value, 0.5).is_ok()
|
|
|
++ from_value.animate(to_value, Procedure::Interpolate { progress: 0.5 }).is_ok()
|
|
|
+ }
|
|
|
+
|
|
|
+ #[no_mangle]
|
|
|
+ pub extern "C" fn Servo_AnimationValues_Add(a: RawServoAnimationValueBorrowed,
|
|
|
+ b: RawServoAnimationValueBorrowed)
|
|
|
+ -> RawServoAnimationValueStrong
|
|
|
+ {
|
|
|
+ let a_value = AnimationValue::as_arc(&a);
|
|
|
+ let b_value = AnimationValue::as_arc(&b);
|
|
|
+- if let Ok(value) = a_value.add(b_value) {
|
|
|
++ if let Ok(value) = a_value.animate(b_value, Procedure::Add) {
|
|
|
+ Arc::new(value).into_strong()
|
|
|
+ } else {
|
|
|
+ RawServoAnimationValueStrong::null()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #[no_mangle]
|
|
|
+ pub extern "C" fn Servo_AnimationValues_Accumulate(a: RawServoAnimationValueBorrowed,
|
|
|
+ b: RawServoAnimationValueBorrowed,
|
|
|
+ count: u64)
|
|
|
+ -> RawServoAnimationValueStrong
|
|
|
+ {
|
|
|
+ let a_value = AnimationValue::as_arc(&a);
|
|
|
+ let b_value = AnimationValue::as_arc(&b);
|
|
|
+- if let Ok(value) = a_value.accumulate(b_value, count) {
|
|
|
++ if let Ok(value) = a_value.animate(b_value, Procedure::Accumulate { count }) {
|
|
|
+ Arc::new(value).into_strong()
|
|
|
+ } else {
|
|
|
+ RawServoAnimationValueStrong::null()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #[no_mangle]
|
|
|
+ pub extern "C" fn Servo_AnimationValues_GetZeroValue(
|
|
|
+@@ -458,22 +458,26 @@ pub extern "C" fn Servo_AnimationCompose
|
|
|
+ let composite_endpoint = |keyframe_value: Option<&RawOffsetArc<AnimationValue>>,
|
|
|
+ composite_op: CompositeOperation| -> Option<AnimationValue> {
|
|
|
+ match keyframe_value {
|
|
|
+ Some(keyframe_value) => {
|
|
|
+ match composite_op {
|
|
|
+ CompositeOperation::Add => {
|
|
|
+ debug_assert!(need_underlying_value,
|
|
|
+ "Should have detected we need an underlying value");
|
|
|
+- underlying_value.as_ref().unwrap().add(keyframe_value).ok()
|
|
|
++ underlying_value.as_ref().unwrap().animate(keyframe_value, Procedure::Add).ok()
|
|
|
+ },
|
|
|
+ CompositeOperation::Accumulate => {
|
|
|
+ debug_assert!(need_underlying_value,
|
|
|
+ "Should have detected we need an underlying value");
|
|
|
+- underlying_value.as_ref().unwrap().accumulate(keyframe_value, 1).ok()
|
|
|
++ underlying_value
|
|
|
++ .as_ref()
|
|
|
++ .unwrap()
|
|
|
++ .animate(keyframe_value, Procedure::Accumulate { count: 1 })
|
|
|
++ .ok()
|
|
|
+ },
|
|
|
+ _ => None,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ None => {
|
|
|
+ debug_assert!(need_underlying_value,
|
|
|
+ "Should have detected we need an underlying value");
|
|
|
+ underlying_value.clone()
|
|
|
+@@ -502,21 +506,27 @@ pub extern "C" fn Servo_AnimationCompose
|
|
|
+ };
|
|
|
+
|
|
|
+ // As with composite_endpoint, a return value of None means, "Use keyframe_value as-is."
|
|
|
+ let apply_iteration_composite = |keyframe_value: Option<&RawOffsetArc<AnimationValue>>,
|
|
|
+ composited_value: Option<AnimationValue>|
|
|
|
+ -> Option<AnimationValue> {
|
|
|
+ let count = computed_timing.mCurrentIteration;
|
|
|
+ match composited_value {
|
|
|
+- Some(endpoint) => last_value.accumulate(&endpoint, count)
|
|
|
+- .ok()
|
|
|
+- .or(Some(endpoint)),
|
|
|
+- None => last_value.accumulate(keyframe_value.unwrap(), count)
|
|
|
+- .ok(),
|
|
|
++ Some(endpoint) => {
|
|
|
++ last_value
|
|
|
++ .animate(&endpoint, Procedure::Accumulate { count })
|
|
|
++ .ok()
|
|
|
++ .or(Some(endpoint))
|
|
|
++ },
|
|
|
++ None => {
|
|
|
++ last_value
|
|
|
++ .animate(keyframe_value.unwrap(), Procedure::Accumulate { count })
|
|
|
++ .ok()
|
|
|
++ },
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ composited_from_value = apply_iteration_composite(keyframe_from_value,
|
|
|
+ composited_from_value);
|
|
|
+ composited_to_value = apply_iteration_composite(keyframe_to_value,
|
|
|
+ composited_to_value);
|
|
|
+ }
|
|
|
+@@ -530,22 +540,22 @@ pub extern "C" fn Servo_AnimationCompose
|
|
|
+ if progress < 0. {
|
|
|
+ value_map.insert(property, from_value.clone());
|
|
|
+ } else {
|
|
|
+ value_map.insert(property, to_value.clone());
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+- let position = unsafe {
|
|
|
++ let pos = unsafe {
|
|
|
+ Gecko_GetPositionInSegment(segment, progress, computed_timing.mBeforeFlag)
|
|
|
+ };
|
|
|
+- if let Ok(value) = from_value.interpolate(to_value, position) {
|
|
|
++ if let Ok(value) = from_value.animate(to_value, Procedure::Interpolate { progress: pos }) {
|
|
|
+ value_map.insert(property, value);
|
|
|
+- } else if position < 0.5 {
|
|
|
++ } else if pos < 0.5 {
|
|
|
+ value_map.insert(property, from_value.clone());
|
|
|
+ } else {
|
|
|
+ value_map.insert(property, to_value.clone());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ macro_rules! get_property_id_from_nscsspropertyid {
|
|
|
+ ($property_id: ident, $ret: expr) => {{
|
|
|
+@@ -2075,18 +2085,18 @@ pub extern "C" fn Servo_MatrixTransform_
|
|
|
+ progress: f64,
|
|
|
+ output: *mut RawGeckoGfxMatrix4x4) {
|
|
|
+ use self::MatrixTransformOperator::{Accumulate, Interpolate};
|
|
|
+ use style::properties::longhands::transform::computed_value::ComputedMatrix;
|
|
|
+
|
|
|
+ let from = ComputedMatrix::from(unsafe { from.as_ref() }.expect("not a valid 'from' matrix"));
|
|
|
+ let to = ComputedMatrix::from(unsafe { to.as_ref() }.expect("not a valid 'to' matrix"));
|
|
|
+ let result = match matrix_operator {
|
|
|
+- Interpolate => from.interpolate(&to, progress),
|
|
|
+- Accumulate => from.accumulate(&to, progress as u64),
|
|
|
++ Interpolate => from.animate(&to, Procedure::Interpolate { progress }),
|
|
|
++ Accumulate => from.animate(&to, Procedure::Accumulate { count: progress as u64 }),
|
|
|
+ };
|
|
|
+
|
|
|
+ let output = unsafe { output.as_mut() }.expect("not a valid 'output' matrix");
|
|
|
+ if let Ok(result) = result {
|
|
|
+ *output = result.into();
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+diff --git a/servo/tests/unit/style/animated_properties.rs b/servo/tests/unit/style/animated_properties.rs
|
|
|
+--- a/servo/tests/unit/style/animated_properties.rs
|
|
|
++++ b/servo/tests/unit/style/animated_properties.rs
|
|
|
+@@ -1,24 +1,25 @@
|
|
|
+ /* 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/. */
|
|
|
+
|
|
|
+ use app_units::Au;
|
|
|
+ use cssparser::RGBA;
|
|
|
+-use style::properties::animated_properties::Animatable;
|
|
|
+ use style::properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
|
|
|
+ use style::properties::longhands::transform::computed_value::T as TransformList;
|
|
|
+-use style::values::animated::ToAnimatedValue;
|
|
|
++use style::values::animated::{Animate, Procedure, ToAnimatedValue};
|
|
|
+ use style::values::computed::Percentage;
|
|
|
+
|
|
|
+ fn interpolate_rgba(from: RGBA, to: RGBA, progress: f64) -> RGBA {
|
|
|
+ let from = from.to_animated_value();
|
|
|
+ let to = to.to_animated_value();
|
|
|
+- RGBA::from_animated_value(from.interpolate(&to, progress).unwrap())
|
|
|
++ RGBA::from_animated_value(
|
|
|
++ from.animate(&to, Procedure::Interpolate { progress }).unwrap(),
|
|
|
++ )
|
|
|
+ }
|
|
|
+
|
|
|
+ // Color
|
|
|
+ #[test]
|
|
|
+ fn test_rgba_color_interepolation_preserves_transparent() {
|
|
|
+ assert_eq!(interpolate_rgba(RGBA::transparent(),
|
|
|
+ RGBA::transparent(), 0.5),
|
|
|
+ RGBA::transparent());
|
|
|
+@@ -69,88 +70,100 @@ fn test_transform_interpolation_on_trans
|
|
|
+ let from = TransformList(Some(vec![
|
|
|
+ TransformOperation::Translate(LengthOrPercentage::Length(Au(0)),
|
|
|
+ LengthOrPercentage::Length(Au(100)),
|
|
|
+ Au(25))]));
|
|
|
+ let to = TransformList(Some(vec![
|
|
|
+ TransformOperation::Translate(LengthOrPercentage::Length(Au(100)),
|
|
|
+ LengthOrPercentage::Length(Au(0)),
|
|
|
+ Au(75))]));
|
|
|
+- assert_eq!(from.interpolate(&to, 0.5).unwrap(),
|
|
|
+- TransformList(Some(vec![
|
|
|
+- TransformOperation::Translate(LengthOrPercentage::Length(Au(50)),
|
|
|
+- LengthOrPercentage::Length(Au(50)),
|
|
|
+- Au(50))])));
|
|
|
++ assert_eq!(
|
|
|
++ from.animate(&to, Procedure::Interpolate { progress: 0.5 }).unwrap(),
|
|
|
++ TransformList(Some(vec![TransformOperation::Translate(
|
|
|
++ LengthOrPercentage::Length(Au(50)),
|
|
|
++ LengthOrPercentage::Length(Au(50)),
|
|
|
++ Au(50),
|
|
|
++ )]))
|
|
|
++ );
|
|
|
+
|
|
|
+ let from = TransformList(Some(vec![TransformOperation::Translate(
|
|
|
+ LengthOrPercentage::Percentage(Percentage(0.5)),
|
|
|
+ LengthOrPercentage::Percentage(Percentage(1.0)),
|
|
|
+ Au(25),
|
|
|
+ )]));
|
|
|
+ let to = TransformList(Some(vec![
|
|
|
+ TransformOperation::Translate(LengthOrPercentage::Length(Au(100)),
|
|
|
+ LengthOrPercentage::Length(Au(50)),
|
|
|
+ Au(75))]));
|
|
|
+ assert_eq!(
|
|
|
+- from.interpolate(&to, 0.5).unwrap(),
|
|
|
++ from.animate(&to, Procedure::Interpolate { progress: 0.5 }).unwrap(),
|
|
|
+ TransformList(Some(vec![TransformOperation::Translate(
|
|
|
+ // calc(50px + 25%)
|
|
|
+ LengthOrPercentage::Calc(CalcLengthOrPercentage::new(Au(50), Some(Percentage(0.25)))),
|
|
|
+ // calc(25px + 50%)
|
|
|
+ LengthOrPercentage::Calc(CalcLengthOrPercentage::new(Au(25), Some(Percentage(0.5)))),
|
|
|
+ Au(50),
|
|
|
+ )]))
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_transform_interpolation_on_scale() {
|
|
|
+ let from = TransformList(Some(vec![TransformOperation::Scale(1.0, 2.0, 1.0)]));
|
|
|
+ let to = TransformList(Some(vec![TransformOperation::Scale(2.0, 4.0, 2.0)]));
|
|
|
+- assert_eq!(from.interpolate(&to, 0.5).unwrap(),
|
|
|
+- TransformList(Some(vec![TransformOperation::Scale(1.5, 3.0, 1.5)])));
|
|
|
++ assert_eq!(
|
|
|
++ from.animate(&to, Procedure::Interpolate { progress: 0.5 }).unwrap(),
|
|
|
++ TransformList(Some(vec![TransformOperation::Scale(1.5, 3.0, 1.5)]))
|
|
|
++ );
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_transform_interpolation_on_rotate() {
|
|
|
+ use style::values::computed::Angle;
|
|
|
+
|
|
|
+ let from = TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0,
|
|
|
+ Angle::from_radians(0.0))]));
|
|
|
+ let to = TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0,
|
|
|
+ Angle::from_radians(100.0))]));
|
|
|
+- assert_eq!(from.interpolate(&to, 0.5).unwrap(),
|
|
|
+- TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0,
|
|
|
+- Angle::from_radians(50.0))])));
|
|
|
++ assert_eq!(
|
|
|
++ from.animate(&to, Procedure::Interpolate { progress: 0.5 }).unwrap(),
|
|
|
++ TransformList(Some(vec![
|
|
|
++ TransformOperation::Rotate(0.0, 0.0, 1.0, Angle::from_radians(50.0)),
|
|
|
++ ]))
|
|
|
++ );
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_transform_interpolation_on_skew() {
|
|
|
+ use style::values::computed::Angle;
|
|
|
+
|
|
|
+ let from = TransformList(Some(vec![TransformOperation::Skew(Angle::from_radians(0.0),
|
|
|
+ Angle::from_radians(100.0))]));
|
|
|
+ let to = TransformList(Some(vec![TransformOperation::Skew(Angle::from_radians(100.0),
|
|
|
+ Angle::from_radians(0.0))]));
|
|
|
+- assert_eq!(from.interpolate(&to, 0.5).unwrap(),
|
|
|
+- TransformList(Some(vec![TransformOperation::Skew(Angle::from_radians(50.0),
|
|
|
+- Angle::from_radians(50.0))])));
|
|
|
++ assert_eq!(
|
|
|
++ from.animate(&to, Procedure::Interpolate { progress: 0.5 }).unwrap(),
|
|
|
++ TransformList(Some(vec![TransformOperation::Skew(
|
|
|
++ Angle::from_radians(50.0),
|
|
|
++ Angle::from_radians(50.0),
|
|
|
++ )]))
|
|
|
++ );
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_transform_interpolation_on_mismatched_lists() {
|
|
|
+ use style::values::computed::{Angle, LengthOrPercentage, Percentage};
|
|
|
+
|
|
|
+ let from = TransformList(Some(vec![TransformOperation::Rotate(0.0, 0.0, 1.0,
|
|
|
+ Angle::from_radians(100.0))]));
|
|
|
+ let to = TransformList(Some(vec![
|
|
|
+ TransformOperation::Translate(LengthOrPercentage::Length(Au(100)),
|
|
|
+ LengthOrPercentage::Length(Au(0)),
|
|
|
+ Au(0))]));
|
|
|
+ assert_eq!(
|
|
|
+- from.interpolate(&to, 0.5).unwrap(),
|
|
|
++ from.animate(&to, Procedure::Interpolate { progress: 0.5 }).unwrap(),
|
|
|
+ TransformList(Some(vec![TransformOperation::InterpolateMatrix {
|
|
|
+ from_list: from.clone(),
|
|
|
+ to_list: to.clone(),
|
|
|
+ progress: Percentage(0.5),
|
|
|
+ }]))
|
|
|
+ );
|
|
|
+ }
|
|
|
+diff --git a/servo/tests/unit/style/stylist.rs b/servo/tests/unit/style/stylist.rs
|
|
|
+--- a/servo/tests/unit/style/stylist.rs
|
|
|
++++ b/servo/tests/unit/style/stylist.rs
|
|
|
+@@ -1,25 +1,23 @@
|
|
|
+ /* 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/. */
|
|
|
+
|
|
|
+ use cssparser::SourceLocation;
|
|
|
+ use euclid::ScaleFactor;
|
|
|
+ use euclid::TypedSize2D;
|
|
|
+-use html5ever::LocalName;
|
|
|
+ use selectors::parser::{AncestorHashes, Selector};
|
|
|
+-use selectors::parser::LocalName as LocalNameSelector;
|
|
|
+ use servo_arc::Arc;
|
|
|
+ use servo_atoms::Atom;
|
|
|
+ use style::context::QuirksMode;
|
|
|
+ use style::media_queries::{Device, MediaType};
|
|
|
+ use style::properties::{PropertyDeclarationBlock, PropertyDeclaration};
|
|
|
+ use style::properties::{longhands, Importance};
|
|
|
+-use style::selector_map::{self, SelectorMap};
|
|
|
++use style::selector_map::SelectorMap;
|
|
|
+ use style::selector_parser::{SelectorImpl, SelectorParser};
|
|
|
+ use style::shared_lock::SharedRwLock;
|
|
|
+ use style::stylesheets::StyleRule;
|
|
|
+ use style::stylist::{Stylist, Rule};
|
|
|
+ use style::stylist::needs_revalidation_for_testing;
|
|
|
+ use style::thread_state;
|
|
|
+
|
|
|
+ /// Helper method to get some Rules from selector strings.
|