diff --git a/dom/animation/ComputedTimingFunction.cpp b/dom/animation/ComputedTimingFunction.cpp index 15ea97ae2622..1b28f302065a 100644 --- a/dom/animation/ComputedTimingFunction.cpp +++ b/dom/animation/ComputedTimingFunction.cpp @@ -13,9 +13,8 @@ namespace mozilla { ComputedTimingFunction::Function ComputedTimingFunction::ConstructFunction( - const nsTimingFunction& aFunction) { - const StyleComputedTimingFunction& timing = aFunction.mTiming; - switch (timing.tag) { + const StyleComputedTimingFunction& aFunction) { + switch (aFunction.tag) { case StyleComputedTimingFunction::Tag::Keyword: { static_assert( static_cast(StyleTimingKeyword::Linear) == 0 && @@ -32,33 +31,39 @@ ComputedTimingFunction::Function ComputedTimingFunction::ConstructFunction( {0.42f, 0.00f, 0.58f, 1.00f} // ease-in-out }; const float(&values)[4] = - timingFunctionValues[uint8_t(timing.keyword._0)]; + timingFunctionValues[uint8_t(aFunction.keyword._0)]; return AsVariant(KeywordFunction{ - timing.keyword._0, + aFunction.keyword._0, SMILKeySpline{values[0], values[1], values[2], values[3]}}); } case StyleComputedTimingFunction::Tag::CubicBezier: return AsVariant( - SMILKeySpline{timing.cubic_bezier.x1, timing.cubic_bezier.y1, - timing.cubic_bezier.x2, timing.cubic_bezier.y2}); + SMILKeySpline{aFunction.cubic_bezier.x1, aFunction.cubic_bezier.y1, + aFunction.cubic_bezier.x2, aFunction.cubic_bezier.y2}); case StyleComputedTimingFunction::Tag::Steps: - return AsVariant( - StepFunc{static_cast(timing.steps._0), timing.steps._1}); + return AsVariant(StepFunc{static_cast(aFunction.steps._0), + aFunction.steps._1}); case StyleComputedTimingFunction::Tag::LinearFunction: { StylePiecewiseLinearFunction result; - Servo_CreatePiecewiseLinearFunction(&timing.linear_function._0, &result); + Servo_CreatePiecewiseLinearFunction(&aFunction.linear_function._0, + &result); return AsVariant(result); } } MOZ_ASSERT_UNREACHABLE("Unknown timing function."); - return ConstructFunction(nsTimingFunction{StyleTimingKeyword::Linear}); + return ConstructFunction(mozilla::StyleComputedTimingFunction::Keyword( + StyleTimingKeyword::Linear)); } ComputedTimingFunction::ComputedTimingFunction( - const nsTimingFunction& aFunction) + const StyleComputedTimingFunction& aFunction) : mFunction{ConstructFunction(aFunction)} {} -static inline double StepTiming( +ComputedTimingFunction::ComputedTimingFunction( + const nsTimingFunction& aFunction) + : ComputedTimingFunction{aFunction.mTiming} {} + +double ComputedTimingFunction::StepTiming( const ComputedTimingFunction::StepFunc& aStepFunc, double aPortion, ComputedTimingFunction::BeforeFlag aBeforeFlag) { // Use the algorithm defined in the spec: @@ -185,7 +190,16 @@ double ComputedTimingFunction::GetValue( void ComputedTimingFunction::AppendToString(nsACString& aResult) const { nsTimingFunction timing; - timing.mTiming = {mFunction.match( + // This does not preserve the original input either - that is, + // linear(0 0% 50%, 1 50% 100%) -> linear(0 0%, 0 50%, 1 50%, 1 100%) + timing.mTiming = {ToStyleComputedTimingFunction(*this)}; + Servo_SerializeEasing(&timing, &aResult); +} + +StyleComputedTimingFunction +ComputedTimingFunction::ToStyleComputedTimingFunction( + const ComputedTimingFunction& aComputedTimingFunction) { + return aComputedTimingFunction.mFunction.match( [](const KeywordFunction& aFunction) { return StyleComputedTimingFunction::Keyword(aFunction.mKeyword); }, @@ -201,7 +215,6 @@ void ComputedTimingFunction::AppendToString(nsACString& aResult) const { static_cast(aFunction.mSteps), aFunction.mPos); }, [](const StylePiecewiseLinearFunction& aFunction) { - // TODO(dshin, bug 1773493): Having to go back and forth isn't ideal. Vector stops; bool reserved = stops.initCapacity(aFunction.entries.Length()); MOZ_RELEASE_ASSERT(reserved, "Failed to reserve memory"); @@ -212,75 +225,6 @@ void ComputedTimingFunction::AppendToString(nsACString& aResult) const { } return StyleComputedTimingFunction::LinearFunction( StyleOwnedSlice{std::move(stops)}); - })}; - Servo_SerializeEasing(&timing, &aResult); -} - -Maybe ComputedTimingFunction::FromLayersTimingFunction( - const layers::TimingFunction& aTimingFunction) { - switch (aTimingFunction.type()) { - case layers::TimingFunction::Tnull_t: - return Nothing(); - case layers::TimingFunction::TCubicBezierFunction: { - auto cbf = aTimingFunction.get_CubicBezierFunction(); - return Some(ComputedTimingFunction::CubicBezier(cbf.x1(), cbf.y1(), - cbf.x2(), cbf.y2())); - } - case layers::TimingFunction::TStepFunction: { - auto sf = aTimingFunction.get_StepFunction(); - StyleStepPosition pos = static_cast(sf.type()); - return Some(ComputedTimingFunction::Steps(sf.steps(), pos)); - } - case layers::TimingFunction::TLinearFunction: { - auto lf = aTimingFunction.get_LinearFunction(); - Vector stops; - bool reserved = stops.initCapacity(lf.stops().Length()); - MOZ_RELEASE_ASSERT(reserved, "Failed to reserve memory"); - for (const auto& e : lf.stops()) { - stops.infallibleAppend( - StylePiecewiseLinearFunctionEntry{e.input(), e.output()}); - } - StylePiecewiseLinearFunction result; - return Some(ComputedTimingFunction{result}); - } - case layers::TimingFunction::T__None: - break; - } - MOZ_ASSERT_UNREACHABLE("Unexpected timing function type."); - return Nothing(); -} - -layers::TimingFunction ComputedTimingFunction::ToLayersTimingFunction( - const Maybe& aComputedTimingFunction) { - if (aComputedTimingFunction.isNothing()) { - return {null_t{}}; - } - return aComputedTimingFunction->mFunction.match( - [](const KeywordFunction& aFunction) { - return layers::TimingFunction{layers::CubicBezierFunction{ - static_cast(aFunction.mFunction.X1()), - static_cast(aFunction.mFunction.Y1()), - static_cast(aFunction.mFunction.X2()), - static_cast(aFunction.mFunction.Y2())}}; - }, - [](const SMILKeySpline& aFunction) { - return layers::TimingFunction{ - layers::CubicBezierFunction{static_cast(aFunction.X1()), - static_cast(aFunction.Y1()), - static_cast(aFunction.X2()), - static_cast(aFunction.Y2())}}; - }, - [](const StepFunc& aFunction) { - return layers::TimingFunction{ - layers::StepFunction{static_cast(aFunction.mSteps), - static_cast(aFunction.mPos)}}; - }, - [](const StylePiecewiseLinearFunction& aFunction) { - nsTArray stops{aFunction.entries.Length()}; - for (const auto& e : aFunction.entries.AsSpan()) { - stops.AppendElement(layers::LinearStop{e.x, e.y}); - } - return layers::TimingFunction{layers::LinearFunction{stops}}; }); } diff --git a/dom/animation/ComputedTimingFunction.h b/dom/animation/ComputedTimingFunction.h index 480b059ca6f2..76db5c96c2db 100644 --- a/dom/animation/ComputedTimingFunction.h +++ b/dom/animation/ComputedTimingFunction.h @@ -18,38 +18,13 @@ namespace mozilla { -namespace layers { -class TimingFunction; -} - class ComputedTimingFunction { public: - struct StepFunc { - uint32_t mSteps = 1; - StyleStepPosition mPos = StyleStepPosition::End; - constexpr StepFunc() = default; - constexpr StepFunc(uint32_t aSteps, StyleStepPosition aPos) - : mSteps(aSteps), mPos(aPos){}; - bool operator==(const StepFunc& aOther) const { - return mSteps == aOther.mSteps && mPos == aOther.mPos; - } - }; - - static ComputedTimingFunction CubicBezier(double x1, double y1, double x2, - double y2) { - return ComputedTimingFunction(x1, y1, x2, y2); - } - static ComputedTimingFunction Steps(uint32_t aSteps, StyleStepPosition aPos) { - MOZ_ASSERT(aSteps > 0, "The number of steps should be 1 or more"); - return ComputedTimingFunction(aSteps, aPos); - } - - static Maybe FromLayersTimingFunction( - const layers::TimingFunction& aTimingFunction); - static layers::TimingFunction ToLayersTimingFunction( - const Maybe& aComputedTimingFunction); - explicit ComputedTimingFunction(const nsTimingFunction& aFunction); + explicit ComputedTimingFunction(const StyleComputedTimingFunction& aFunction); + + static StyleComputedTimingFunction ToStyleComputedTimingFunction( + const ComputedTimingFunction& aComputedTimingFunction); // BeforeFlag is used in step timing function. // https://drafts.csswg.org/css-easing/#before-flag @@ -62,37 +37,7 @@ class ComputedTimingFunction { return !(*this == aOther); } bool operator==(const nsTimingFunction& aOther) const { - return mFunction.match( - [&aOther](const KeywordFunction& aFunction) { - return aOther.mTiming.tag == - StyleComputedTimingFunction::Tag::Keyword && - aFunction.mKeyword == aOther.mTiming.keyword._0; - }, - [&aOther](const SMILKeySpline& aFunction) { - return aOther.mTiming.tag == - StyleComputedTimingFunction::Tag::CubicBezier && - aFunction.X1() == aOther.mTiming.cubic_bezier.x1 && - aFunction.Y1() == aOther.mTiming.cubic_bezier.y1 && - aFunction.X2() == aOther.mTiming.cubic_bezier.x2 && - aFunction.Y2() == aOther.mTiming.cubic_bezier.y2; - }, - [&aOther](const StepFunc& aFunction) { - return aOther.mTiming.tag == - StyleComputedTimingFunction::Tag::Steps && - aFunction.mSteps == uint32_t(aOther.mTiming.steps._0) && - aFunction.mPos == aOther.mTiming.steps._1; - }, - [&aOther](const StylePiecewiseLinearFunction& aFunction) { - if (aOther.mTiming.tag != - StyleComputedTimingFunction::Tag::LinearFunction) { - return false; - } - StylePiecewiseLinearFunction other; - // TODO(dshin, bug 1773493): Having to go back and forth isn't ideal. - Servo_CreatePiecewiseLinearFunction( - &aOther.mTiming.linear_function._0, &other); - return aFunction.entries == other.entries; - }); + return ToStyleComputedTimingFunction(*this) == aOther.mTiming; } bool operator!=(const nsTimingFunction& aOther) const { return !(*this == aOther); @@ -105,6 +50,17 @@ class ComputedTimingFunction { } private: + struct StepFunc { + uint32_t mSteps = 1; + StyleStepPosition mPos = StyleStepPosition::End; + constexpr StepFunc() = default; + constexpr StepFunc(uint32_t aSteps, StyleStepPosition aPos) + : mSteps(aSteps), mPos(aPos){}; + bool operator==(const StepFunc& aOther) const { + return mSteps == aOther.mSteps && mPos == aOther.mPos; + } + }; + struct KeywordFunction { KeywordFunction(mozilla::StyleTimingKeyword aKeyword, SMILKeySpline aFunction) @@ -120,14 +76,16 @@ class ComputedTimingFunction { using Function = mozilla::Variant; - static Function ConstructFunction(const nsTimingFunction& aFunction); + static Function ConstructFunction( + const StyleComputedTimingFunction& aFunction); ComputedTimingFunction(double x1, double y1, double x2, double y2) : mFunction{AsVariant(SMILKeySpline{x1, y1, x2, y2})} {} ComputedTimingFunction(uint32_t aSteps, StyleStepPosition aPos) : mFunction{AsVariant(StepFunc{aSteps, aPos})} {} explicit ComputedTimingFunction(StylePiecewiseLinearFunction aFunction) : mFunction{AsVariant(std::move(aFunction))} {} - + static double StepTiming(const StepFunc& aStepFunc, double aPortion, + BeforeFlag aBeforeFlag); Function mFunction; }; diff --git a/gfx/layers/AnimationHelper.cpp b/gfx/layers/AnimationHelper.cpp index 85167f344389..843ca044f538 100644 --- a/gfx/layers/AnimationHelper.cpp +++ b/gfx/layers/AnimationHelper.cpp @@ -460,6 +460,10 @@ AnimationStorageData AnimationHelper::ExtractAnimations( PropertyAnimation* propertyAnimation = currData->mAnimations.AppendElement(); + Maybe easingFunction; + if (animation.easingFunction().isSome()) { + easingFunction.emplace(*animation.easingFunction()); + } propertyAnimation->mOriginTime = animation.originTime(); propertyAnimation->mStartTime = animation.startTime(); @@ -477,21 +481,23 @@ AnimationStorageData AnimationHelper::ExtractAnimations( animation.iterationStart(), static_cast(animation.direction()), GetAdjustedFillMode(animation), - ComputedTimingFunction::FromLayersTimingFunction( - animation.easingFunction())}; + std::move(easingFunction)}; propertyAnimation->mScrollTimelineOptions = animation.scrollTimelineOptions(); nsTArray& segmentData = propertyAnimation->mSegments; for (const AnimationSegment& segment : animation.segments()) { + Maybe sampleFn; + if (segment.sampleFn().isSome()) { + sampleFn.emplace(*segment.sampleFn()); + } segmentData.AppendElement(PropertyAnimation::SegmentData{ AnimationValue::FromAnimatable(animation.property(), segment.startState()), AnimationValue::FromAnimatable(animation.property(), segment.endState()), - ComputedTimingFunction::FromLayersTimingFunction(segment.sampleFn()), - segment.startPortion(), segment.endPortion(), + std::move(sampleFn), segment.startPortion(), segment.endPortion(), static_cast(segment.startComposite()), static_cast(segment.endComposite())}); } diff --git a/gfx/layers/AnimationInfo.cpp b/gfx/layers/AnimationInfo.cpp index f69a54e71621..da4115c965b7 100644 --- a/gfx/layers/AnimationInfo.cpp +++ b/gfx/layers/AnimationInfo.cpp @@ -480,8 +480,12 @@ void AnimationInfo::AddAnimationForProperty( ? static_cast(aAnimation->PlaybackRate()) : std::numeric_limits::quiet_NaN(); animation->transformData() = aTransformData; - animation->easingFunction() = - ComputedTimingFunction::ToLayersTimingFunction(timing.TimingFunction()); + animation->easingFunction() = Nothing(); + if (timing.TimingFunction().isSome()) { + animation->easingFunction().emplace( + ComputedTimingFunction::ToStyleComputedTimingFunction( + *timing.TimingFunction())); + } animation->iterationComposite() = static_cast( aAnimation->GetEffect()->AsKeyframeEffect()->IterationComposite()); animation->isNotPlaying() = !aAnimation->IsPlaying(); @@ -518,8 +522,12 @@ void AnimationInfo::AddAnimationForProperty( animSegment->startComposite() = static_cast(segment.mFromComposite); animSegment->endComposite() = static_cast(segment.mToComposite); - animSegment->sampleFn() = - ComputedTimingFunction::ToLayersTimingFunction(segment.mTimingFunction); + animSegment->sampleFn() = Nothing(); + if (segment.mTimingFunction.isSome()) { + animSegment->sampleFn().emplace( + ComputedTimingFunction::ToStyleComputedTimingFunction( + *segment.mTimingFunction)); + } } } @@ -834,7 +842,7 @@ void AnimationInfo::AddNonAnimatingTransformLikePropertiesStyles( : AddAnimation(); animation->property() = aProperty; animation->baseStyle() = std::move(aBaseStyle); - animation->easingFunction() = null_t(); + animation->easingFunction() = Nothing(); animation->isNotAnimating() = true; }; diff --git a/gfx/layers/ipc/LayersMessageUtils.h b/gfx/layers/ipc/LayersMessageUtils.h index a7130d1677e8..4a0dc1fe7ae9 100644 --- a/gfx/layers/ipc/LayersMessageUtils.h +++ b/gfx/layers/ipc/LayersMessageUtils.h @@ -1009,6 +1009,7 @@ IMPL_PARAMTRAITS_BY_SERDE(StyleRotate) IMPL_PARAMTRAITS_BY_SERDE(StyleScale) IMPL_PARAMTRAITS_BY_SERDE(StyleTranslate) IMPL_PARAMTRAITS_BY_SERDE(StyleTransform) +IMPL_PARAMTRAITS_BY_SERDE(StyleComputedTimingFunction) } /* namespace IPC */ diff --git a/gfx/layers/ipc/LayersMessages.ipdlh b/gfx/layers/ipc/LayersMessages.ipdlh index 73a6b6a64840..8b41093197a3 100644 --- a/gfx/layers/ipc/LayersMessages.ipdlh +++ b/gfx/layers/ipc/LayersMessages.ipdlh @@ -68,6 +68,7 @@ using mozilla::StyleRotate from "mozilla/ServoStyleConsts.h"; using mozilla::StyleScale from "mozilla/ServoStyleConsts.h"; using mozilla::StyleTranslate from "mozilla/ServoStyleConsts.h"; using mozilla::StyleTransform from "mozilla/ServoStyleConsts.h"; +using mozilla::StyleComputedTimingFunction from "mozilla/ServoStyleConsts.h"; namespace mozilla { namespace layers { @@ -88,35 +89,6 @@ struct OpAttachAsyncCompositable { LayerHandle layer; CompositableHandle compositable; }; - -struct CubicBezierFunction { - float x1; - float y1; - float x2; - float y2; -}; - -struct StepFunction { - int steps; - uint8_t type; // Converted from StyleStepPosition. -}; - -struct LinearStop { - float input; - float output; -}; - -struct LinearFunction { - LinearStop[] stops; -}; - -union TimingFunction { - null_t; - CubicBezierFunction; - StepFunction; - LinearFunction; -}; - struct LayerColor { DeviceColor value; }; [Comparable] union Animatable { @@ -140,7 +112,7 @@ struct AnimationSegment { float endPortion; uint8_t startComposite; uint8_t endComposite; - TimingFunction sampleFn; + StyleComputedTimingFunction? sampleFn; }; [Comparable] struct MotionPathData { @@ -234,7 +206,7 @@ struct Animation { // to the playbackRate is being performed. float previousPlaybackRate; // This is used in the transformed progress calculation. - TimingFunction easingFunction; + StyleComputedTimingFunction? easingFunction; uint8_t iterationComposite; // True if the animation has a fixed current time (e.g. paused and // forward-filling animations). diff --git a/layout/style/ServoBindings.h b/layout/style/ServoBindings.h index b09f13d8176f..38f03e4c16ec 100644 --- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -86,6 +86,7 @@ BASIC_SERDE_FUNCS(StyleTransform) BASIC_SERDE_FUNCS(StyleOffsetPath) BASIC_SERDE_FUNCS(StyleOffsetRotate) BASIC_SERDE_FUNCS(StylePositionOrAuto) +BASIC_SERDE_FUNCS(StyleComputedTimingFunction) #undef BASIC_SERDE_FUNCS diff --git a/servo/components/style/values/generics/easing.rs b/servo/components/style/values/generics/easing.rs index 12e42f3c9d4b..5033d31130ea 100644 --- a/servo/components/style/values/generics/easing.rs +++ b/servo/components/style/values/generics/easing.rs @@ -20,6 +20,8 @@ use crate::values::generics::Optional; ToCss, ToResolvedValue, ToShmem, + Serialize, + Deserialize, )] #[repr(C)] pub struct LinearStop { @@ -44,9 +46,12 @@ pub struct LinearStop { ToCss, ToResolvedValue, ToShmem, + Serialize, + Deserialize, )] #[value_info(ty = "TIMING_FUNCTION")] #[repr(u8, C)] +/// cbindgen:private-default-tagged-enum-constructor=false pub enum TimingFunction { /// `linear | ease | ease-in | ease-out | ease-in-out` Keyword(TimingKeyword), @@ -86,6 +91,8 @@ pub enum TimingFunction { ToCss, ToResolvedValue, ToShmem, + Serialize, + Deserialize, )] #[repr(u8)] pub enum TimingKeyword { @@ -120,6 +127,8 @@ fn step_position_jump_enabled(_context: &ParserContext) -> bool { ToCss, ToResolvedValue, ToShmem, + Serialize, + Deserialize, )] #[repr(u8)] pub enum StepPosition { diff --git a/servo/components/style/values/generics/mod.rs b/servo/components/style/values/generics/mod.rs index 8c10085af18a..58c982f78c60 100644 --- a/servo/components/style/values/generics/mod.rs +++ b/servo/components/style/values/generics/mod.rs @@ -336,6 +336,8 @@ pub use page::PageSize; ToCss, ToResolvedValue, ToShmem, + Serialize, + Deserialize, )] #[repr(C, u8)] pub enum Optional { diff --git a/servo/ports/geckolib/cbindgen.toml b/servo/ports/geckolib/cbindgen.toml index 67d8ce2390b7..e533b961c6ac 100644 --- a/servo/ports/geckolib/cbindgen.toml +++ b/servo/ports/geckolib/cbindgen.toml @@ -103,7 +103,6 @@ include = [ "FontFamily", "FontFamilyNameSyntax", "OverflowWrap", - "TimingFunction", "OffsetPath", "OffsetRotate", "UnicodeRange", @@ -810,6 +809,12 @@ renaming_overrides_prefixing = true StyleGenericPositionOrAuto(): tag(Tag::Auto) {} """ +"TimingFunction" = """ + public: + // The implementation of IPC LayersMessages needs this to be public. + StyleTimingFunction() : tag(Tag::Keyword) { ::new (&keyword._0) (StyleTimingKeyword) (StyleTimingKeyword::Linear); } +""" + "GenericImage" = """ public: // Returns the intrinsic resolution of the image. diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs index eca29a62f475..07dcf5a1ebb0 100644 --- a/servo/ports/geckolib/glue.rs +++ b/servo/ports/geckolib/glue.rs @@ -1087,6 +1087,12 @@ impl_basic_serde_funcs!( computed::position::PositionOrAuto ); +impl_basic_serde_funcs!( + Servo_StyleComputedTimingFunction_Serialize, + Servo_StyleComputedTimingFunction_Deserialize, + computed::easing::ComputedTimingFunction +); + #[no_mangle] pub extern "C" fn Servo_SVGPathData_Normalize( input: &specified::SVGPathData,