blob: c67b135855f771a40dac21db28e88c6ec1ebf5df [file] [log] [blame]
Mike Reed74065272021-04-12 09:52:07 -04001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "SkiaInterpolator.h"
18
Kevin Lubick46691242022-11-29 19:08:17 +000019#include "include/core/SkScalar.h"
20#include "include/core/SkTypes.h"
Mike Reed74065272021-04-12 09:52:07 -040021
Kevin Lubick963ce9f2023-02-17 12:51:14 +000022#include <cstdlib>
Kevin Lubick98046982023-01-05 18:42:26 +000023#include <log/log.h>
24
Mike Reed74065272021-04-12 09:52:07 -040025typedef int Dot14;
26#define Dot14_ONE (1 << 14)
27#define Dot14_HALF (1 << 13)
28
29#define Dot14ToFloat(x) ((x) / 16384.f)
30
31static inline Dot14 Dot14Mul(Dot14 a, Dot14 b) {
32 return (a * b + Dot14_HALF) >> 14;
33}
34
35static inline Dot14 eval_cubic(Dot14 t, Dot14 A, Dot14 B, Dot14 C) {
36 return Dot14Mul(Dot14Mul(Dot14Mul(C, t) + B, t) + A, t);
37}
38
39static inline Dot14 pin_and_convert(float x) {
40 if (x <= 0) {
41 return 0;
42 }
Kevin Lubick963ce9f2023-02-17 12:51:14 +000043 if (x >= 1.0f) {
Mike Reed74065272021-04-12 09:52:07 -040044 return Dot14_ONE;
45 }
Kevin Lubick963ce9f2023-02-17 12:51:14 +000046 return static_cast<Dot14>(x * Dot14_ONE);
Mike Reed74065272021-04-12 09:52:07 -040047}
48
49static float SkUnitCubicInterp(float value, float bx, float by, float cx, float cy) {
50 // pin to the unit-square, and convert to 2.14
51 Dot14 x = pin_and_convert(value);
52
Kevin Lubick963ce9f2023-02-17 12:51:14 +000053 if (x == 0) return 0.0f;
54 if (x == Dot14_ONE) return 1.0f;
Mike Reed74065272021-04-12 09:52:07 -040055
56 Dot14 b = pin_and_convert(bx);
57 Dot14 c = pin_and_convert(cx);
58
59 // Now compute our coefficients from the control points
60 // t -> 3b
61 // t^2 -> 3c - 6b
62 // t^3 -> 3b - 3c + 1
63 Dot14 A = 3 * b;
64 Dot14 B = 3 * (c - 2 * b);
65 Dot14 C = 3 * (b - c) + Dot14_ONE;
66
67 // Now search for a t value given x
68 Dot14 t = Dot14_HALF;
69 Dot14 dt = Dot14_HALF;
70 for (int i = 0; i < 13; i++) {
71 dt >>= 1;
72 Dot14 guess = eval_cubic(t, A, B, C);
73 if (x < guess) {
74 t -= dt;
75 } else {
76 t += dt;
77 }
78 }
79
80 // Now we have t, so compute the coeff for Y and evaluate
81 b = pin_and_convert(by);
82 c = pin_and_convert(cy);
83 A = 3 * b;
84 B = 3 * (c - 2 * b);
85 C = 3 * (b - c) + Dot14_ONE;
Kevin Lubick963ce9f2023-02-17 12:51:14 +000086 return Dot14ToFloat(eval_cubic(t, A, B, C));
Mike Reed74065272021-04-12 09:52:07 -040087}
88
89///////////////////////////////////////////////////////////////////////////////////////////////////
90
91SkiaInterpolatorBase::SkiaInterpolatorBase() {
92 fStorage = nullptr;
93 fTimes = nullptr;
Mike Reed74065272021-04-12 09:52:07 -040094}
95
96SkiaInterpolatorBase::~SkiaInterpolatorBase() {
97 if (fStorage) {
Kevin Lubick98046982023-01-05 18:42:26 +000098 free(fStorage);
Mike Reed74065272021-04-12 09:52:07 -040099 }
100}
101
102void SkiaInterpolatorBase::reset(int elemCount, int frameCount) {
103 fFlags = 0;
Kevin Lubickd9ccdf62023-01-05 19:07:26 +0000104 fElemCount = static_cast<uint8_t>(elemCount);
105 fFrameCount = static_cast<int16_t>(frameCount);
Kevin Lubick963ce9f2023-02-17 12:51:14 +0000106 fRepeat = 1.0f;
Mike Reed74065272021-04-12 09:52:07 -0400107 if (fStorage) {
Kevin Lubick98046982023-01-05 18:42:26 +0000108 free(fStorage);
Mike Reed74065272021-04-12 09:52:07 -0400109 fStorage = nullptr;
110 fTimes = nullptr;
Mike Reed74065272021-04-12 09:52:07 -0400111 }
112}
113
114/* Each value[] run is formatted as:
115 <time (in msec)>
116 <blend>
117 <data[fElemCount]>
118
119 Totaling fElemCount+2 entries per keyframe
120*/
121
122bool SkiaInterpolatorBase::getDuration(SkMSec* startTime, SkMSec* endTime) const {
123 if (fFrameCount == 0) {
124 return false;
125 }
126
127 if (startTime) {
128 *startTime = fTimes[0].fTime;
129 }
130 if (endTime) {
131 *endTime = fTimes[fFrameCount - 1].fTime;
132 }
133 return true;
134}
135
136float SkiaInterpolatorBase::ComputeRelativeT(SkMSec time, SkMSec prevTime, SkMSec nextTime,
137 const float blend[4]) {
Kevin Lubick963ce9f2023-02-17 12:51:14 +0000138 LOG_FATAL_IF(time < prevTime || time > nextTime);
Mike Reed74065272021-04-12 09:52:07 -0400139
140 float t = (float)(time - prevTime) / (float)(nextTime - prevTime);
141 return blend ? SkUnitCubicInterp(t, blend[0], blend[1], blend[2], blend[3]) : t;
142}
143
Kevin Lubick963ce9f2023-02-17 12:51:14 +0000144// Returns the index of where the item is or the bit not of the index
145// where the item should go in order to keep arr sorted in ascending order.
146int SkiaInterpolatorBase::binarySearch(const SkTimeCode* arr, int count, SkMSec target) {
147 if (count <= 0) {
148 return ~0;
149 }
150
151 int lo = 0;
152 int hi = count - 1;
153
154 while (lo < hi) {
155 int mid = (hi + lo) / 2;
156 SkMSec elem = arr[mid].fTime;
157 if (elem == target) {
158 return mid;
159 } else if (elem < target) {
160 lo = mid + 1;
161 } else {
162 hi = mid;
163 }
164 }
165 // Check to see if target is greater or less than where we stopped
166 if (target < arr[lo].fTime) {
167 return ~lo;
168 }
169 // e.g. it should go at the end.
170 return ~(lo + 1);
171}
172
Mike Reed74065272021-04-12 09:52:07 -0400173SkiaInterpolatorBase::Result SkiaInterpolatorBase::timeToT(SkMSec time, float* T, int* indexPtr,
174 bool* exactPtr) const {
Kevin Lubick963ce9f2023-02-17 12:51:14 +0000175 LOG_FATAL_IF(fFrameCount <= 0);
Mike Reed74065272021-04-12 09:52:07 -0400176 Result result = kNormal_Result;
Kevin Lubick963ce9f2023-02-17 12:51:14 +0000177 if (fRepeat != 1.0f) {
Mike Reed74065272021-04-12 09:52:07 -0400178 SkMSec startTime = 0, endTime = 0; // initialize to avoid warning
179 this->getDuration(&startTime, &endTime);
180 SkMSec totalTime = endTime - startTime;
181 SkMSec offsetTime = time - startTime;
182 endTime = SkScalarFloorToInt(fRepeat * totalTime);
183 if (offsetTime >= endTime) {
184 float fraction = SkScalarFraction(fRepeat);
185 offsetTime = fraction == 0 && fRepeat > 0
186 ? totalTime
187 : (SkMSec)SkScalarFloorToInt(fraction * totalTime);
188 result = kFreezeEnd_Result;
189 } else {
190 int mirror = fFlags & kMirror;
191 offsetTime = offsetTime % (totalTime << mirror);
192 if (offsetTime > totalTime) { // can only be true if fMirror is true
193 offsetTime = (totalTime << 1) - offsetTime;
194 }
195 }
196 time = offsetTime + startTime;
197 }
198
Kevin Lubick963ce9f2023-02-17 12:51:14 +0000199 int index = SkiaInterpolatorBase::binarySearch(fTimes, fFrameCount, time);
Mike Reed74065272021-04-12 09:52:07 -0400200 bool exact = true;
Mike Reed74065272021-04-12 09:52:07 -0400201 if (index < 0) {
202 index = ~index;
203 if (index == 0) {
204 result = kFreezeStart_Result;
205 } else if (index == fFrameCount) {
206 if (fFlags & kReset) {
207 index = 0;
208 } else {
209 index -= 1;
210 }
211 result = kFreezeEnd_Result;
212 } else {
Kevin Lubick963ce9f2023-02-17 12:51:14 +0000213 // Need to interpolate between two frames.
Mike Reed74065272021-04-12 09:52:07 -0400214 exact = false;
215 }
216 }
Kevin Lubick963ce9f2023-02-17 12:51:14 +0000217 LOG_FATAL_IF(index >= fFrameCount);
Mike Reed74065272021-04-12 09:52:07 -0400218 const SkTimeCode* nextTime = &fTimes[index];
219 SkMSec nextT = nextTime[0].fTime;
220 if (exact) {
221 *T = 0;
222 } else {
223 SkMSec prevT = nextTime[-1].fTime;
224 *T = ComputeRelativeT(time, prevT, nextT, nextTime[-1].fBlend);
225 }
226 *indexPtr = index;
227 *exactPtr = exact;
228 return result;
229}
230
231SkiaInterpolator::SkiaInterpolator() {
232 INHERITED::reset(0, 0);
233 fValues = nullptr;
Mike Reed74065272021-04-12 09:52:07 -0400234}
235
236SkiaInterpolator::SkiaInterpolator(int elemCount, int frameCount) {
Kevin Lubick963ce9f2023-02-17 12:51:14 +0000237 LOG_FATAL_IF(elemCount <= 0);
Mike Reed74065272021-04-12 09:52:07 -0400238 this->reset(elemCount, frameCount);
239}
240
241void SkiaInterpolator::reset(int elemCount, int frameCount) {
242 INHERITED::reset(elemCount, frameCount);
Kevin Lubick98046982023-01-05 18:42:26 +0000243 size_t numBytes = (sizeof(float) * elemCount + sizeof(SkTimeCode)) * frameCount;
244 fStorage = malloc(numBytes);
245 LOG_ALWAYS_FATAL_IF(!fStorage, "Failed to allocate %zu bytes in %s",
246 numBytes, __func__);
Mike Reed74065272021-04-12 09:52:07 -0400247 fTimes = (SkTimeCode*)fStorage;
248 fValues = (float*)((char*)fStorage + sizeof(SkTimeCode) * frameCount);
Mike Reed74065272021-04-12 09:52:07 -0400249}
250
Mike Reed74065272021-04-12 09:52:07 -0400251static const float gIdentityBlend[4] = {0.33333333f, 0.33333333f, 0.66666667f, 0.66666667f};
252
253bool SkiaInterpolator::setKeyFrame(int index, SkMSec time, const float values[],
254 const float blend[4]) {
Kevin Lubick963ce9f2023-02-17 12:51:14 +0000255 LOG_FATAL_IF(values == nullptr);
Mike Reed74065272021-04-12 09:52:07 -0400256
257 if (blend == nullptr) {
258 blend = gIdentityBlend;
259 }
260
Kevin Lubick963ce9f2023-02-17 12:51:14 +0000261 // Verify the time should go after all the frames before index
262 bool success = ~index == SkiaInterpolatorBase::binarySearch(fTimes, index, time);
263 LOG_FATAL_IF(!success);
Mike Reed74065272021-04-12 09:52:07 -0400264 if (success) {
265 SkTimeCode* timeCode = &fTimes[index];
266 timeCode->fTime = time;
267 memcpy(timeCode->fBlend, blend, sizeof(timeCode->fBlend));
268 float* dst = &fValues[fElemCount * index];
269 memcpy(dst, values, fElemCount * sizeof(float));
270 }
271 return success;
272}
273
274SkiaInterpolator::Result SkiaInterpolator::timeToValues(SkMSec time, float values[]) const {
275 float T;
276 int index;
277 bool exact;
278 Result result = timeToT(time, &T, &index, &exact);
279 if (values) {
280 const float* nextSrc = &fValues[index * fElemCount];
281
282 if (exact) {
283 memcpy(values, nextSrc, fElemCount * sizeof(float));
284 } else {
Kevin Lubick963ce9f2023-02-17 12:51:14 +0000285 LOG_FATAL_IF(index <= 0);
Mike Reed74065272021-04-12 09:52:07 -0400286
287 const float* prevSrc = nextSrc - fElemCount;
288
289 for (int i = fElemCount - 1; i >= 0; --i) {
290 values[i] = SkScalarInterp(prevSrc[i], nextSrc[i], T);
291 }
292 }
293 }
294 return result;
295}