Bug 1773493 - Use Rust's ComputedTimingFunction for IPDL. r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D149663
This commit is contained in:
David Shin 2022-06-23 17:07:13 +00:00
parent 7f2dc4ffd4
commit aca7c3d4df
11 changed files with 99 additions and 187 deletions

View File

@ -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<uint8_t>(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<uint32_t>(timing.steps._0), timing.steps._1});
return AsVariant(StepFunc{static_cast<uint32_t>(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<int>(aFunction.mSteps), aFunction.mPos);
},
[](const StylePiecewiseLinearFunction& aFunction) {
// TODO(dshin, bug 1773493): Having to go back and forth isn't ideal.
Vector<StyleComputedLinearStop> 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<StyleComputedLinearStop>{std::move(stops)});
})};
Servo_SerializeEasing(&timing, &aResult);
}
Maybe<ComputedTimingFunction> 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<StyleStepPosition>(sf.type());
return Some(ComputedTimingFunction::Steps(sf.steps(), pos));
}
case layers::TimingFunction::TLinearFunction: {
auto lf = aTimingFunction.get_LinearFunction();
Vector<StylePiecewiseLinearFunctionEntry> 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<ComputedTimingFunction>& aComputedTimingFunction) {
if (aComputedTimingFunction.isNothing()) {
return {null_t{}};
}
return aComputedTimingFunction->mFunction.match(
[](const KeywordFunction& aFunction) {
return layers::TimingFunction{layers::CubicBezierFunction{
static_cast<float>(aFunction.mFunction.X1()),
static_cast<float>(aFunction.mFunction.Y1()),
static_cast<float>(aFunction.mFunction.X2()),
static_cast<float>(aFunction.mFunction.Y2())}};
},
[](const SMILKeySpline& aFunction) {
return layers::TimingFunction{
layers::CubicBezierFunction{static_cast<float>(aFunction.X1()),
static_cast<float>(aFunction.Y1()),
static_cast<float>(aFunction.X2()),
static_cast<float>(aFunction.Y2())}};
},
[](const StepFunc& aFunction) {
return layers::TimingFunction{
layers::StepFunction{static_cast<int>(aFunction.mSteps),
static_cast<uint8_t>(aFunction.mPos)}};
},
[](const StylePiecewiseLinearFunction& aFunction) {
nsTArray<layers::LinearStop> 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}};
});
}

View File

@ -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<ComputedTimingFunction> FromLayersTimingFunction(
const layers::TimingFunction& aTimingFunction);
static layers::TimingFunction ToLayersTimingFunction(
const Maybe<ComputedTimingFunction>& 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<KeywordFunction, SMILKeySpline, StepFunc,
StylePiecewiseLinearFunction>;
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;
};

View File

@ -460,6 +460,10 @@ AnimationStorageData AnimationHelper::ExtractAnimations(
PropertyAnimation* propertyAnimation =
currData->mAnimations.AppendElement();
Maybe<mozilla::ComputedTimingFunction> 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<dom::PlaybackDirection>(animation.direction()),
GetAdjustedFillMode(animation),
ComputedTimingFunction::FromLayersTimingFunction(
animation.easingFunction())};
std::move(easingFunction)};
propertyAnimation->mScrollTimelineOptions =
animation.scrollTimelineOptions();
nsTArray<PropertyAnimation::SegmentData>& segmentData =
propertyAnimation->mSegments;
for (const AnimationSegment& segment : animation.segments()) {
Maybe<mozilla::ComputedTimingFunction> 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<dom::CompositeOperation>(segment.startComposite()),
static_cast<dom::CompositeOperation>(segment.endComposite())});
}

View File

@ -480,8 +480,12 @@ void AnimationInfo::AddAnimationForProperty(
? static_cast<float>(aAnimation->PlaybackRate())
: std::numeric_limits<float>::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<uint8_t>(
aAnimation->GetEffect()->AsKeyframeEffect()->IterationComposite());
animation->isNotPlaying() = !aAnimation->IsPlaying();
@ -518,8 +522,12 @@ void AnimationInfo::AddAnimationForProperty(
animSegment->startComposite() =
static_cast<uint8_t>(segment.mFromComposite);
animSegment->endComposite() = static_cast<uint8_t>(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;
};

View File

@ -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 */

View File

@ -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).

View File

@ -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

View File

@ -20,6 +20,8 @@ use crate::values::generics::Optional;
ToCss,
ToResolvedValue,
ToShmem,
Serialize,
Deserialize,
)]
#[repr(C)]
pub struct LinearStop<Number, Percentage> {
@ -44,9 +46,12 @@ pub struct LinearStop<Number, Percentage> {
ToCss,
ToResolvedValue,
ToShmem,
Serialize,
Deserialize,
)]
#[value_info(ty = "TIMING_FUNCTION")]
#[repr(u8, C)]
/// cbindgen:private-default-tagged-enum-constructor=false
pub enum TimingFunction<Integer, Number, Percentage> {
/// `linear | ease | ease-in | ease-out | ease-in-out`
Keyword(TimingKeyword),
@ -86,6 +91,8 @@ pub enum TimingFunction<Integer, Number, Percentage> {
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 {

View File

@ -336,6 +336,8 @@ pub use page::PageSize;
ToCss,
ToResolvedValue,
ToShmem,
Serialize,
Deserialize,
)]
#[repr(C, u8)]
pub enum Optional<T> {

View File

@ -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.

View File

@ -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,