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,bool isScreen)24 AvgPoint ResampleAlgo::GetAvgPoint(const std::vector<PointerEvent>&& events,
25 bool isScreen)
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 if (!isScreen) {
35 avgX += iter->x;
36 avgY += iter->y;
37 } else {
38 avgX += iter->screenX;
39 avgY += iter->screenY;
40 }
41 avgTime += static_cast<uint64_t>(iter->time.time_since_epoch().count());
42 i++;
43 lastTime = static_cast<uint64_t>(iter->time.time_since_epoch().count());
44 }
45 }
46 if (i > 0) {
47 avgX /= i;
48 avgY /= i;
49 avgTime /= static_cast<uint64_t>(i);
50 }
51 return {
52 avgX,
53 avgY,
54 avgTime,
55 0.0f,
56 0.0f
57 };
58 }
59
LinearInterpolation(const AvgPoint & history,const AvgPoint & current,uint64_t nanoTimeStamp)60 ResamplePoint ResampleAlgo::LinearInterpolation(const AvgPoint& history, const AvgPoint& current,
61 uint64_t nanoTimeStamp)
62 {
63 if ((nanoTimeStamp == history.time || nanoTimeStamp == current.time) ||
64 (current.time <= history.time) ||
65 (current.time - history.time > INTERPOLATION_THRESHOLD) ||
66 (nanoTimeStamp < history.time)) {
67 return {};
68 }
69 auto inputXDeltaSlope = (current.x - history.x) * ONE_S_IN_NS /
70 (float)(current.time - history.time);
71 auto inputYDeltaSlope = (current.y - history.y) * ONE_S_IN_NS /
72 (float)(current.time - history.time);
73 if (nanoTimeStamp < current.time) {
74 float alpha = (float)(nanoTimeStamp - history.time) /
75 (float)(current.time - history.time);
76 float x = history.x + alpha * (current.x - history.x);
77 float y = history.y + alpha * (current.y - history.y);
78 return {
79 x,
80 y,
81 inputXDeltaSlope,
82 inputYDeltaSlope
83 };
84 } else if (nanoTimeStamp > current.time) {
85 float alpha = (float)(nanoTimeStamp - current.time) /
86 (float)(current.time - history.time);
87 float x = current.x + alpha * (current.x - history.x);
88 float y = current.y + alpha * (current.y - history.y);
89 return {
90 x,
91 y,
92 inputXDeltaSlope,
93 inputYDeltaSlope
94 };
95 }
96 return {};
97 }
98
GetResampleCoord(const std::vector<PointerEvent> && history,const std::vector<PointerEvent> && current,uint64_t nanoTimeStamp,bool isScreen)99 ResamplePoint ResampleAlgo::GetResampleCoord(const std::vector<PointerEvent>&& history,
100 const std::vector<PointerEvent>&& current, uint64_t nanoTimeStamp,
101 bool isScreen)
102 {
103 if (history.empty() || current.empty()) {
104 return {};
105 }
106 uint64_t lastNanoTime = 0;
107 float x = 0.0f;
108 float y = 0.0f;
109 for (const auto& item : current) {
110 uint64_t currentNanoTime = static_cast<uint64_t>(item.time.time_since_epoch().count());
111 if (lastNanoTime < currentNanoTime) {
112 lastNanoTime = currentNanoTime;
113 x = item.x;
114 y = item.y;
115 }
116 }
117 if (nanoTimeStamp > RESAMPLE_COORD_TIME_THRESHOLD + lastNanoTime) {
118 return {
119 x,
120 y,
121 0.0f,
122 0.0f
123 };
124 }
125 auto historyPoint = GetAvgPoint(std::move(history), isScreen);
126 auto currentPoint = GetAvgPoint(std::move(current), isScreen);
127 return LinearInterpolation(historyPoint, currentPoint, nanoTimeStamp);
128 }
129
Lerp(float a,float b,float alpha)130 inline float Lerp(float a, float b, float alpha)
131 {
132 return a + alpha * (b - a);
133 }
134
135 template<typename T>
FindSampleRightBefore(std::vector<T> & events,uint64_t resampleTime)136 typename std::vector<T>::iterator FindSampleRightBefore(std::vector<T>& events, uint64_t resampleTime)
137 {
138 std::chrono::nanoseconds nanoseconds(resampleTime);
139 TimeStamp ts(nanoseconds);
140 auto iter = events.rbegin(); // events must not be empty
141 do {
142 if (iter->time < ts) {
143 return --iter.base();
144 }
145 } while (++iter != events.rend());
146 return events.end();
147 }
148
IsRebound(TouchEvent & prev,TouchEvent & mid,TouchEvent & next)149 bool IsRebound(TouchEvent& prev, TouchEvent& mid, TouchEvent& next)
150 {
151 float deltaXA = mid.x - prev.x;
152 float deltaXB = next.x - mid.x;
153 float deltaYA = mid.y - prev.y;
154 float deltaYB = next.y - mid.y;
155 // dot product of the recent 2 deltas
156 // if negtive, it is definitely a rebound
157 // if it is a very small positive,
158 float dotProduct = deltaXA * deltaXB + deltaYA * deltaYB;
159 return (dotProduct < 0.5f);
160 }
161
162 template<class T>
GetResamplePointerEvent(std::vector<T> & events,uint64_t resampleTime,PointerEvent & resample,ResamplePoint & slope)163 bool ResampleAlgo::GetResamplePointerEvent(std::vector<T>& events,
164 uint64_t resampleTime, PointerEvent& resample, ResamplePoint& slope)
165 {
166 constexpr int64_t MAX_EXTERNAL_INTERPOLATE_TIME = 8 * 1000 * 1000; // 8ms
167 constexpr int64_t MIN_DELTA_TIME = 2 * 1000 * 1000; // 2ms
168 constexpr int64_t MAX_DELTA_TIME = 20 * 1000 * 1000; // 20ms
169
170 if (events.size() < 2) { // resample need at least 2 points.
171 return false;
172 }
173 auto iter = FindSampleRightBefore(events, resampleTime);
174 if (iter == events.end()) { // no event before resample
175 return false;
176 }
177 auto nextIter = std::next(iter);
178 int64_t delta = 0;
179 uint64_t iterTime = iter->time.time_since_epoch().count();
180 if (nextIter == events.end()) {
181 // external interpolation
182 nextIter = std::prev(iter);
183 if (nextIter != events.begin()) {
184 auto prepre = std::prev(nextIter);
185 if (IsRebound(*prepre, *nextIter, *iter)) {
186 return false;
187 }
188 }
189 uint64_t nextTime = nextIter->time.time_since_epoch().count();
190 delta = iterTime - nextTime;
191 if (delta > MAX_DELTA_TIME || delta < MIN_DELTA_TIME) {
192 return false;
193 }
194 uint64_t maxPredict = std::min(delta / 2, MAX_EXTERNAL_INTERPOLATE_TIME) + iterTime;
195 resampleTime = std::min(resampleTime, maxPredict);
196 delta = -delta;
197 } else if (nextIter->time.time_since_epoch().count() == static_cast<int64_t>(resampleTime)) {
198 return false;
199 } else {
200 // internal interpolation
201 delta = std::chrono::duration_cast<std::chrono::nanoseconds>(nextIter->time - iter->time).count();
202 if (delta < MIN_DELTA_TIME) {
203 return false;
204 }
205 }
206 float alpha = (static_cast<float>(resampleTime) - iterTime) / delta;
207 resample.x = Lerp(iter->x, nextIter->x, alpha);
208 resample.y = Lerp(iter->y, nextIter->y, alpha);
209 resample.screenX = Lerp(iter->screenX, nextIter->screenX, alpha);
210 resample.screenY = Lerp(iter->screenY, nextIter->screenY, alpha);
211 std::chrono::nanoseconds nanoseconds(resampleTime);
212 resample.time = TimeStamp(nanoseconds);
213 slope.inputXDeltaSlope = (nextIter->x - iter->x) / delta;
214 slope.inputYDeltaSlope = (nextIter->y - iter->y) / delta;
215 return true;
216 }
217
218 template bool ResampleAlgo::GetResamplePointerEvent<TouchEvent>(
219 std::vector<TouchEvent>&, uint64_t, PointerEvent&, ResamplePoint&);
220 } // namespace OHOS::Ace