xref: /aosp_15_r20/frameworks/base/libs/hwui/SkiaInterpolator.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
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 
19 #include "include/core/SkScalar.h"
20 #include "include/core/SkTypes.h"
21 
22 #include <cstdlib>
23 #include <cstring>
24 #include <log/log.h>
25 
26 typedef int Dot14;
27 #define Dot14_ONE (1 << 14)
28 #define Dot14_HALF (1 << 13)
29 
30 #define Dot14ToFloat(x) ((x) / 16384.f)
31 
Dot14Mul(Dot14 a,Dot14 b)32 static inline Dot14 Dot14Mul(Dot14 a, Dot14 b) {
33     return (a * b + Dot14_HALF) >> 14;
34 }
35 
eval_cubic(Dot14 t,Dot14 A,Dot14 B,Dot14 C)36 static inline Dot14 eval_cubic(Dot14 t, Dot14 A, Dot14 B, Dot14 C) {
37     return Dot14Mul(Dot14Mul(Dot14Mul(C, t) + B, t) + A, t);
38 }
39 
pin_and_convert(float x)40 static inline Dot14 pin_and_convert(float x) {
41     if (x <= 0) {
42         return 0;
43     }
44     if (x >= 1.0f) {
45         return Dot14_ONE;
46     }
47     return static_cast<Dot14>(x * Dot14_ONE);
48 }
49 
50 using MSec = uint32_t;  // millisecond duration
51 
SkUnitCubicInterp(float value,float bx,float by,float cx,float cy)52 static float SkUnitCubicInterp(float value, float bx, float by, float cx, float cy) {
53     // pin to the unit-square, and convert to 2.14
54     Dot14 x = pin_and_convert(value);
55 
56     if (x == 0) return 0.0f;
57     if (x == Dot14_ONE) return 1.0f;
58 
59     Dot14 b = pin_and_convert(bx);
60     Dot14 c = pin_and_convert(cx);
61 
62     // Now compute our coefficients from the control points
63     //  t   -> 3b
64     //  t^2 -> 3c - 6b
65     //  t^3 -> 3b - 3c + 1
66     Dot14 A = 3 * b;
67     Dot14 B = 3 * (c - 2 * b);
68     Dot14 C = 3 * (b - c) + Dot14_ONE;
69 
70     // Now search for a t value given x
71     Dot14 t = Dot14_HALF;
72     Dot14 dt = Dot14_HALF;
73     for (int i = 0; i < 13; i++) {
74         dt >>= 1;
75         Dot14 guess = eval_cubic(t, A, B, C);
76         if (x < guess) {
77             t -= dt;
78         } else {
79             t += dt;
80         }
81     }
82 
83     // Now we have t, so compute the coeff for Y and evaluate
84     b = pin_and_convert(by);
85     c = pin_and_convert(cy);
86     A = 3 * b;
87     B = 3 * (c - 2 * b);
88     C = 3 * (b - c) + Dot14_ONE;
89     return Dot14ToFloat(eval_cubic(t, A, B, C));
90 }
91 
92 ///////////////////////////////////////////////////////////////////////////////////////////////////
93 
SkiaInterpolatorBase()94 SkiaInterpolatorBase::SkiaInterpolatorBase() {
95     fStorage = nullptr;
96     fTimes = nullptr;
97 }
98 
~SkiaInterpolatorBase()99 SkiaInterpolatorBase::~SkiaInterpolatorBase() {
100     if (fStorage) {
101         free(fStorage);
102     }
103 }
104 
reset(int elemCount,int frameCount)105 void SkiaInterpolatorBase::reset(int elemCount, int frameCount) {
106     fFlags = 0;
107     fElemCount = static_cast<uint8_t>(elemCount);
108     fFrameCount = static_cast<int16_t>(frameCount);
109     fRepeat = 1.0f;
110     if (fStorage) {
111         free(fStorage);
112         fStorage = nullptr;
113         fTimes = nullptr;
114     }
115 }
116 
117 /*  Each value[] run is formatted as:
118         <time (in msec)>
119         <blend>
120         <data[fElemCount]>
121 
122     Totaling fElemCount+2 entries per keyframe
123 */
124 
getDuration(MSec * startTime,MSec * endTime) const125 bool SkiaInterpolatorBase::getDuration(MSec* startTime, MSec* endTime) const {
126     if (fFrameCount == 0) {
127         return false;
128     }
129 
130     if (startTime) {
131         *startTime = fTimes[0].fTime;
132     }
133     if (endTime) {
134         *endTime = fTimes[fFrameCount - 1].fTime;
135     }
136     return true;
137 }
138 
ComputeRelativeT(MSec time,MSec prevTime,MSec nextTime,const float blend[4])139 float SkiaInterpolatorBase::ComputeRelativeT(MSec time, MSec prevTime, MSec nextTime,
140                                              const float blend[4]) {
141     LOG_FATAL_IF(time < prevTime || time > nextTime);
142 
143     float t = (float)(time - prevTime) / (float)(nextTime - prevTime);
144     return blend ? SkUnitCubicInterp(t, blend[0], blend[1], blend[2], blend[3]) : t;
145 }
146 
147 // Returns the index of where the item is or the bit not of the index
148 // where the item should go in order to keep arr sorted in ascending order.
binarySearch(const SkTimeCode * arr,int count,MSec target)149 int SkiaInterpolatorBase::binarySearch(const SkTimeCode* arr, int count, MSec target) {
150     if (count <= 0) {
151         return ~0;
152     }
153 
154     int lo = 0;
155     int hi = count - 1;
156 
157     while (lo < hi) {
158         int mid = (hi + lo) / 2;
159         MSec elem = arr[mid].fTime;
160         if (elem == target) {
161             return mid;
162         } else if (elem < target) {
163             lo = mid + 1;
164         } else {
165             hi = mid;
166         }
167     }
168     // Check to see if target is greater or less than where we stopped
169     if (target < arr[lo].fTime) {
170         return ~lo;
171     }
172     // e.g. it should go at the end.
173     return ~(lo + 1);
174 }
175 
timeToT(MSec time,float * T,int * indexPtr,bool * exactPtr) const176 SkiaInterpolatorBase::Result SkiaInterpolatorBase::timeToT(MSec time, float* T, int* indexPtr,
177                                                            bool* exactPtr) const {
178     LOG_FATAL_IF(fFrameCount <= 0);
179     Result result = kNormal_Result;
180     if (fRepeat != 1.0f) {
181         MSec startTime = 0, endTime = 0;  // initialize to avoid warning
182         this->getDuration(&startTime, &endTime);
183         MSec totalTime = endTime - startTime;
184         MSec offsetTime = time - startTime;
185         endTime = SkScalarFloorToInt(fRepeat * totalTime);
186         if (offsetTime >= endTime) {
187             float fraction = SkScalarFraction(fRepeat);
188             offsetTime = fraction == 0 && fRepeat > 0
189                                  ? totalTime
190                                  : (MSec)SkScalarFloorToInt(fraction * totalTime);
191             result = kFreezeEnd_Result;
192         } else {
193             int mirror = fFlags & kMirror;
194             offsetTime = offsetTime % (totalTime << mirror);
195             if (offsetTime > totalTime) {  // can only be true if fMirror is true
196                 offsetTime = (totalTime << 1) - offsetTime;
197             }
198         }
199         time = offsetTime + startTime;
200     }
201 
202     int index = SkiaInterpolatorBase::binarySearch(fTimes, fFrameCount, time);
203     bool exact = true;
204     if (index < 0) {
205         index = ~index;
206         if (index == 0) {
207             result = kFreezeStart_Result;
208         } else if (index == fFrameCount) {
209             if (fFlags & kReset) {
210                 index = 0;
211             } else {
212                 index -= 1;
213             }
214             result = kFreezeEnd_Result;
215         } else {
216             // Need to interpolate between two frames.
217             exact = false;
218         }
219     }
220     LOG_FATAL_IF(index >= fFrameCount);
221     const SkTimeCode* nextTime = &fTimes[index];
222     MSec nextT = nextTime[0].fTime;
223     if (exact) {
224         *T = 0;
225     } else {
226         MSec prevT = nextTime[-1].fTime;
227         *T = ComputeRelativeT(time, prevT, nextT, nextTime[-1].fBlend);
228     }
229     *indexPtr = index;
230     *exactPtr = exact;
231     return result;
232 }
233 
SkiaInterpolator()234 SkiaInterpolator::SkiaInterpolator() {
235     INHERITED::reset(0, 0);
236     fValues = nullptr;
237 }
238 
SkiaInterpolator(int elemCount,int frameCount)239 SkiaInterpolator::SkiaInterpolator(int elemCount, int frameCount) {
240     LOG_FATAL_IF(elemCount <= 0);
241     this->reset(elemCount, frameCount);
242 }
243 
reset(int elemCount,int frameCount)244 void SkiaInterpolator::reset(int elemCount, int frameCount) {
245     INHERITED::reset(elemCount, frameCount);
246     size_t numBytes = (sizeof(float) * elemCount + sizeof(SkTimeCode)) * frameCount;
247     fStorage = malloc(numBytes);
248     LOG_ALWAYS_FATAL_IF(!fStorage, "Failed to allocate %zu bytes in %s",
249                         numBytes, __func__);
250     fTimes = (SkTimeCode*)fStorage;
251     fValues = (float*)((char*)fStorage + sizeof(SkTimeCode) * frameCount);
252 }
253 
254 static const float gIdentityBlend[4] = {0.33333333f, 0.33333333f, 0.66666667f, 0.66666667f};
255 
setKeyFrame(int index,MSec time,const float values[],const float blend[4])256 bool SkiaInterpolator::setKeyFrame(int index, MSec time, const float values[],
257                                    const float blend[4]) {
258     LOG_FATAL_IF(values == nullptr);
259 
260     if (blend == nullptr) {
261         blend = gIdentityBlend;
262     }
263 
264     // Verify the time should go after all the frames before index
265     bool success = ~index == SkiaInterpolatorBase::binarySearch(fTimes, index, time);
266     LOG_FATAL_IF(!success);
267     if (success) {
268         SkTimeCode* timeCode = &fTimes[index];
269         timeCode->fTime = time;
270         memcpy(timeCode->fBlend, blend, sizeof(timeCode->fBlend));
271         float* dst = &fValues[fElemCount * index];
272         memcpy(dst, values, fElemCount * sizeof(float));
273     }
274     return success;
275 }
276 
timeToValues(MSec time,float values[]) const277 SkiaInterpolator::Result SkiaInterpolator::timeToValues(MSec time, float values[]) const {
278     float T;
279     int index;
280     bool exact;
281     Result result = timeToT(time, &T, &index, &exact);
282     if (values) {
283         const float* nextSrc = &fValues[index * fElemCount];
284 
285         if (exact) {
286             memcpy(values, nextSrc, fElemCount * sizeof(float));
287         } else {
288             LOG_FATAL_IF(index <= 0);
289 
290             const float* prevSrc = nextSrc - fElemCount;
291 
292             for (int i = fElemCount - 1; i >= 0; --i) {
293                 values[i] = SkScalarInterp(prevSrc[i], nextSrc[i], T);
294             }
295         }
296     }
297     return result;
298 }
299