Exemplo n.º 1
0
PRBool
nsSMILAnimationFunction::WillReplace() const
{
  /*
   * In IsAdditive() we don't consider to-animation to be additive as it is
   * a special case that is dealt with differently in the compositing method but
   * here we return false for to animation as it builds on the underlying value
   * unless its a frozen to animation.
   */
  return !mErrorFlags && (!(IsAdditive() || IsToAnimation()) ||
                          (IsToAnimation() && mIsFrozen && !mHasChanged));
}
bool
nsSMILAnimationFunction::WillReplace() const
{
    /*
     * In IsAdditive() we don't consider to-animation to be additive as it is
     * a special case that is dealt with differently in the compositing method.
     * Here, however, we return FALSE for to-animation (i.e. it will NOT replace
     * the underlying value) as it builds on the underlying value.
     */
    return !mErrorFlags && !(IsAdditive() || IsToAnimation());
}
Exemplo n.º 3
0
/**
 * Performs checks for the keyTimes attribute required by the SMIL spec but
 * which depend on other attributes and therefore needs to be updated as
 * dependent attributes are set.
 */
void
nsSMILAnimationFunction::CheckKeyTimes(PRUint32 aNumValues)
{
  if (!HasAttr(nsGkAtoms::keyTimes))
    return;

  // attribute is ignored for calcMode = paced
  if (GetCalcMode() == CALC_PACED) {
    SetKeyTimesErrorFlag(PR_FALSE);
    return;
  }

  if (mKeyTimes.Length() < 1) {
    // keyTimes isn't set or failed preliminary checks
    SetKeyTimesErrorFlag(PR_TRUE);
    return;
  }

  // no. keyTimes == no. values
  if ((mKeyTimes.Length() != aNumValues && !IsToAnimation()) ||
      (IsToAnimation() && mKeyTimes.Length() != 2)) {
    SetKeyTimesErrorFlag(PR_TRUE);
    return;
  }

  // special handling if there is only one keyTime. The spec doesn't say what to
  // do in this case so we allow the keyTime to be either 0 or 1.
  if (mKeyTimes.Length() == 1) {
    double time = mKeyTimes[0];
    SetKeyTimesErrorFlag(!(time == 0.0 || time == 1.0));
    return;
  }

  // According to the spec, the first value should be 0 and for linear or spline
  // calcMode's the last value should be 1, but then an example is give with
  // a spline calcMode and keyTimes "0.0; 0.7". So we don't bother checking
  // the end-values here but just allow bad specs.

  SetKeyTimesErrorFlag(PR_FALSE);
}
nsresult
nsSMILAnimationFunction::AccumulateResult(const nsSMILValueArray& aValues,
        nsSMILValue& aResult)
{
    if (!IsToAnimation() && GetAccumulate() && mRepeatIteration) {
        const nsSMILValue& lastValue = aValues[aValues.Length() - 1];

        // If the target attribute type doesn't support addition, Add will
        // fail and we leave aResult untouched.
        aResult.Add(lastValue, mRepeatIteration);
    }

    return NS_OK;
}
Exemplo n.º 5
0
void
nsSMILAnimationFunction::CheckKeySplines(PRUint32 aNumValues)
{
  // attribute is ignored if calc mode is not spline
  if (GetCalcMode() != CALC_SPLINE) {
    SetKeySplinesErrorFlag(PR_FALSE);
    return;
  }

  // calc mode is spline but the attribute is not set
  if (!HasAttr(nsGkAtoms::keySplines)) {
    SetKeySplinesErrorFlag(PR_FALSE);
    return;
  }

  if (mKeySplines.Length() < 1) {
    // keyTimes isn't set or failed preliminary checks
    SetKeySplinesErrorFlag(PR_TRUE);
    return;
  }

  // ignore splines if there's only one value
  if (aNumValues == 1 && !IsToAnimation()) {
    SetKeySplinesErrorFlag(PR_FALSE);
    return;
  }

  // no. keySpline specs == no. values - 1
  PRUint32 splineSpecs = mKeySplines.Length();
  if ((splineSpecs != aNumValues - 1 && !IsToAnimation()) ||
      (IsToAnimation() && splineSpecs != 1)) {
    SetKeySplinesErrorFlag(PR_TRUE);
    return;
  }

  SetKeySplinesErrorFlag(PR_FALSE);
}
/**
 * Performs checks for the keyTimes attribute required by the SMIL spec but
 * which depend on other attributes and therefore needs to be updated as
 * dependent attributes are set.
 */
void
nsSMILAnimationFunction::CheckKeyTimes(uint32_t aNumValues)
{
    if (!HasAttr(nsGkAtoms::keyTimes))
        return;

    nsSMILCalcMode calcMode = GetCalcMode();

    // attribute is ignored for calcMode = paced
    if (calcMode == CALC_PACED) {
        SetKeyTimesErrorFlag(false);
        return;
    }

    uint32_t numKeyTimes = mKeyTimes.Length();
    if (numKeyTimes < 1) {
        // keyTimes isn't set or failed preliminary checks
        SetKeyTimesErrorFlag(true);
        return;
    }

    // no. keyTimes == no. values
    // For to-animation the number of values is considered to be 2.
    bool matchingNumOfValues =
        numKeyTimes == (IsToAnimation() ? 2 : aNumValues);
    if (!matchingNumOfValues) {
        SetKeyTimesErrorFlag(true);
        return;
    }

    // first value must be 0
    if (mKeyTimes[0] != 0.0) {
        SetKeyTimesErrorFlag(true);
        return;
    }

    // last value must be 1 for linear or spline calcModes
    if (calcMode != CALC_DISCRETE && numKeyTimes > 1 &&
            mKeyTimes[numKeyTimes - 1] != 1.0) {
        SetKeyTimesErrorFlag(true);
        return;
    }

    SetKeyTimesErrorFlag(false);
}
Exemplo n.º 7
0
nsresult
nsSMILAnimationFunction::InterpolateResult(const nsSMILValueArray& aValues,
                                           nsSMILValue& aResult,
                                           nsSMILValue& aBaseValue)
{
  nsresult rv = NS_OK;
  const nsSMILTime& dur = mSimpleDuration.GetMillis();

  // Sanity Checks
  NS_ABORT_IF_FALSE(mSampleTime >= 0.0f, "Sample time should not be negative");
  NS_ABORT_IF_FALSE(dur >= 0.0f, "Simple duration should not be negative");

  if (mSampleTime >= dur || mSampleTime < 0.0f) {
    NS_ERROR("Animation sampled outside interval");
    return NS_ERROR_FAILURE;
  }

  if ((!IsToAnimation() && aValues.Length() < 2) ||
      (IsToAnimation()  && aValues.Length() != 1)) {
    NS_ERROR("Unexpected number of values");
    return NS_ERROR_FAILURE;
  }
  // End Sanity Checks

  double fTime = double(mSampleTime);
  double fDur = double(dur);

  // Get the normalised progress through the simple duration
  double simpleProgress = (fDur > 0.0) ? fTime / fDur : 0.0;

  // Handle bad keytimes (where first != 0 and/or last != 1)
  // See http://brian.sol1.net/svg/range-for-keytimes for more info.
  if (HasAttr(nsGkAtoms::keyTimes) &&
      GetCalcMode() != CALC_PACED) {
    double first = mKeyTimes[0];
    if (first > 0.0 && simpleProgress < first) {
      if (!IsToAnimation())
        aResult = aValues[0];
      return rv;
    }
    double last = mKeyTimes[mKeyTimes.Length() - 1];
    if (last < 1.0 && simpleProgress >= last) {
      if (IsToAnimation())
        aResult = aValues[0];
      else
        aResult = aValues[aValues.Length() - 1];
      return rv;
    }
  }

  if (GetCalcMode() != CALC_DISCRETE) {
    // Get the normalised progress between adjacent values
    const nsSMILValue* from = nsnull;
    const nsSMILValue* to = nsnull;
    double intervalProgress;
    if (IsToAnimation()) {
      from = &aBaseValue;
      to = &aValues[0];
      if (GetCalcMode() == CALC_PACED) {
        // Note: key[Times/Splines/Points] are ignored for calcMode="paced"
        intervalProgress = simpleProgress;
      } else {
        ScaleSimpleProgress(simpleProgress);
        intervalProgress = simpleProgress;
        ScaleIntervalProgress(intervalProgress, 0, 1);
      }
    } else {
      if (GetCalcMode() == CALC_PACED) {
        rv = ComputePacedPosition(aValues, simpleProgress,
                                  intervalProgress, from, to);
        // Note: If the above call fails, we'll skip the "from->Interpolate"
        // call below, and we'll drop into the CALC_DISCRETE section
        // instead. (as the spec says we should, because our failure was
        // presumably due to the values being non-additive)
      } else { // GetCalcMode() == CALC_LINEAR or GetCalcMode() == CALC_SPLINE
        ScaleSimpleProgress(simpleProgress);
        PRUint32 index = (PRUint32)floor(simpleProgress *
                                         (aValues.Length() - 1));
        from = &aValues[index];
        to = &aValues[index + 1];
        intervalProgress = simpleProgress * (aValues.Length() - 1) - index;
        ScaleIntervalProgress(intervalProgress, index, aValues.Length() - 1);
      }
    }
    if (NS_SUCCEEDED(rv)) {
      NS_ABORT_IF_FALSE(from, "NULL from-value during interpolation");
      NS_ABORT_IF_FALSE(to, "NULL to-value during interpolation");
      NS_ABORT_IF_FALSE(0.0f <= intervalProgress && intervalProgress < 1.0f,
                      "Interval progress should be in the range [0, 1)");
      rv = from->Interpolate(*to, intervalProgress, aResult);
    }
  }

  // Discrete-CalcMode case
  // Note: If interpolation failed (isn't supported for this type), the SVG
  // spec says to force discrete mode.
  if (GetCalcMode() == CALC_DISCRETE || NS_FAILED(rv)) {
    if (IsToAnimation()) {
      // SMIL 3, 12.6.4: Since a to animation has only 1 value, a discrete to
      // animation will simply set the to value for the simple duration.
      aResult = aValues[0];
    } else {
      PRUint32 index = (PRUint32) floor(simpleProgress * (aValues.Length()));
      aResult = aValues[index];
    }
    rv = NS_OK;
  }
  return rv;
}
Exemplo n.º 8
0
void
nsSMILAnimationFunction::ComposeResult(const nsISMILAttr& aSMILAttr,
                                       nsSMILValue& aResult)
{
  mHasChanged = PR_FALSE;

  // Skip animations that are inactive or in error
  if (!IsActiveOrFrozen() || mErrorFlags != 0)
    return;

  // Get the animation values
  nsSMILValueArray values;
  nsresult rv = GetValues(aSMILAttr, values);
  if (NS_FAILED(rv))
    return;

  // Check that we have the right number of keySplines and keyTimes
  CheckValueListDependentAttrs(values.Length());
  if (mErrorFlags != 0)
    return;

  // If this interval is active, we must have a non-negative mSampleTime
  NS_ABORT_IF_FALSE(mSampleTime >= 0 || !mIsActive,
      "Negative sample time for active animation");
  NS_ABORT_IF_FALSE(mSimpleDuration.IsResolved() ||
      mSimpleDuration.IsIndefinite() || mLastValue,
      "Unresolved simple duration for active or frozen animation");

  nsSMILValue result(aResult.mType);

  if (mSimpleDuration.IsIndefinite() ||
      (values.Length() == 1 && TreatSingleValueAsStatic())) {
    // Indefinite duration or only one value set: Always set the first value
    result = values[0];

  } else if (mLastValue) {

    // Sampling last value
    const nsSMILValue& last = values[values.Length() - 1];
    result = last;

    // See comment in AccumulateResult: to-animation does not accumulate
    if (!IsToAnimation() && GetAccumulate() && mRepeatIteration) {
      // If the target attribute type doesn't support addition Add will
      // fail leaving result = last
      result.Add(last, mRepeatIteration);
    }

  } else if (!mFrozenValue.IsNull() && !mHasChanged) {

    // Frozen to animation
    result = mFrozenValue;

  } else {

    // Interpolation
    if (NS_FAILED(InterpolateResult(values, result, aResult)))
      return;

    if (NS_FAILED(AccumulateResult(values, result)))
      return;

    if (IsToAnimation() && mIsFrozen) {
      mFrozenValue = result;
    }
  }

  // If additive animation isn't required or isn't supported, set the value.
  if (!IsAdditive() || NS_FAILED(aResult.SandwichAdd(result))) {
    aResult.Swap(result);
    // Note: The old value of aResult is now in |result|, and it will get
    // cleaned up when |result| goes out of scope, when this function returns.
  }
}
nsresult
nsSMILAnimationFunction::InterpolateResult(const nsSMILValueArray& aValues,
        nsSMILValue& aResult,
        nsSMILValue& aBaseValue)
{
    // Sanity check animation values
    if ((!IsToAnimation() && aValues.Length() < 2) ||
            (IsToAnimation()  && aValues.Length() != 1)) {
        NS_ERROR("Unexpected number of values");
        return NS_ERROR_FAILURE;
    }

    if (IsToAnimation() && aBaseValue.IsNull()) {
        return NS_ERROR_FAILURE;
    }

    // Get the normalised progress through the simple duration.
    //
    // If we have an indefinite simple duration, just set the progress to be
    // 0 which will give us the expected behaviour of the animation being fixed at
    // its starting point.
    double simpleProgress = 0.0;

    if (mSimpleDuration.IsDefinite()) {
        nsSMILTime dur = mSimpleDuration.GetMillis();

        NS_ABORT_IF_FALSE(dur >= 0, "Simple duration should not be negative");
        NS_ABORT_IF_FALSE(mSampleTime >= 0, "Sample time should not be negative");

        if (mSampleTime >= dur || mSampleTime < 0) {
            NS_ERROR("Animation sampled outside interval");
            return NS_ERROR_FAILURE;
        }

        if (dur > 0) {
            simpleProgress = (double)mSampleTime / dur;
        } // else leave simpleProgress at 0.0 (e.g. if mSampleTime == dur == 0)
    }

    nsresult rv = NS_OK;
    nsSMILCalcMode calcMode = GetCalcMode();
    if (calcMode != CALC_DISCRETE) {
        // Get the normalised progress between adjacent values
        const nsSMILValue* from = nullptr;
        const nsSMILValue* to = nullptr;
        // Init to -1 to make sure that if we ever forget to set this, the
        // NS_ABORT_IF_FALSE that tests that intervalProgress is in range will fail.
        double intervalProgress = -1.f;
        if (IsToAnimation()) {
            from = &aBaseValue;
            to = &aValues[0];
            if (calcMode == CALC_PACED) {
                // Note: key[Times/Splines/Points] are ignored for calcMode="paced"
                intervalProgress = simpleProgress;
            } else {
                double scaledSimpleProgress =
                    ScaleSimpleProgress(simpleProgress, calcMode);
                intervalProgress = ScaleIntervalProgress(scaledSimpleProgress, 0);
            }
        } else if (calcMode == CALC_PACED) {
            rv = ComputePacedPosition(aValues, simpleProgress,
                                      intervalProgress, from, to);
            // Note: If the above call fails, we'll skip the "from->Interpolate"
            // call below, and we'll drop into the CALC_DISCRETE section
            // instead. (as the spec says we should, because our failure was
            // presumably due to the values being non-additive)
        } else { // calcMode == CALC_LINEAR or calcMode == CALC_SPLINE
            double scaledSimpleProgress =
                ScaleSimpleProgress(simpleProgress, calcMode);
            uint32_t index = (uint32_t)floor(scaledSimpleProgress *
                                             (aValues.Length() - 1));
            from = &aValues[index];
            to = &aValues[index + 1];
            intervalProgress =
                scaledSimpleProgress * (aValues.Length() - 1) - index;
            intervalProgress = ScaleIntervalProgress(intervalProgress, index);
        }

        if (NS_SUCCEEDED(rv)) {
            NS_ABORT_IF_FALSE(from, "NULL from-value during interpolation");
            NS_ABORT_IF_FALSE(to, "NULL to-value during interpolation");
            NS_ABORT_IF_FALSE(0.0f <= intervalProgress && intervalProgress < 1.0f,
                              "Interval progress should be in the range [0, 1)");
            rv = from->Interpolate(*to, intervalProgress, aResult);
        }
    }

    // Discrete-CalcMode case
    // Note: If interpolation failed (isn't supported for this type), the SVG
    // spec says to force discrete mode.
    if (calcMode == CALC_DISCRETE || NS_FAILED(rv)) {
        double scaledSimpleProgress =
            ScaleSimpleProgress(simpleProgress, CALC_DISCRETE);

        // Floating-point errors can mean that, for example, a sample time of 29s in
        // a 100s duration animation gives us a simple progress of 0.28999999999
        // instead of the 0.29 we'd expect. Normally this isn't a noticeable
        // problem, but when we have sudden jumps in animation values (such as is
        // the case here with discrete animation) we can get unexpected results.
        //
        // To counteract this, before we perform a floor() on the animation
        // progress, we add a tiny fudge factor to push us into the correct interval
        // in cases where floating-point errors might cause us to fall short.
        static const double kFloatingPointFudgeFactor = 1.0e-16;
        if (scaledSimpleProgress + kFloatingPointFudgeFactor <= 1.0) {
            scaledSimpleProgress += kFloatingPointFudgeFactor;
        }

        if (IsToAnimation()) {
            // We don't follow SMIL 3, 12.6.4, where discrete to animations
            // are the same as <set> animations.  Instead, we treat it as a
            // discrete animation with two values (the underlying value and
            // the to="" value), and honor keyTimes="" as well.
            uint32_t index = (uint32_t)floor(scaledSimpleProgress * 2);
            aResult = index == 0 ? aBaseValue : aValues[0];
        } else {
            uint32_t index = (uint32_t)floor(scaledSimpleProgress * aValues.Length());
            aResult = aValues[index];
        }
        rv = NS_OK;
    }
    return rv;
}
Exemplo n.º 10
0
void
nsSMILAnimationFunction::ComposeResult(const nsISMILAttr& aSMILAttr,
                                       nsSMILValue& aResult)
{
    mHasChanged = false;
    mPrevSampleWasSingleValueAnimation = false;
    mWasSkippedInPrevSample = false;

    // Skip animations that are inactive or in error
    if (!IsActiveOrFrozen() || mErrorFlags != 0)
        return;

    // Get the animation values
    nsSMILValueArray values;
    nsresult rv = GetValues(aSMILAttr, values);
    if (NS_FAILED(rv))
        return;

    // Check that we have the right number of keySplines and keyTimes
    CheckValueListDependentAttrs(values.Length());
    if (mErrorFlags != 0)
        return;

    // If this interval is active, we must have a non-negative mSampleTime
    NS_ABORT_IF_FALSE(mSampleTime >= 0 || !mIsActive,
                      "Negative sample time for active animation");
    NS_ABORT_IF_FALSE(mSimpleDuration.IsResolved() || mLastValue,
                      "Unresolved simple duration for active or frozen animation");

    // If we want to add but don't have a base value then just fail outright.
    // This can happen when we skipped getting the base value because there's an
    // animation function in the sandwich that should replace it but that function
    // failed unexpectedly.
    bool isAdditive = IsAdditive();
    if (isAdditive && aResult.IsNull())
        return;

    nsSMILValue result;

    if (values.Length() == 1 && !IsToAnimation()) {

        // Single-valued animation
        result = values[0];
        mPrevSampleWasSingleValueAnimation = true;

    } else if (mLastValue) {

        // Sampling last value
        const nsSMILValue& last = values[values.Length() - 1];
        result = last;

        // See comment in AccumulateResult: to-animation does not accumulate
        if (!IsToAnimation() && GetAccumulate() && mRepeatIteration) {
            // If the target attribute type doesn't support addition Add will
            // fail leaving result = last
            result.Add(last, mRepeatIteration);
        }

    } else {

        // Interpolation
        if (NS_FAILED(InterpolateResult(values, result, aResult)))
            return;

        if (NS_FAILED(AccumulateResult(values, result)))
            return;
    }

    // If additive animation isn't required or isn't supported, set the value.
    if (!isAdditive || NS_FAILED(aResult.SandwichAdd(result))) {
        aResult.Swap(result);
        // Note: The old value of aResult is now in |result|, and it will get
        // cleaned up when |result| goes out of scope, when this function returns.
    }
}