Bug 1198708 - Part 1: Store exact timing-function type on nsTimingFunction and ComputedTimingFunction. r=birtles

Since Keyframe.easing should reflect the {transition,animation}-timing-
function value relevant to each keyframe, we'll need to store on
nsTimingFunction the specific timing function value that was used, and
copy it down into ComputedTimingFunction for
KeyframeEffectReadOnly.getFrames() to access.  This includes storing
whether the optional start/end keyword in a steps() function was
specified.
This commit is contained in:
Cameron McCormack 2015-09-29 12:20:13 +10:00
parent eac578d6c8
commit 8fcad4b3cb
10 changed files with 110 additions and 48 deletions

View File

@ -17,11 +17,12 @@ void
ComputedTimingFunction::Init(const nsTimingFunction &aFunction)
{
mType = aFunction.mType;
if (mType == nsTimingFunction::Function) {
if (nsTimingFunction::IsSplineType(mType)) {
mTimingFunction.Init(aFunction.mFunc.mX1, aFunction.mFunc.mY1,
aFunction.mFunc.mX2, aFunction.mFunc.mY2);
} else {
mSteps = aFunction.mSteps;
mStepSyntax = aFunction.mStepSyntax;
}
}
@ -36,23 +37,20 @@ StepEnd(uint32_t aSteps, double aPortion)
double
ComputedTimingFunction::GetValue(double aPortion) const
{
switch (mType) {
case nsTimingFunction::Function:
return mTimingFunction.GetSplineValue(aPortion);
case nsTimingFunction::StepStart:
// There are diagrams in the spec that seem to suggest this check
// and the bounds point should not be symmetric with StepEnd, but
// should actually step up at rather than immediately after the
// fraction points. However, we rely on rounding negative values
// up to zero, so we can't do that. And it's not clear the spec
// really meant it.
return 1.0 - StepEnd(mSteps, 1.0 - aPortion);
default:
MOZ_ASSERT(false, "bad type");
// fall through
case nsTimingFunction::StepEnd:
return StepEnd(mSteps, aPortion);
if (HasSpline()) {
return mTimingFunction.GetSplineValue(aPortion);
}
if (mType == nsTimingFunction::Type::StepStart) {
// There are diagrams in the spec that seem to suggest this check
// and the bounds point should not be symmetric with StepEnd, but
// should actually step up at rather than immediately after the
// fraction points. However, we rely on rounding negative values
// up to zero, so we can't do that. And it's not clear the spec
// really meant it.
return 1.0 - StepEnd(mSteps, 1.0 - aPortion);
}
MOZ_ASSERT(mType == nsTimingFunction::Type::StepEnd, "bad type");
return StepEnd(mSteps, aPortion);
}
// In the Web Animations model, the iteration progress can be outside the range

View File

@ -109,19 +109,23 @@ class ComputedTimingFunction
{
public:
typedef nsTimingFunction::Type Type;
typedef nsTimingFunction::StepSyntax StepSyntax;
void Init(const nsTimingFunction &aFunction);
double GetValue(double aPortion) const;
const nsSMILKeySpline* GetFunction() const {
NS_ASSERTION(mType == nsTimingFunction::Function, "Type mismatch");
NS_ASSERTION(HasSpline(), "Type mismatch");
return &mTimingFunction;
}
Type GetType() const { return mType; }
bool HasSpline() const { return nsTimingFunction::IsSplineType(mType); }
uint32_t GetSteps() const { return mSteps; }
StepSyntax GetStepSyntax() const { return mStepSyntax; }
bool operator==(const ComputedTimingFunction& aOther) const {
return mType == aOther.mType &&
(mType == nsTimingFunction::Function ?
(HasSpline() ?
mTimingFunction == aOther.mTimingFunction :
mSteps == aOther.mSteps);
(mSteps == aOther.mSteps &&
mStepSyntax == aOther.mStepSyntax));
}
bool operator!=(const ComputedTimingFunction& aOther) const {
return !(*this == aOther);
@ -131,6 +135,7 @@ private:
Type mType;
nsSMILKeySpline mTimingFunction;
uint32_t mSteps;
StepSyntax mStepSyntax;
};
struct AnimationPropertySegment

View File

@ -463,9 +463,11 @@ Layer::SetAnimations(const AnimationArray& aAnimations)
NS_ASSERTION(tf.type() == TimingFunction::TStepFunction,
"Function must be bezier or step");
StepFunction sf = tf.get_StepFunction();
nsTimingFunction::Type type = sf.type() == 1 ? nsTimingFunction::StepStart
: nsTimingFunction::StepEnd;
ctf->Init(nsTimingFunction(type, sf.steps()));
nsTimingFunction::Type type = sf.type() == 1 ?
nsTimingFunction::Type::StepStart :
nsTimingFunction::Type::StepEnd;
ctf->Init(nsTimingFunction(type, sf.steps(),
nsTimingFunction::Keyword::Explicit));
break;
}
}

View File

@ -340,13 +340,13 @@ static void AddTransformFunctions(nsCSSValueList* aList,
static TimingFunction
ToTimingFunction(const ComputedTimingFunction& aCTF)
{
if (aCTF.GetType() == nsTimingFunction::Function) {
if (aCTF.HasSpline()) {
const nsSMILKeySpline* spline = aCTF.GetFunction();
return TimingFunction(CubicBezierFunction(spline->X1(), spline->Y1(),
spline->X2(), spline->Y2()));
}
uint32_t type = aCTF.GetType() == nsTimingFunction::StepStart ? 1 : 2;
uint32_t type = aCTF.GetType() == nsTimingFunction::Type::StepStart ? 1 : 2;
return TimingFunction(StepFunction(aCTF.GetSteps(), type));
}

View File

@ -14687,12 +14687,11 @@ CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue)
return false;
}
int32_t type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END;
int32_t type = -1; // indicates an implicit end value
if (ExpectSymbol(',', true)) {
if (!GetToken(true)) {
return false;
}
type = -1;
if (mToken.mType == eCSSToken_Ident) {
if (mToken.mIdent.LowerCaseEqualsLiteral("start")) {
type = NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START;

View File

@ -1062,7 +1062,8 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult,
(array->Item(i).GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START ||
array->Item(i).GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END),
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END ||
array->Item(i).GetIntValue() == -1),
"unexpected value");
if (array->Item(i).GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START) {

View File

@ -5801,7 +5801,7 @@ nsComputedDOMStyle::AppendTimingFunction(nsDOMCSSValueList *aValueList,
nsAutoString tmp;
if (aTimingFunction.mType == nsTimingFunction::Function) {
if (aTimingFunction.HasSpline()) {
// set the value from the cubic-bezier control points
// (We could try to regenerate the keywords if we want.)
tmp.AppendLiteral("cubic-bezier(");
@ -5816,7 +5816,7 @@ nsComputedDOMStyle::AppendTimingFunction(nsDOMCSSValueList *aValueList,
} else {
tmp.AppendLiteral("steps(");
tmp.AppendInt(aTimingFunction.mSteps);
if (aTimingFunction.mType == nsTimingFunction::StepStart) {
if (aTimingFunction.mType == nsTimingFunction::Type::StepStart) {
tmp.AppendLiteral(", start)");
} else {
tmp.AppendLiteral(", end)");

View File

@ -4848,13 +4848,18 @@ ComputeTimingFunction(const nsCSSValue& aValue, nsTimingFunction& aResult)
(array->Item(1).GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START ||
array->Item(1).GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END),
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END ||
array->Item(1).GetIntValue() == -1),
"unexpected second value");
nsTimingFunction::Type type =
(array->Item(1).GetIntValue() ==
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END)
? nsTimingFunction::StepEnd : nsTimingFunction::StepStart;
aResult = nsTimingFunction(type, array->Item(0).GetIntValue());
NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START) ?
nsTimingFunction::Type::StepStart :
nsTimingFunction::Type::StepEnd;
aResult = nsTimingFunction(type, array->Item(0).GetIntValue(),
array->Item(1).GetIntValue() == -1 ?
nsTimingFunction::Keyword::Implicit :
nsTimingFunction::Keyword::Explicit);
}
break;
default:

View File

@ -2462,15 +2462,24 @@ void nsTimingFunction::AssignFromKeyword(int32_t aTimingFunctionType)
{
switch (aTimingFunctionType) {
case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START:
mType = StepStart;
mSteps = 1;
return;
case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END:
mType = StepEnd;
mType = Type::StepStart;
mStepSyntax = StepSyntax::Keyword;
mSteps = 1;
return;
default:
mType = Function;
MOZ_ASSERT_UNREACHABLE("aTimingFunctionType must be a keyword value");
// fall through
case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END:
mType = Type::StepEnd;
mStepSyntax = StepSyntax::Keyword;
mSteps = 1;
return;
case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE:
case NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR:
case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN:
case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT:
case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT:
mType = static_cast<Type>(aTimingFunctionType);
break;
}

View File

@ -1874,7 +1874,31 @@ struct nsStyleVisibility {
};
struct nsTimingFunction {
enum Type { Function, StepStart, StepEnd };
enum class Type {
Ease, // ease
Linear, // linear
EaseIn, // ease-in
EaseOut, // ease-out
EaseInOut, // ease-in-out
StepStart, // step-start and steps(..., start)
StepEnd, // step-end, steps(..., end) and steps(...)
CubicBezier, // cubic-bezier()
};
enum class StepSyntax {
Keyword, // step-start and step-end
FunctionalWithoutKeyword, // steps(...)
FunctionalWithStartKeyword, // steps(..., start)
FunctionalWithEndKeyword, // steps(..., end)
};
// Whether the timing function type is represented by a spline,
// and thus will have mFunc filled in.
static bool IsSplineType(Type aType)
{
return aType != Type::StepStart && aType != Type::StepEnd;
}
explicit nsTimingFunction(int32_t aTimingFunctionType
= NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE)
@ -1883,7 +1907,7 @@ struct nsTimingFunction {
}
nsTimingFunction(float x1, float y1, float x2, float y2)
: mType(Function)
: mType(Type::CubicBezier)
{
mFunc.mX1 = x1;
mFunc.mY1 = y1;
@ -1891,11 +1915,23 @@ struct nsTimingFunction {
mFunc.mY2 = y2;
}
nsTimingFunction(Type aType, uint32_t aSteps)
enum class Keyword { Implicit, Explicit };
nsTimingFunction(Type aType, uint32_t aSteps, Keyword aKeyword)
: mType(aType)
{
MOZ_ASSERT(mType == StepStart || mType == StepEnd, "wrong type");
MOZ_ASSERT(mType == Type::StepStart || mType == Type::StepEnd,
"wrong type");
mSteps = aSteps;
if (mType == Type::StepStart) {
MOZ_ASSERT(aKeyword == Keyword::Explicit,
"only StepEnd can have an implicit keyword");
mStepSyntax = StepSyntax::FunctionalWithStartKeyword;
} else {
mStepSyntax = aKeyword == Keyword::Explicit ?
StepSyntax::FunctionalWithEndKeyword :
StepSyntax::FunctionalWithoutKeyword;
}
}
nsTimingFunction(const nsTimingFunction& aOther)
@ -1911,7 +1947,10 @@ struct nsTimingFunction {
float mX2;
float mY2;
} mFunc;
uint32_t mSteps;
struct {
StepSyntax mStepSyntax;
uint32_t mSteps;
};
};
nsTimingFunction&
@ -1922,13 +1961,14 @@ struct nsTimingFunction {
mType = aOther.mType;
if (mType == Function) {
if (HasSpline()) {
mFunc.mX1 = aOther.mFunc.mX1;
mFunc.mY1 = aOther.mFunc.mY1;
mFunc.mX2 = aOther.mFunc.mX2;
mFunc.mY2 = aOther.mFunc.mY2;
} else {
mSteps = aOther.mSteps;
mStepSyntax = aOther.mStepSyntax;
}
return *this;
@ -1939,11 +1979,12 @@ struct nsTimingFunction {
if (mType != aOther.mType) {
return false;
}
if (mType == Function) {
if (HasSpline()) {
return mFunc.mX1 == aOther.mFunc.mX1 && mFunc.mY1 == aOther.mFunc.mY1 &&
mFunc.mX2 == aOther.mFunc.mX2 && mFunc.mY2 == aOther.mFunc.mY2;
}
return mSteps == aOther.mSteps;
return mSteps == aOther.mSteps &&
mStepSyntax == aOther.mStepSyntax;
}
bool operator!=(const nsTimingFunction& aOther) const
@ -1951,6 +1992,8 @@ struct nsTimingFunction {
return !(*this == aOther);
}
bool HasSpline() const { return IsSplineType(mType); }
private:
void AssignFromKeyword(int32_t aTimingFunctionType);
};