LCOV - code coverage report
Current view: top level - dom/animation - ComputedTimingFunction.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 15 93 16.1 %
Date: 2017-07-14 16:53:18 Functions: 2 7 28.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       5             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "ComputedTimingFunction.h"
       8             : #include "nsAlgorithm.h" // For clamped()
       9             : #include "nsStyleUtil.h"
      10             : 
      11             : namespace mozilla {
      12             : 
      13             : void
      14           4 : ComputedTimingFunction::Init(const nsTimingFunction &aFunction)
      15             : {
      16           4 :   mType = aFunction.mType;
      17           4 :   if (nsTimingFunction::IsSplineType(mType)) {
      18           8 :     mTimingFunction.Init(aFunction.mFunc.mX1, aFunction.mFunc.mY1,
      19          12 :                          aFunction.mFunc.mX2, aFunction.mFunc.mY2);
      20             :   } else {
      21           0 :     mStepsOrFrames = aFunction.mStepsOrFrames;
      22             :   }
      23           4 : }
      24             : 
      25             : static inline double
      26           0 : StepTiming(uint32_t aSteps,
      27             :            double aPortion,
      28             :            ComputedTimingFunction::BeforeFlag aBeforeFlag,
      29             :            nsTimingFunction::Type aType)
      30             : {
      31           0 :   MOZ_ASSERT(aType == nsTimingFunction::Type::StepStart ||
      32             :              aType == nsTimingFunction::Type::StepEnd, "invalid type");
      33             : 
      34             :   // Calculate current step using step-end behavior
      35           0 :   int32_t step = floor(aPortion * aSteps);
      36             : 
      37             :   // step-start is one step ahead
      38           0 :   if (aType == nsTimingFunction::Type::StepStart) {
      39           0 :     step++;
      40             :   }
      41             : 
      42             :   // If the "before flag" is set and we are at a transition point,
      43             :   // drop back a step
      44           0 :   if (aBeforeFlag == ComputedTimingFunction::BeforeFlag::Set &&
      45           0 :       fmod(aPortion * aSteps, 1) == 0) {
      46           0 :     step--;
      47             :   }
      48             : 
      49             :   // Convert to a progress value
      50           0 :   double result = double(step) / double(aSteps);
      51             : 
      52             :   // We should not produce a result outside [0, 1] unless we have an
      53             :   // input outside that range. This takes care of steps that would otherwise
      54             :   // occur at boundaries.
      55           0 :   if (result < 0.0 && aPortion >= 0.0) {
      56           0 :     return 0.0;
      57             :   }
      58           0 :   if (result > 1.0 && aPortion <= 1.0) {
      59           0 :     return 1.0;
      60             :   }
      61           0 :   return result;
      62             : }
      63             : 
      64             : static inline double
      65           0 : FramesTiming(uint32_t aFrames, double aPortion)
      66             : {
      67           0 :   MOZ_ASSERT(aFrames > 1, "the number of frames must be greater than 1");
      68           0 :   int32_t currentFrame = floor(aPortion * aFrames);
      69           0 :   double result = double(currentFrame) / double(aFrames - 1);
      70             : 
      71             :   // Don't overshoot the natural range of the animation (by producing an output
      72             :   // progress greater than 1.0) when we are at the exact end of its interval
      73             :   // (i.e. the input progress is 1.0).
      74           0 :   if (result > 1.0 && aPortion <= 1.0) {
      75           0 :     return 1.0;
      76             :   }
      77           0 :   return result;
      78             : }
      79             : 
      80             : double
      81           4 : ComputedTimingFunction::GetValue(
      82             :     double aPortion,
      83             :     ComputedTimingFunction::BeforeFlag aBeforeFlag) const
      84             : {
      85           4 :   if (HasSpline()) {
      86             :     // Check for a linear curve.
      87             :     // (GetSplineValue(), below, also checks this but doesn't work when
      88             :     // aPortion is outside the range [0.0, 1.0]).
      89           4 :     if (mTimingFunction.X1() == mTimingFunction.Y1() &&
      90           0 :         mTimingFunction.X2() == mTimingFunction.Y2()) {
      91           0 :       return aPortion;
      92             :     }
      93             : 
      94             :     // Ensure that we return 0 or 1 on both edges.
      95           4 :     if (aPortion == 0.0) {
      96           2 :       return 0.0;
      97             :     }
      98           2 :     if (aPortion == 1.0) {
      99           0 :       return 1.0;
     100             :     }
     101             : 
     102             :     // For negative values, try to extrapolate with tangent (p1 - p0) or,
     103             :     // if p1 is coincident with p0, with (p2 - p0).
     104           2 :     if (aPortion < 0.0) {
     105           0 :       if (mTimingFunction.X1() > 0.0) {
     106           0 :         return aPortion * mTimingFunction.Y1() / mTimingFunction.X1();
     107           0 :       } else if (mTimingFunction.Y1() == 0 && mTimingFunction.X2() > 0.0) {
     108           0 :         return aPortion * mTimingFunction.Y2() / mTimingFunction.X2();
     109             :       }
     110             :       // If we can't calculate a sensible tangent, don't extrapolate at all.
     111           0 :       return 0.0;
     112             :     }
     113             : 
     114             :     // For values greater than 1, try to extrapolate with tangent (p2 - p3) or,
     115             :     // if p2 is coincident with p3, with (p1 - p3).
     116           2 :     if (aPortion > 1.0) {
     117           0 :       if (mTimingFunction.X2() < 1.0) {
     118           0 :         return 1.0 + (aPortion - 1.0) *
     119           0 :           (mTimingFunction.Y2() - 1) / (mTimingFunction.X2() - 1);
     120           0 :       } else if (mTimingFunction.Y2() == 1 && mTimingFunction.X1() < 1.0) {
     121           0 :         return 1.0 + (aPortion - 1.0) *
     122           0 :           (mTimingFunction.Y1() - 1) / (mTimingFunction.X1() - 1);
     123             :       }
     124             :       // If we can't calculate a sensible tangent, don't extrapolate at all.
     125           0 :       return 1.0;
     126             :     }
     127             : 
     128           2 :     return mTimingFunction.GetSplineValue(aPortion);
     129             :   }
     130             : 
     131           0 :   return mType == nsTimingFunction::Type::Frames
     132           0 :          ? FramesTiming(mStepsOrFrames, aPortion)
     133           0 :          : StepTiming(mStepsOrFrames, aPortion, aBeforeFlag, mType);
     134             : }
     135             : 
     136             : int32_t
     137           0 : ComputedTimingFunction::Compare(const ComputedTimingFunction& aRhs) const
     138             : {
     139           0 :   if (mType != aRhs.mType) {
     140           0 :     return int32_t(mType) - int32_t(aRhs.mType);
     141             :   }
     142             : 
     143           0 :   if (mType == nsTimingFunction::Type::CubicBezier) {
     144           0 :     int32_t order = mTimingFunction.Compare(aRhs.mTimingFunction);
     145           0 :     if (order != 0) {
     146           0 :       return order;
     147             :     }
     148           0 :   } else if (mType == nsTimingFunction::Type::StepStart ||
     149           0 :              mType == nsTimingFunction::Type::StepEnd ||
     150           0 :              mType == nsTimingFunction::Type::Frames) {
     151           0 :     if (mStepsOrFrames != aRhs.mStepsOrFrames) {
     152           0 :       return int32_t(mStepsOrFrames) - int32_t(aRhs.mStepsOrFrames);
     153             :     }
     154             :   }
     155             : 
     156           0 :   return 0;
     157             : }
     158             : 
     159             : void
     160           0 : ComputedTimingFunction::AppendToString(nsAString& aResult) const
     161             : {
     162           0 :   switch (mType) {
     163             :     case nsTimingFunction::Type::CubicBezier:
     164           0 :       nsStyleUtil::AppendCubicBezierTimingFunction(mTimingFunction.X1(),
     165           0 :                                                    mTimingFunction.Y1(),
     166           0 :                                                    mTimingFunction.X2(),
     167           0 :                                                    mTimingFunction.Y2(),
     168           0 :                                                    aResult);
     169           0 :       break;
     170             :     case nsTimingFunction::Type::StepStart:
     171             :     case nsTimingFunction::Type::StepEnd:
     172           0 :       nsStyleUtil::AppendStepsTimingFunction(mType, mStepsOrFrames, aResult);
     173           0 :       break;
     174             :     case nsTimingFunction::Type::Frames:
     175           0 :       nsStyleUtil::AppendFramesTimingFunction(mStepsOrFrames, aResult);
     176           0 :       break;
     177             :     default:
     178           0 :       nsStyleUtil::AppendCubicBezierKeywordTimingFunction(mType, aResult);
     179           0 :       break;
     180             :   }
     181           0 : }
     182             : 
     183             : /* static */ int32_t
     184           0 : ComputedTimingFunction::Compare(const Maybe<ComputedTimingFunction>& aLhs,
     185             :                                 const Maybe<ComputedTimingFunction>& aRhs)
     186             : {
     187             :   // We can't use |operator<| for const Maybe<>& here because
     188             :   // 'ease' is prior to 'linear' which is represented by Nothing().
     189             :   // So we have to convert Nothing() as 'linear' and check it first.
     190           0 :   nsTimingFunction::Type lhsType = aLhs.isNothing() ?
     191           0 :     nsTimingFunction::Type::Linear : aLhs->GetType();
     192           0 :   nsTimingFunction::Type rhsType = aRhs.isNothing() ?
     193           0 :     nsTimingFunction::Type::Linear : aRhs->GetType();
     194             : 
     195           0 :   if (lhsType != rhsType) {
     196           0 :     return int32_t(lhsType) - int32_t(rhsType);
     197             :   }
     198             : 
     199             :   // Both of them are Nothing().
     200           0 :   if (lhsType == nsTimingFunction::Type::Linear) {
     201           0 :     return 0;
     202             :   }
     203             : 
     204             :   // Other types.
     205           0 :   return aLhs->Compare(aRhs.value());
     206             : }
     207             : 
     208             : } // namespace mozilla

Generated by: LCOV version 1.13