/src/mozilla-central/dom/animation/ComputedTimingFunction.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | ComputedTimingFunction::Init(const nsTimingFunction &aFunction) |
15 | 0 | { |
16 | 0 | mType = aFunction.mType; |
17 | 0 | if (nsTimingFunction::IsSplineType(mType)) { |
18 | 0 | mTimingFunction.Init(aFunction.mFunc.mX1, aFunction.mFunc.mY1, |
19 | 0 | aFunction.mFunc.mX2, aFunction.mFunc.mY2); |
20 | 0 | } else { |
21 | 0 | mStepsOrFrames = aFunction.mStepsOrFrames; |
22 | 0 | } |
23 | 0 | } |
24 | | |
25 | | static inline double |
26 | | StepTiming(uint32_t aSteps, |
27 | | double aPortion, |
28 | | ComputedTimingFunction::BeforeFlag aBeforeFlag, |
29 | | nsTimingFunction::Type aType) |
30 | 0 | { |
31 | 0 | MOZ_ASSERT(aType == nsTimingFunction::Type::StepStart || |
32 | 0 | aType == nsTimingFunction::Type::StepEnd, "invalid type"); |
33 | 0 |
|
34 | 0 | // Calculate current step using step-end behavior |
35 | 0 | int32_t step = floor(aPortion * aSteps); |
36 | 0 |
|
37 | 0 | // step-start is one step ahead |
38 | 0 | if (aType == nsTimingFunction::Type::StepStart) { |
39 | 0 | step++; |
40 | 0 | } |
41 | 0 |
|
42 | 0 | // If the "before flag" is set and we are at a transition point, |
43 | 0 | // drop back a step |
44 | 0 | if (aBeforeFlag == ComputedTimingFunction::BeforeFlag::Set && |
45 | 0 | fmod(aPortion * aSteps, 1) == 0) { |
46 | 0 | step--; |
47 | 0 | } |
48 | 0 |
|
49 | 0 | // Convert to a progress value |
50 | 0 | double result = double(step) / double(aSteps); |
51 | 0 |
|
52 | 0 | // We should not produce a result outside [0, 1] unless we have an |
53 | 0 | // input outside that range. This takes care of steps that would otherwise |
54 | 0 | // occur at boundaries. |
55 | 0 | if (result < 0.0 && aPortion >= 0.0) { |
56 | 0 | return 0.0; |
57 | 0 | } |
58 | 0 | if (result > 1.0 && aPortion <= 1.0) { |
59 | 0 | return 1.0; |
60 | 0 | } |
61 | 0 | return result; |
62 | 0 | } |
63 | | |
64 | | static inline double |
65 | | FramesTiming(uint32_t aFrames, double aPortion) |
66 | 0 | { |
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 | 0 |
|
71 | 0 | // Don't overshoot the natural range of the animation (by producing an output |
72 | 0 | // progress greater than 1.0) when we are at the exact end of its interval |
73 | 0 | // (i.e. the input progress is 1.0). |
74 | 0 | if (result > 1.0 && aPortion <= 1.0) { |
75 | 0 | return 1.0; |
76 | 0 | } |
77 | 0 | return result; |
78 | 0 | } |
79 | | |
80 | | double |
81 | | ComputedTimingFunction::GetValue( |
82 | | double aPortion, |
83 | | ComputedTimingFunction::BeforeFlag aBeforeFlag) const |
84 | 0 | { |
85 | 0 | if (HasSpline()) { |
86 | 0 | // Check for a linear curve. |
87 | 0 | // (GetSplineValue(), below, also checks this but doesn't work when |
88 | 0 | // aPortion is outside the range [0.0, 1.0]). |
89 | 0 | if (mTimingFunction.X1() == mTimingFunction.Y1() && |
90 | 0 | mTimingFunction.X2() == mTimingFunction.Y2()) { |
91 | 0 | return aPortion; |
92 | 0 | } |
93 | 0 | |
94 | 0 | // Ensure that we return 0 or 1 on both edges. |
95 | 0 | if (aPortion == 0.0) { |
96 | 0 | return 0.0; |
97 | 0 | } |
98 | 0 | if (aPortion == 1.0) { |
99 | 0 | return 1.0; |
100 | 0 | } |
101 | 0 | |
102 | 0 | // For negative values, try to extrapolate with tangent (p1 - p0) or, |
103 | 0 | // if p1 is coincident with p0, with (p2 - p0). |
104 | 0 | 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 | 0 | } |
110 | 0 | // If we can't calculate a sensible tangent, don't extrapolate at all. |
111 | 0 | return 0.0; |
112 | 0 | } |
113 | 0 | |
114 | 0 | // For values greater than 1, try to extrapolate with tangent (p2 - p3) or, |
115 | 0 | // if p2 is coincident with p3, with (p1 - p3). |
116 | 0 | 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 | 0 | } |
124 | 0 | // If we can't calculate a sensible tangent, don't extrapolate at all. |
125 | 0 | return 1.0; |
126 | 0 | } |
127 | 0 | |
128 | 0 | return mTimingFunction.GetSplineValue(aPortion); |
129 | 0 | } |
130 | 0 | |
131 | 0 | return mType == nsTimingFunction::Type::Frames |
132 | 0 | ? FramesTiming(mStepsOrFrames, aPortion) |
133 | 0 | : StepTiming(mStepsOrFrames, aPortion, aBeforeFlag, mType); |
134 | 0 | } |
135 | | |
136 | | int32_t |
137 | | ComputedTimingFunction::Compare(const ComputedTimingFunction& aRhs) const |
138 | 0 | { |
139 | 0 | if (mType != aRhs.mType) { |
140 | 0 | return int32_t(mType) - int32_t(aRhs.mType); |
141 | 0 | } |
142 | 0 | |
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 | 0 | } |
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 | 0 | } |
154 | 0 | } |
155 | 0 | |
156 | 0 | return 0; |
157 | 0 | } |
158 | | |
159 | | void |
160 | | ComputedTimingFunction::AppendToString(nsAString& aResult) const |
161 | | { |
162 | | switch (mType) { |
163 | | case nsTimingFunction::Type::CubicBezier: |
164 | | nsStyleUtil::AppendCubicBezierTimingFunction(mTimingFunction.X1(), |
165 | | mTimingFunction.Y1(), |
166 | | mTimingFunction.X2(), |
167 | | mTimingFunction.Y2(), |
168 | | aResult); |
169 | | break; |
170 | | case nsTimingFunction::Type::StepStart: |
171 | | case nsTimingFunction::Type::StepEnd: |
172 | | nsStyleUtil::AppendStepsTimingFunction(mType, mStepsOrFrames, aResult); |
173 | | break; |
174 | | case nsTimingFunction::Type::Frames: |
175 | | nsStyleUtil::AppendFramesTimingFunction(mStepsOrFrames, aResult); |
176 | | break; |
177 | | default: |
178 | | nsStyleUtil::AppendCubicBezierKeywordTimingFunction(mType, aResult); |
179 | | break; |
180 | | } |
181 | | } |
182 | | |
183 | | /* static */ int32_t |
184 | | ComputedTimingFunction::Compare(const Maybe<ComputedTimingFunction>& aLhs, |
185 | | const Maybe<ComputedTimingFunction>& aRhs) |
186 | 0 | { |
187 | 0 | // We can't use |operator<| for const Maybe<>& here because |
188 | 0 | // 'ease' is prior to 'linear' which is represented by Nothing(). |
189 | 0 | // 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 | 0 |
|
195 | 0 | if (lhsType != rhsType) { |
196 | 0 | return int32_t(lhsType) - int32_t(rhsType); |
197 | 0 | } |
198 | 0 | |
199 | 0 | // Both of them are Nothing(). |
200 | 0 | if (lhsType == nsTimingFunction::Type::Linear) { |
201 | 0 | return 0; |
202 | 0 | } |
203 | 0 | |
204 | 0 | // Other types. |
205 | 0 | return aLhs->Compare(aRhs.value()); |
206 | 0 | } |
207 | | |
208 | | } // namespace mozilla |