• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "core/event/resample_algo.h"
16 
17 #include <algorithm>
18 #include <chrono>
19 #include <cinttypes>
20 
21 #include "core/event/touch_event.h"
22 
23 namespace OHOS::Ace {
GetAvgPoint(const std::vector<PointerEvent> && events,CoordinateType coordinateType)24 AvgPoint ResampleAlgo::GetAvgPoint(const std::vector<PointerEvent>&& events,
25     CoordinateType coordinateType)
26 {
27     float avgX = 0.0f;
28     float avgY = 0.0f;
29     uint64_t avgTime = 0;
30     int32_t i = 0;
31     uint64_t lastTime = 0;
32     for (auto iter = events.begin(); iter != events.end(); iter++) {
33         if (lastTime == 0 || static_cast<uint64_t>(iter->time.time_since_epoch().count()) != lastTime) {
34             switch (coordinateType) {
35                 case CoordinateType::NORMAL:
36                     avgX += iter->x;
37                     avgY += iter->y;
38                     break;
39                 case CoordinateType::SCREEN:
40                     avgX += iter->screenX;
41                     avgY += iter->screenY;
42                     break;
43                 case CoordinateType::GLOBALDISPLAY:
44                     avgX += iter->globalDisplayX;
45                     avgY += iter->globalDisplayY;
46                     break;
47                 default:
48                     break;
49             }
50             avgTime += static_cast<uint64_t>(iter->time.time_since_epoch().count());
51             i++;
52             lastTime = static_cast<uint64_t>(iter->time.time_since_epoch().count());
53         }
54     }
55     if (i > 0) {
56         avgX /= i;
57         avgY /= i;
58         avgTime /= static_cast<uint64_t>(i);
59     }
60     return {
61         avgX,
62         avgY,
63         avgTime,
64         0.0f,
65         0.0f
66     };
67 }
68 
LinearInterpolation(const AvgPoint & history,const AvgPoint & current,uint64_t nanoTimeStamp)69 ResamplePoint ResampleAlgo::LinearInterpolation(const AvgPoint& history, const AvgPoint& current,
70     uint64_t nanoTimeStamp)
71 {
72     if ((nanoTimeStamp == history.time || nanoTimeStamp == current.time) ||
73         (current.time <= history.time) ||
74         (current.time - history.time > INTERPOLATION_THRESHOLD) ||
75         (nanoTimeStamp < history.time)) {
76         return {};
77     }
78     auto inputXDeltaSlope = (current.x - history.x) * ONE_S_IN_NS /
79                         (float)(current.time - history.time);
80     auto inputYDeltaSlope = (current.y - history.y) * ONE_S_IN_NS /
81                         (float)(current.time - history.time);
82     if (nanoTimeStamp < current.time) {
83         float alpha = (float)(nanoTimeStamp - history.time) /
84                 (float)(current.time - history.time);
85         float x = history.x + alpha * (current.x - history.x);
86         float y = history.y + alpha * (current.y - history.y);
87         return {
88             x,
89             y,
90             inputXDeltaSlope,
91             inputYDeltaSlope
92         };
93     } else if (nanoTimeStamp > current.time) {
94         float alpha = (float)(nanoTimeStamp - current.time) /
95                 (float)(current.time - history.time);
96         float x = current.x + alpha * (current.x - history.x);
97         float y = current.y + alpha * (current.y - history.y);
98         return {
99             x,
100             y,
101             inputXDeltaSlope,
102             inputYDeltaSlope
103         };
104     }
105     return {};
106 }
107 
GetResampleCoord(const std::vector<PointerEvent> && history,const std::vector<PointerEvent> && current,uint64_t nanoTimeStamp,CoordinateType coordinateType)108 ResamplePoint ResampleAlgo::GetResampleCoord(const std::vector<PointerEvent>&& history,
109     const std::vector<PointerEvent>&& current, uint64_t nanoTimeStamp,
110     CoordinateType coordinateType)
111 {
112     if (history.empty() || current.empty()) {
113         return {};
114     }
115     uint64_t lastNanoTime = 0;
116     float x = 0.0f;
117     float y = 0.0f;
118     for (const auto& item : current) {
119         uint64_t currentNanoTime = static_cast<uint64_t>(item.time.time_since_epoch().count());
120         if (lastNanoTime < currentNanoTime) {
121             lastNanoTime = currentNanoTime;
122             x = item.x;
123             y = item.y;
124         }
125     }
126     if (nanoTimeStamp > RESAMPLE_COORD_TIME_THRESHOLD + lastNanoTime) {
127         return {
128             x,
129             y,
130             0.0f,
131             0.0f
132         };
133     }
134     auto historyPoint = GetAvgPoint(std::move(history), coordinateType);
135     auto currentPoint = GetAvgPoint(std::move(current), coordinateType);
136     return LinearInterpolation(historyPoint, currentPoint, nanoTimeStamp);
137 }
138 
Lerp(float a,float b,float alpha)139 inline float Lerp(float a, float b, float alpha)
140 {
141     return a + alpha * (b - a);
142 }
143 
144 template<typename T>
FindSampleRightBefore(std::vector<T> & events,uint64_t resampleTime)145 typename std::vector<T>::iterator FindSampleRightBefore(std::vector<T>& events, uint64_t resampleTime)
146 {
147     std::chrono::nanoseconds nanoseconds(resampleTime);
148     TimeStamp ts(nanoseconds);
149     auto iter = events.rbegin(); // events must not be empty
150     do {
151         if (iter->time < ts) {
152             return --iter.base();
153         }
154     } while (++iter != events.rend());
155     return events.end();
156 }
157 
IsRebound(TouchEvent & prev,TouchEvent & mid,TouchEvent & next)158 bool IsRebound(TouchEvent& prev, TouchEvent& mid, TouchEvent& next)
159 {
160     float deltaXA = mid.x - prev.x;
161     float deltaXB = next.x - mid.x;
162     float deltaYA = mid.y - prev.y;
163     float deltaYB = next.y - mid.y;
164     // dot product of the recent 2 deltas
165     // if negtive, it is definitely a rebound
166     // if it is a very small positive,
167     float dotProduct = deltaXA * deltaXB + deltaYA * deltaYB;
168     return (dotProduct < 0.5f);
169 }
170 
171 template<class T>
GetResamplePointerEvent(std::vector<T> & events,uint64_t resampleTime,PointerEvent & resample,ResamplePoint & slope)172 bool ResampleAlgo::GetResamplePointerEvent(std::vector<T>& events,
173     uint64_t resampleTime, PointerEvent& resample, ResamplePoint& slope)
174 {
175     constexpr int64_t MAX_EXTERNAL_INTERPOLATE_TIME = 8 * 1000 * 1000; // 8ms
176     constexpr int64_t MIN_DELTA_TIME = 2 * 1000 * 1000; // 2ms
177     constexpr int64_t MAX_DELTA_TIME = 20 * 1000 * 1000; // 20ms
178 
179     if (events.size() < 2) { // resample need at least 2 points.
180         return false;
181     }
182     auto iter = FindSampleRightBefore(events, resampleTime);
183     if (iter == events.end()) { // no event before resample
184         return false;
185     }
186     auto nextIter = std::next(iter);
187     int64_t delta = 0;
188     uint64_t iterTime = iter->time.time_since_epoch().count();
189     if (nextIter == events.end()) {
190         // external interpolation
191         nextIter = std::prev(iter);
192         if (nextIter != events.begin()) {
193             auto prepre = std::prev(nextIter);
194             if (IsRebound(*prepre, *nextIter, *iter)) {
195                 return false;
196             }
197         }
198         int64_t nextTime = nextIter->time.time_since_epoch().count();
199         delta = static_cast<int64_t>(iterTime) - nextTime;
200         if (delta > MAX_DELTA_TIME || delta < MIN_DELTA_TIME) {
201             return false;
202         }
203         uint64_t maxPredict = std::min(delta / 2, MAX_EXTERNAL_INTERPOLATE_TIME) + iterTime;
204         resampleTime = std::min(resampleTime, maxPredict);
205         delta = -delta;
206     } else if (nextIter->time.time_since_epoch().count() == static_cast<int64_t>(resampleTime)) {
207         return false;
208     } else {
209         // internal interpolation
210         delta = std::chrono::duration_cast<std::chrono::nanoseconds>(nextIter->time - iter->time).count();
211         if (delta < MIN_DELTA_TIME) {
212             return false;
213         }
214     }
215     float alpha = (static_cast<int64_t>(resampleTime) - static_cast<int64_t>(iterTime)) / static_cast<double>(delta);
216     resample.x = Lerp(iter->x, nextIter->x, alpha);
217     resample.y = Lerp(iter->y, nextIter->y, alpha);
218     resample.screenX = Lerp(iter->screenX, nextIter->screenX, alpha);
219     resample.screenY = Lerp(iter->screenY, nextIter->screenY, alpha);
220     resample.globalDisplayX = Lerp(iter->globalDisplayX, nextIter->globalDisplayX, alpha);
221     resample.globalDisplayY = Lerp(iter->globalDisplayY, nextIter->globalDisplayY, alpha);
222     std::chrono::nanoseconds nanoseconds(resampleTime);
223     resample.time = TimeStamp(nanoseconds);
224     slope.inputXDeltaSlope = (nextIter->x - iter->x) / delta;
225     slope.inputYDeltaSlope = (nextIter->y - iter->y) / delta;
226     return true;
227 }
228 
229 template bool ResampleAlgo::GetResamplePointerEvent<TouchEvent>(
230     std::vector<TouchEvent>&, uint64_t, PointerEvent&, ResamplePoint&);
231 } // namespace OHOS::Ace