Bug 436418, patch F: SVG/SMIL animateMotion - add support for keyPoints attribute. r=roc

This commit is contained in:
Daniel Holbert 2010-04-28 16:00:54 -07:00
parent 49730afb12
commit c240503b49
7 changed files with 122 additions and 19 deletions

View File

@ -1287,6 +1287,7 @@ GK_ATOM(by, "by")
GK_ATOM(calcMode, "calcMode")
GK_ATOM(css, "CSS")
GK_ATOM(dur, "dur")
GK_ATOM(keyPoints, "keyPoints")
GK_ATOM(keySplines, "keySplines")
GK_ATOM(keyTimes, "keyTimes")
GK_ATOM(mozAnimateMotionDummyAttr, "_mozAnimateMotionDummyAttr")

View File

@ -234,7 +234,8 @@ nsSMILAnimationFunction::ComposeResult(const nsISMILAttr& aSMILAttr,
if (NS_FAILED(rv))
return;
// GetValues may update the error state
// Check that we have the right number of keySplines and keyTimes
CheckValueListDependentAttrs(values.Length());
if (mErrorFlags != 0)
return;
@ -801,15 +802,18 @@ nsSMILAnimationFunction::GetValues(const nsISMILAttr& aSMILAttr,
}
}
// Check that we have the right number of keySplines and keyTimes
CheckKeyTimes(result.Length());
CheckKeySplines(result.Length());
result.SwapElements(aResult);
return NS_OK;
}
void
nsSMILAnimationFunction::CheckValueListDependentAttrs(PRUint32 aNumValues)
{
CheckKeyTimes(aNumValues);
CheckKeySplines(aNumValues);
}
/**
* Performs checks for the keyTimes attribute required by the SMIL spec but
* which depend on other attributes and therefore needs to be updated as
@ -1031,7 +1035,9 @@ nsSMILAnimationFunction::SetKeyTimes(const nsAString& aKeyTimes,
mKeyTimes.Clear();
aResult.SetTo(aKeyTimes);
nsresult rv = nsSMILParserUtils::ParseKeyTimes(aKeyTimes, mKeyTimes);
nsresult rv =
nsSMILParserUtils::ParseSemicolonDelimitedProgressList(aKeyTimes, PR_TRUE,
mKeyTimes);
if (NS_SUCCEEDED(rv) && mKeyTimes.Length() < 1)
rv = NS_ERROR_FAILURE;

View File

@ -326,8 +326,9 @@ protected:
virtual nsresult GetValues(const nsISMILAttr& aSMILAttr,
nsSMILValueArray& aResult);
void CheckKeyTimes(PRUint32 aNumValues);
void CheckKeySplines(PRUint32 aNumValues);
virtual void CheckValueListDependentAttrs(PRUint32 aNumValues);
void CheckKeyTimes(PRUint32 aNumValues);
void CheckKeySplines(PRUint32 aNumValues);
// When GetValues() returns a single-value array, this method indicates
// whether that single value can be understood to be a static value, to be

View File

@ -494,8 +494,9 @@ nsSMILParserUtils::ParseKeySplines(const nsAString& aSpec,
}
nsresult
nsSMILParserUtils::ParseKeyTimes(const nsAString& aSpec,
nsTArray<double>& aTimeArray)
nsSMILParserUtils::ParseSemicolonDelimitedProgressList(const nsAString& aSpec,
PRBool aNonDecreasing,
nsTArray<double>& aArray)
{
nsresult rv = NS_OK;
@ -512,12 +513,13 @@ nsSMILParserUtils::ParseKeyTimes(const nsAString& aSpec,
if (NS_FAILED(rv))
break;
if (value > 1.0 || value < 0.0 || value < previousValue) {
if (value > 1.0 || value < 0.0 ||
(aNonDecreasing && value < previousValue)) {
rv = NS_ERROR_FAILURE;
break;
}
if (!aTimeArray.AppendElement(value)) {
if (!aArray.AppendElement(value)) {
rv = NS_ERROR_OUT_OF_MEMORY;
break;
}

View File

@ -66,8 +66,10 @@ public:
static nsresult ParseKeySplines(const nsAString& aSpec,
nsTArray<double>& aSplineArray);
static nsresult ParseKeyTimes(const nsAString& aSpec,
nsTArray<double>& aTimesArray);
// Used for parsing the |keyTimes| and |keyPoints| attributes.
static nsresult ParseSemicolonDelimitedProgressList(const nsAString& aSpec,
PRBool aNonDecreasing,
nsTArray<double>& aArray);
static nsresult ParseValues(const nsAString& aSpec,
const nsISMILAnimationElement* aSrcElement,

View File

@ -81,7 +81,13 @@ SVGMotionSMILAnimationFunction::SetAttr(nsIAtom* aAttribute,
nsAttrValue& aResult,
nsresult* aParseResult)
{
if (aAttribute == nsGkAtoms::rotate) {
// Handle motion-specific attrs
if (aAttribute == nsGkAtoms::keyPoints) {
nsresult rv = SetKeyPoints(aValue, aResult);
if (aParseResult) {
*aParseResult = rv;
}
} else if (aAttribute == nsGkAtoms::rotate) {
nsresult rv = SetRotate(aValue, aResult);
if (aParseResult) {
*aParseResult = rv;
@ -103,7 +109,9 @@ SVGMotionSMILAnimationFunction::SetAttr(nsIAtom* aAttribute,
PRBool
SVGMotionSMILAnimationFunction::UnsetAttr(nsIAtom* aAttribute)
{
if (aAttribute == nsGkAtoms::rotate) {
if (aAttribute == nsGkAtoms::keyPoints) {
UnsetKeyPoints();
} else if (aAttribute == nsGkAtoms::rotate) {
UnsetRotate();
} else if (aAttribute == nsGkAtoms::by ||
aAttribute == nsGkAtoms::from ||
@ -234,14 +242,18 @@ SVGMotionSMILAnimationFunction::
PRBool
SVGMotionSMILAnimationFunction::
GenerateValuesForPathAndPoints(gfxFlattenedPath* aPath,
PRBool aIsKeyPoints,
nsTArray<double>& aPointDistances,
nsTArray<nsSMILValue>& aResult)
{
NS_ABORT_IF_FALSE(aResult.IsEmpty(), "outparam is non-empty");
// If we're using "keyPoints" as our list of input distances, then we need
// to de-normalize from the [0, 1] scale to the [0, totalPathLen] scale.
double distanceMultiplier = aIsKeyPoints ? aPath->GetLength() : 1.0;
const PRUint32 numPoints = aPointDistances.Length();
for (PRUint32 i = 0; i < numPoints; ++i) {
double curDist = aPointDistances[i];
double curDist = aPointDistances[i] * distanceMultiplier;
if (!aResult.AppendElement(
SVGMotionSMILType::ConstructSMILValue(aPath, curDist,
mRotateType, mRotateAngle))) {
@ -267,8 +279,11 @@ SVGMotionSMILAnimationFunction::GetValues(const nsISMILAttr& aSMILAttr,
}
NS_ABORT_IF_FALSE(!mPathVertices.IsEmpty(), "have a path but no vertices");
// Now: Make the actual list of nsSMILValues
PRBool success = GenerateValuesForPathAndPoints(mPath, mPathVertices,
// Now: Make the actual list of nsSMILValues (using keyPoints, if set)
PRBool isUsingKeyPoints = !mKeyPoints.IsEmpty();
PRBool success = GenerateValuesForPathAndPoints(mPath, isUsingKeyPoints,
isUsingKeyPoints ?
mKeyPoints : mPathVertices,
aResult);
if (!success) {
return NS_ERROR_OUT_OF_MEMORY;
@ -277,6 +292,75 @@ SVGMotionSMILAnimationFunction::GetValues(const nsISMILAttr& aSMILAttr,
return NS_OK;
}
void
SVGMotionSMILAnimationFunction::
CheckValueListDependentAttrs(PRUint32 aNumValues)
{
// Call superclass method.
nsSMILAnimationFunction::CheckValueListDependentAttrs(aNumValues);
// Added behavior: Do checks specific to keyPoints.
CheckKeyPoints();
}
void
SVGMotionSMILAnimationFunction::CheckKeyPoints()
{
if (!HasAttr(nsGkAtoms::keyPoints))
return;
// attribute is ignored for calcMode="paced" (even if it's got errors)
if (GetCalcMode() == CALC_PACED) {
SetKeyPointsErrorFlag(PR_FALSE);
}
if (mKeyPoints.IsEmpty()) {
// keyPoints attr is set, but array is empty => it failed preliminary checks
SetKeyPointsErrorFlag(PR_TRUE);
return;
}
// Nothing else to check -- we can catch all keyPoints errors elsewhere.
// - Formatting & range issues will be caught in SetKeyPoints, and will
// result in an empty mKeyPoints array, which will drop us into the error
// case above.
// - Number-of-entries issues will be caught in CheckKeyTimes (and flagged
// as a problem with |keyTimes|), since we use our keyPoints entries to
// populate the "values" list, and that list's count gets passed to
// CheckKeyTimes.
}
nsresult
SVGMotionSMILAnimationFunction::SetKeyPoints(const nsAString& aKeyPoints,
nsAttrValue& aResult)
{
mKeyPoints.Clear();
aResult.SetTo(aKeyPoints);
nsresult rv =
nsSMILParserUtils::ParseSemicolonDelimitedProgressList(aKeyPoints, PR_FALSE,
mKeyPoints);
if (NS_SUCCEEDED(rv) && mKeyPoints.Length() < 1)
rv = NS_ERROR_FAILURE;
if (NS_FAILED(rv)) {
mKeyPoints.Clear();
}
mHasChanged = PR_TRUE;
return NS_OK;
}
void
SVGMotionSMILAnimationFunction::UnsetKeyPoints()
{
mKeyTimes.Clear();
SetKeyPointsErrorFlag(PR_FALSE);
mHasChanged = PR_TRUE;
}
nsresult
SVGMotionSMILAnimationFunction::SetRotate(const nsAString& aRotate,
nsAttrValue& aResult)

View File

@ -75,8 +75,12 @@ protected:
NS_OVERRIDE virtual nsSMILCalcMode GetCalcMode() const;
NS_OVERRIDE virtual nsresult GetValues(const nsISMILAttr& aSMILAttr,
nsSMILValueArray& aResult);
NS_OVERRIDE virtual void CheckValueListDependentAttrs(PRUint32 aNumValues);
NS_OVERRIDE virtual PRBool TreatSingleValueAsStatic() const;
void CheckKeyPoints();
nsresult SetKeyPoints(const nsAString& aKeyPoints, nsAttrValue& aResult);
void UnsetKeyPoints();
nsresult SetRotate(const nsAString& aRotate, nsAttrValue& aResult);
void UnsetRotate();
@ -86,11 +90,14 @@ protected:
void RebuildPathAndVertices(const nsIContent* aContextElem);
void RebuildPathAndVerticesFromBasicAttrs(const nsIContent* aContextElem);
PRBool GenerateValuesForPathAndPoints(gfxFlattenedPath* aPath,
PRBool aIsKeyPoints,
nsTArray<double>& aPointDistances,
nsTArray<nsSMILValue>& aResult);
// Members
// -------
nsTArray<double> mKeyPoints; // parsed from "keyPoints" attribute.
RotateType mRotateType; // auto, auto-reverse, or explicit.
float mRotateAngle; // the angle value, if explicit.