1 | /* |
2 | * Copyright 2008 The Android Open Source Project |
3 | * |
4 | * Use of this source code is governed by a BSD-style license that can be |
5 | * found in the LICENSE file. |
6 | */ |
7 | |
8 | #include "include/utils/SkInterpolator.h" |
9 | |
10 | #include "include/core/SkMath.h" |
11 | #include "include/private/SkFixed.h" |
12 | #include "include/private/SkMalloc.h" |
13 | #include "include/private/SkTo.h" |
14 | #include "src/core/SkTSearch.h" |
15 | |
16 | SkInterpolatorBase::SkInterpolatorBase() { |
17 | fStorage = nullptr; |
18 | fTimes = nullptr; |
19 | SkDEBUGCODE(fTimesArray = nullptr;) |
20 | } |
21 | |
22 | SkInterpolatorBase::~SkInterpolatorBase() { |
23 | if (fStorage) { |
24 | sk_free(fStorage); |
25 | } |
26 | } |
27 | |
28 | void SkInterpolatorBase::reset(int elemCount, int frameCount) { |
29 | fFlags = 0; |
30 | fElemCount = SkToU8(elemCount); |
31 | fFrameCount = SkToS16(frameCount); |
32 | fRepeat = SK_Scalar1; |
33 | if (fStorage) { |
34 | sk_free(fStorage); |
35 | fStorage = nullptr; |
36 | fTimes = nullptr; |
37 | SkDEBUGCODE(fTimesArray = nullptr); |
38 | } |
39 | } |
40 | |
41 | /* Each value[] run is formated as: |
42 | <time (in msec)> |
43 | <blend> |
44 | <data[fElemCount]> |
45 | |
46 | Totaling fElemCount+2 entries per keyframe |
47 | */ |
48 | |
49 | bool SkInterpolatorBase::getDuration(SkMSec* startTime, SkMSec* endTime) const { |
50 | if (fFrameCount == 0) { |
51 | return false; |
52 | } |
53 | |
54 | if (startTime) { |
55 | *startTime = fTimes[0].fTime; |
56 | } |
57 | if (endTime) { |
58 | *endTime = fTimes[fFrameCount - 1].fTime; |
59 | } |
60 | return true; |
61 | } |
62 | |
63 | SkScalar SkInterpolatorBase::ComputeRelativeT(SkMSec time, SkMSec prevTime, |
64 | SkMSec nextTime, const SkScalar blend[4]) { |
65 | SkASSERT(time > prevTime && time < nextTime); |
66 | |
67 | SkScalar t = (SkScalar)(time - prevTime) / (SkScalar)(nextTime - prevTime); |
68 | return blend ? |
69 | SkUnitCubicInterp(t, blend[0], blend[1], blend[2], blend[3]) : t; |
70 | } |
71 | |
72 | SkInterpolatorBase::Result SkInterpolatorBase::timeToT(SkMSec time, SkScalar* T, |
73 | int* indexPtr, bool* exactPtr) const { |
74 | SkASSERT(fFrameCount > 0); |
75 | Result result = kNormal_Result; |
76 | if (fRepeat != SK_Scalar1) { |
77 | SkMSec startTime = 0, endTime = 0; // initialize to avoid warning |
78 | this->getDuration(&startTime, &endTime); |
79 | SkMSec totalTime = endTime - startTime; |
80 | SkMSec offsetTime = time - startTime; |
81 | endTime = SkScalarFloorToInt(fRepeat * totalTime); |
82 | if (offsetTime >= endTime) { |
83 | SkScalar fraction = SkScalarFraction(fRepeat); |
84 | offsetTime = fraction == 0 && fRepeat > 0 ? totalTime : |
85 | (SkMSec) SkScalarFloorToInt(fraction * totalTime); |
86 | result = kFreezeEnd_Result; |
87 | } else { |
88 | int mirror = fFlags & kMirror; |
89 | offsetTime = offsetTime % (totalTime << mirror); |
90 | if (offsetTime > totalTime) { // can only be true if fMirror is true |
91 | offsetTime = (totalTime << 1) - offsetTime; |
92 | } |
93 | } |
94 | time = offsetTime + startTime; |
95 | } |
96 | |
97 | int index = SkTSearch<SkMSec>(&fTimes[0].fTime, fFrameCount, time, |
98 | sizeof(SkTimeCode)); |
99 | |
100 | bool exact = true; |
101 | |
102 | if (index < 0) { |
103 | index = ~index; |
104 | if (index == 0) { |
105 | result = kFreezeStart_Result; |
106 | } else if (index == fFrameCount) { |
107 | if (fFlags & kReset) { |
108 | index = 0; |
109 | } else { |
110 | index -= 1; |
111 | } |
112 | result = kFreezeEnd_Result; |
113 | } else { |
114 | exact = false; |
115 | } |
116 | } |
117 | SkASSERT(index < fFrameCount); |
118 | const SkTimeCode* nextTime = &fTimes[index]; |
119 | SkMSec nextT = nextTime[0].fTime; |
120 | if (exact) { |
121 | *T = 0; |
122 | } else { |
123 | SkMSec prevT = nextTime[-1].fTime; |
124 | *T = ComputeRelativeT(time, prevT, nextT, nextTime[-1].fBlend); |
125 | } |
126 | *indexPtr = index; |
127 | *exactPtr = exact; |
128 | return result; |
129 | } |
130 | |
131 | |
132 | SkInterpolator::SkInterpolator() { |
133 | INHERITED::reset(0, 0); |
134 | fValues = nullptr; |
135 | SkDEBUGCODE(fScalarsArray = nullptr;) |
136 | } |
137 | |
138 | SkInterpolator::SkInterpolator(int elemCount, int frameCount) { |
139 | SkASSERT(elemCount > 0); |
140 | this->reset(elemCount, frameCount); |
141 | } |
142 | |
143 | void SkInterpolator::reset(int elemCount, int frameCount) { |
144 | INHERITED::reset(elemCount, frameCount); |
145 | fStorage = sk_malloc_throw((sizeof(SkScalar) * elemCount + |
146 | sizeof(SkTimeCode)) * frameCount); |
147 | fTimes = (SkTimeCode*) fStorage; |
148 | fValues = (SkScalar*) ((char*) fStorage + sizeof(SkTimeCode) * frameCount); |
149 | #ifdef SK_DEBUG |
150 | fTimesArray = (SkTimeCode(*)[10]) fTimes; |
151 | fScalarsArray = (SkScalar(*)[10]) fValues; |
152 | #endif |
153 | } |
154 | |
155 | #define SK_Fixed1Third (SK_Fixed1/3) |
156 | #define SK_Fixed2Third (SK_Fixed1*2/3) |
157 | |
158 | static const SkScalar gIdentityBlend[4] = { |
159 | 0.33333333f, 0.33333333f, 0.66666667f, 0.66666667f |
160 | }; |
161 | |
162 | bool SkInterpolator::setKeyFrame(int index, SkMSec time, |
163 | const SkScalar values[], const SkScalar blend[4]) { |
164 | SkASSERT(values != nullptr); |
165 | |
166 | if (blend == nullptr) { |
167 | blend = gIdentityBlend; |
168 | } |
169 | |
170 | bool success = ~index == SkTSearch<SkMSec>(&fTimes->fTime, index, time, |
171 | sizeof(SkTimeCode)); |
172 | SkASSERT(success); |
173 | if (success) { |
174 | SkTimeCode* timeCode = &fTimes[index]; |
175 | timeCode->fTime = time; |
176 | memcpy(timeCode->fBlend, blend, sizeof(timeCode->fBlend)); |
177 | SkScalar* dst = &fValues[fElemCount * index]; |
178 | memcpy(dst, values, fElemCount * sizeof(SkScalar)); |
179 | } |
180 | return success; |
181 | } |
182 | |
183 | SkInterpolator::Result SkInterpolator::timeToValues(SkMSec time, |
184 | SkScalar values[]) const { |
185 | SkScalar T; |
186 | int index; |
187 | bool exact; |
188 | Result result = timeToT(time, &T, &index, &exact); |
189 | if (values) { |
190 | const SkScalar* nextSrc = &fValues[index * fElemCount]; |
191 | |
192 | if (exact) { |
193 | memcpy(values, nextSrc, fElemCount * sizeof(SkScalar)); |
194 | } else { |
195 | SkASSERT(index > 0); |
196 | |
197 | const SkScalar* prevSrc = nextSrc - fElemCount; |
198 | |
199 | for (int i = fElemCount - 1; i >= 0; --i) { |
200 | values[i] = SkScalarInterp(prevSrc[i], nextSrc[i], T); |
201 | } |
202 | } |
203 | } |
204 | return result; |
205 | } |
206 | |
207 | /////////////////////////////////////////////////////////////////////////////// |
208 | |
209 | typedef int Dot14; |
210 | #define Dot14_ONE (1 << 14) |
211 | #define Dot14_HALF (1 << 13) |
212 | |
213 | #define Dot14ToFloat(x) ((x) / 16384.f) |
214 | |
215 | static inline Dot14 Dot14Mul(Dot14 a, Dot14 b) { |
216 | return (a * b + Dot14_HALF) >> 14; |
217 | } |
218 | |
219 | static inline Dot14 eval_cubic(Dot14 t, Dot14 A, Dot14 B, Dot14 C) { |
220 | return Dot14Mul(Dot14Mul(Dot14Mul(C, t) + B, t) + A, t); |
221 | } |
222 | |
223 | static inline Dot14 pin_and_convert(SkScalar x) { |
224 | if (x <= 0) { |
225 | return 0; |
226 | } |
227 | if (x >= SK_Scalar1) { |
228 | return Dot14_ONE; |
229 | } |
230 | return SkScalarToFixed(x) >> 2; |
231 | } |
232 | |
233 | SkScalar SkUnitCubicInterp(SkScalar value, SkScalar bx, SkScalar by, |
234 | SkScalar cx, SkScalar cy) { |
235 | // pin to the unit-square, and convert to 2.14 |
236 | Dot14 x = pin_and_convert(value); |
237 | |
238 | if (x == 0) return 0; |
239 | if (x == Dot14_ONE) return SK_Scalar1; |
240 | |
241 | Dot14 b = pin_and_convert(bx); |
242 | Dot14 c = pin_and_convert(cx); |
243 | |
244 | // Now compute our coefficients from the control points |
245 | // t -> 3b |
246 | // t^2 -> 3c - 6b |
247 | // t^3 -> 3b - 3c + 1 |
248 | Dot14 A = 3*b; |
249 | Dot14 B = 3*(c - 2*b); |
250 | Dot14 C = 3*(b - c) + Dot14_ONE; |
251 | |
252 | // Now search for a t value given x |
253 | Dot14 t = Dot14_HALF; |
254 | Dot14 dt = Dot14_HALF; |
255 | for (int i = 0; i < 13; i++) { |
256 | dt >>= 1; |
257 | Dot14 guess = eval_cubic(t, A, B, C); |
258 | if (x < guess) { |
259 | t -= dt; |
260 | } else { |
261 | t += dt; |
262 | } |
263 | } |
264 | |
265 | // Now we have t, so compute the coeff for Y and evaluate |
266 | b = pin_and_convert(by); |
267 | c = pin_and_convert(cy); |
268 | A = 3*b; |
269 | B = 3*(c - 2*b); |
270 | C = 3*(b - c) + Dot14_ONE; |
271 | return SkFixedToScalar(eval_cubic(t, A, B, C) << 2); |
272 | } |
273 | |