xref: /aosp_15_r20/frameworks/av/include/media/Interpolator.h (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker  * Copyright 2017 The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker  *
4*ec779b8eSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker  *
8*ec779b8eSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker  *
10*ec779b8eSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker  * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker  */
16*ec779b8eSAndroid Build Coastguard Worker 
17*ec779b8eSAndroid Build Coastguard Worker #ifndef ANDROID_INTERPOLATOR_H
18*ec779b8eSAndroid Build Coastguard Worker #define ANDROID_INTERPOLATOR_H
19*ec779b8eSAndroid Build Coastguard Worker 
20*ec779b8eSAndroid Build Coastguard Worker #include <map>
21*ec779b8eSAndroid Build Coastguard Worker #include <sstream>
22*ec779b8eSAndroid Build Coastguard Worker #include <unordered_map>
23*ec779b8eSAndroid Build Coastguard Worker 
24*ec779b8eSAndroid Build Coastguard Worker #include <android/media/InterpolatorConfig.h>
25*ec779b8eSAndroid Build Coastguard Worker #include <binder/Parcel.h>
26*ec779b8eSAndroid Build Coastguard Worker #include <utils/RefBase.h>
27*ec779b8eSAndroid Build Coastguard Worker 
28*ec779b8eSAndroid Build Coastguard Worker #pragma push_macro("LOG_TAG")
29*ec779b8eSAndroid Build Coastguard Worker #undef LOG_TAG
30*ec779b8eSAndroid Build Coastguard Worker #define LOG_TAG "Interpolator"
31*ec779b8eSAndroid Build Coastguard Worker 
32*ec779b8eSAndroid Build Coastguard Worker namespace android {
33*ec779b8eSAndroid Build Coastguard Worker 
34*ec779b8eSAndroid Build Coastguard Worker /*
35*ec779b8eSAndroid Build Coastguard Worker  * A general purpose spline interpolator class which takes a set of points
36*ec779b8eSAndroid Build Coastguard Worker  * and performs interpolation.  This is used for the VolumeShaper class.
37*ec779b8eSAndroid Build Coastguard Worker  */
38*ec779b8eSAndroid Build Coastguard Worker 
39*ec779b8eSAndroid Build Coastguard Worker template <typename S, typename T>
40*ec779b8eSAndroid Build Coastguard Worker class Interpolator : public std::map<S, T> {
41*ec779b8eSAndroid Build Coastguard Worker public:
42*ec779b8eSAndroid Build Coastguard Worker     // Polynomial spline interpolators
43*ec779b8eSAndroid Build Coastguard Worker     using InterpolatorType  = media::InterpolatorType;
44*ec779b8eSAndroid Build Coastguard Worker 
45*ec779b8eSAndroid Build Coastguard Worker     explicit Interpolator(
46*ec779b8eSAndroid Build Coastguard Worker             InterpolatorType interpolatorType = InterpolatorType::CUBIC,
47*ec779b8eSAndroid Build Coastguard Worker             bool cache = true)
mCache(cache)48*ec779b8eSAndroid Build Coastguard Worker         : mCache(cache)
49*ec779b8eSAndroid Build Coastguard Worker         , mFirstSlope(0)
50*ec779b8eSAndroid Build Coastguard Worker         , mLastSlope(0) {
51*ec779b8eSAndroid Build Coastguard Worker         setInterpolatorType(interpolatorType);
52*ec779b8eSAndroid Build Coastguard Worker     }
53*ec779b8eSAndroid Build Coastguard Worker 
first()54*ec779b8eSAndroid Build Coastguard Worker     std::pair<S, T> first() const {
55*ec779b8eSAndroid Build Coastguard Worker         return *this->begin();
56*ec779b8eSAndroid Build Coastguard Worker     }
57*ec779b8eSAndroid Build Coastguard Worker 
last()58*ec779b8eSAndroid Build Coastguard Worker     std::pair<S, T> last() const {
59*ec779b8eSAndroid Build Coastguard Worker         return *this->rbegin();
60*ec779b8eSAndroid Build Coastguard Worker     }
61*ec779b8eSAndroid Build Coastguard Worker 
62*ec779b8eSAndroid Build Coastguard Worker     // find the corresponding Y point from a X point.
findY(S x)63*ec779b8eSAndroid Build Coastguard Worker     T findY(S x) { // logically const, but modifies cache
64*ec779b8eSAndroid Build Coastguard Worker         auto high = this->lower_bound(x);
65*ec779b8eSAndroid Build Coastguard Worker         // greater than last point
66*ec779b8eSAndroid Build Coastguard Worker         if (high == this->end()) {
67*ec779b8eSAndroid Build Coastguard Worker             return this->rbegin()->second;
68*ec779b8eSAndroid Build Coastguard Worker         }
69*ec779b8eSAndroid Build Coastguard Worker         // at or before first point
70*ec779b8eSAndroid Build Coastguard Worker         if (high == this->begin()) {
71*ec779b8eSAndroid Build Coastguard Worker             return high->second;
72*ec779b8eSAndroid Build Coastguard Worker         }
73*ec779b8eSAndroid Build Coastguard Worker         // go lower.
74*ec779b8eSAndroid Build Coastguard Worker         auto low = high;
75*ec779b8eSAndroid Build Coastguard Worker         --low;
76*ec779b8eSAndroid Build Coastguard Worker 
77*ec779b8eSAndroid Build Coastguard Worker         // now that we have two adjacent points:
78*ec779b8eSAndroid Build Coastguard Worker         switch (mInterpolatorType) {
79*ec779b8eSAndroid Build Coastguard Worker         case InterpolatorType::STEP:
80*ec779b8eSAndroid Build Coastguard Worker             return high->first == x ? high->second : low->second;
81*ec779b8eSAndroid Build Coastguard Worker         case InterpolatorType::LINEAR:
82*ec779b8eSAndroid Build Coastguard Worker             return ((high->first - x) * low->second + (x - low->first) * high->second)
83*ec779b8eSAndroid Build Coastguard Worker                     / (high->first - low->first);
84*ec779b8eSAndroid Build Coastguard Worker         case InterpolatorType::CUBIC:
85*ec779b8eSAndroid Build Coastguard Worker         case InterpolatorType::CUBIC_MONOTONIC:
86*ec779b8eSAndroid Build Coastguard Worker         default: {
87*ec779b8eSAndroid Build Coastguard Worker             // See https://en.wikipedia.org/wiki/Cubic_Hermite_spline
88*ec779b8eSAndroid Build Coastguard Worker 
89*ec779b8eSAndroid Build Coastguard Worker             const S interval =  high->first - low->first;
90*ec779b8eSAndroid Build Coastguard Worker 
91*ec779b8eSAndroid Build Coastguard Worker             // check to see if we've cached the polynomial coefficients
92*ec779b8eSAndroid Build Coastguard Worker             if (mMemo.count(low->first) != 0) {
93*ec779b8eSAndroid Build Coastguard Worker                 const S t = (x - low->first) / interval;
94*ec779b8eSAndroid Build Coastguard Worker                 const S t2 = t * t;
95*ec779b8eSAndroid Build Coastguard Worker                 const auto &memo = mMemo[low->first];
96*ec779b8eSAndroid Build Coastguard Worker                 return low->second + std::get<0>(memo) * t
97*ec779b8eSAndroid Build Coastguard Worker                         + (std::get<1>(memo) + std::get<2>(memo) * t) * t2;
98*ec779b8eSAndroid Build Coastguard Worker             }
99*ec779b8eSAndroid Build Coastguard Worker 
100*ec779b8eSAndroid Build Coastguard Worker             // find the neighboring points (low2 < low < high < high2)
101*ec779b8eSAndroid Build Coastguard Worker             auto low2 = this->end();
102*ec779b8eSAndroid Build Coastguard Worker             if (low != this->begin()) {
103*ec779b8eSAndroid Build Coastguard Worker                 low2 = low;
104*ec779b8eSAndroid Build Coastguard Worker                 --low2; // decrementing this->begin() is undefined
105*ec779b8eSAndroid Build Coastguard Worker             }
106*ec779b8eSAndroid Build Coastguard Worker             auto high2 = high;
107*ec779b8eSAndroid Build Coastguard Worker             ++high2;
108*ec779b8eSAndroid Build Coastguard Worker 
109*ec779b8eSAndroid Build Coastguard Worker             // you could have catmullRom with monotonic or
110*ec779b8eSAndroid Build Coastguard Worker             // non catmullRom (finite difference) with regular cubic;
111*ec779b8eSAndroid Build Coastguard Worker             // the choices here minimize computation.
112*ec779b8eSAndroid Build Coastguard Worker             bool monotonic, catmullRom;
113*ec779b8eSAndroid Build Coastguard Worker             if (mInterpolatorType == InterpolatorType::CUBIC_MONOTONIC) {
114*ec779b8eSAndroid Build Coastguard Worker                 monotonic = true;
115*ec779b8eSAndroid Build Coastguard Worker                 catmullRom = false;
116*ec779b8eSAndroid Build Coastguard Worker             } else {
117*ec779b8eSAndroid Build Coastguard Worker                 monotonic = false;
118*ec779b8eSAndroid Build Coastguard Worker                 catmullRom = true;
119*ec779b8eSAndroid Build Coastguard Worker             }
120*ec779b8eSAndroid Build Coastguard Worker 
121*ec779b8eSAndroid Build Coastguard Worker             // secants are only needed for finite difference splines or
122*ec779b8eSAndroid Build Coastguard Worker             // monotonic computation.
123*ec779b8eSAndroid Build Coastguard Worker             // we use lazy computation here - if we precompute in
124*ec779b8eSAndroid Build Coastguard Worker             // a single pass, duplicate secant computations may be avoided.
125*ec779b8eSAndroid Build Coastguard Worker             S sec{}, sec0{}, sec1{};  // initialization not needed, used for clang-tidy
126*ec779b8eSAndroid Build Coastguard Worker             if (!catmullRom || monotonic) {
127*ec779b8eSAndroid Build Coastguard Worker                 sec = (high->second - low->second) / interval;
128*ec779b8eSAndroid Build Coastguard Worker                 sec0 = low2 != this->end()
129*ec779b8eSAndroid Build Coastguard Worker                         ? (low->second - low2->second) / (low->first - low2->first)
130*ec779b8eSAndroid Build Coastguard Worker                         : mFirstSlope;
131*ec779b8eSAndroid Build Coastguard Worker                 sec1 = high2 != this->end()
132*ec779b8eSAndroid Build Coastguard Worker                         ? (high2->second - high->second) / (high2->first - high->first)
133*ec779b8eSAndroid Build Coastguard Worker                         : mLastSlope;
134*ec779b8eSAndroid Build Coastguard Worker             }
135*ec779b8eSAndroid Build Coastguard Worker 
136*ec779b8eSAndroid Build Coastguard Worker             // compute the tangent slopes at the control points
137*ec779b8eSAndroid Build Coastguard Worker             S m0, m1;
138*ec779b8eSAndroid Build Coastguard Worker             if (catmullRom) {
139*ec779b8eSAndroid Build Coastguard Worker                 // Catmull-Rom spline
140*ec779b8eSAndroid Build Coastguard Worker                 m0 = low2 != this->end()
141*ec779b8eSAndroid Build Coastguard Worker                         ? (high->second - low2->second) / (high->first - low2->first)
142*ec779b8eSAndroid Build Coastguard Worker                         : mFirstSlope;
143*ec779b8eSAndroid Build Coastguard Worker 
144*ec779b8eSAndroid Build Coastguard Worker                 m1 = high2 != this->end()
145*ec779b8eSAndroid Build Coastguard Worker                         ? (high2->second - low->second) / (high2->first - low->first)
146*ec779b8eSAndroid Build Coastguard Worker                         : mLastSlope;
147*ec779b8eSAndroid Build Coastguard Worker             } else {
148*ec779b8eSAndroid Build Coastguard Worker                 // finite difference spline
149*ec779b8eSAndroid Build Coastguard Worker                 m0 = (sec0 + sec) * 0.5f;
150*ec779b8eSAndroid Build Coastguard Worker                 m1 = (sec1 + sec) * 0.5f;
151*ec779b8eSAndroid Build Coastguard Worker             }
152*ec779b8eSAndroid Build Coastguard Worker 
153*ec779b8eSAndroid Build Coastguard Worker             if (monotonic) {
154*ec779b8eSAndroid Build Coastguard Worker                 // https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
155*ec779b8eSAndroid Build Coastguard Worker                 // A sufficient condition for Fritsch–Carlson monotonicity is constraining
156*ec779b8eSAndroid Build Coastguard Worker                 // (1) the normalized slopes to be within the circle of radius 3, or
157*ec779b8eSAndroid Build Coastguard Worker                 // (2) the normalized slopes to be within the square of radius 3.
158*ec779b8eSAndroid Build Coastguard Worker                 // Condition (2) is more generous and easier to compute.
159*ec779b8eSAndroid Build Coastguard Worker                 const S maxSlope = 3 * sec;
160*ec779b8eSAndroid Build Coastguard Worker                 m0 = constrainSlope(m0, maxSlope);
161*ec779b8eSAndroid Build Coastguard Worker                 m1 = constrainSlope(m1, maxSlope);
162*ec779b8eSAndroid Build Coastguard Worker 
163*ec779b8eSAndroid Build Coastguard Worker                 m0 = constrainSlope(m0, 3 * sec0);
164*ec779b8eSAndroid Build Coastguard Worker                 m1 = constrainSlope(m1, 3 * sec1);
165*ec779b8eSAndroid Build Coastguard Worker             }
166*ec779b8eSAndroid Build Coastguard Worker 
167*ec779b8eSAndroid Build Coastguard Worker             const S t = (x - low->first) / interval;
168*ec779b8eSAndroid Build Coastguard Worker             const S t2 = t * t;
169*ec779b8eSAndroid Build Coastguard Worker             if (mCache) {
170*ec779b8eSAndroid Build Coastguard Worker                 // convert to cubic polynomial coefficients and compute
171*ec779b8eSAndroid Build Coastguard Worker                 m0 *= interval;
172*ec779b8eSAndroid Build Coastguard Worker                 m1 *= interval;
173*ec779b8eSAndroid Build Coastguard Worker                 const T dy = high->second - low->second;
174*ec779b8eSAndroid Build Coastguard Worker                 const S c0 = low->second;
175*ec779b8eSAndroid Build Coastguard Worker                 const S c1 = m0;
176*ec779b8eSAndroid Build Coastguard Worker                 const S c2 = 3 * dy - 2 * m0 - m1;
177*ec779b8eSAndroid Build Coastguard Worker                 const S c3 = m0 + m1 - 2 * dy;
178*ec779b8eSAndroid Build Coastguard Worker                 mMemo[low->first] = std::make_tuple(c1, c2, c3);
179*ec779b8eSAndroid Build Coastguard Worker                 return c0 + c1 * t + (c2 + c3 * t) * t2;
180*ec779b8eSAndroid Build Coastguard Worker             } else {
181*ec779b8eSAndroid Build Coastguard Worker                 // classic Hermite interpolation
182*ec779b8eSAndroid Build Coastguard Worker                 const S t3 = t2 * t;
183*ec779b8eSAndroid Build Coastguard Worker                 const S h00 =  2 * t3 - 3 * t2     + 1;
184*ec779b8eSAndroid Build Coastguard Worker                 const S h10 =      t3 - 2 * t2 + t    ;
185*ec779b8eSAndroid Build Coastguard Worker                 const S h01 = -2 * t3 + 3 * t2        ;
186*ec779b8eSAndroid Build Coastguard Worker                 const S h11 =      t3     - t2        ;
187*ec779b8eSAndroid Build Coastguard Worker                 return h00 * low->second + (h10 * m0 + h11 * m1) * interval + h01 * high->second;
188*ec779b8eSAndroid Build Coastguard Worker             }
189*ec779b8eSAndroid Build Coastguard Worker         } // default
190*ec779b8eSAndroid Build Coastguard Worker         }
191*ec779b8eSAndroid Build Coastguard Worker     }
192*ec779b8eSAndroid Build Coastguard Worker 
getInterpolatorType()193*ec779b8eSAndroid Build Coastguard Worker     InterpolatorType getInterpolatorType() const {
194*ec779b8eSAndroid Build Coastguard Worker         return mInterpolatorType;
195*ec779b8eSAndroid Build Coastguard Worker     }
196*ec779b8eSAndroid Build Coastguard Worker 
setInterpolatorType(InterpolatorType interpolatorType)197*ec779b8eSAndroid Build Coastguard Worker     status_t setInterpolatorType(InterpolatorType interpolatorType) {
198*ec779b8eSAndroid Build Coastguard Worker         switch (interpolatorType) {
199*ec779b8eSAndroid Build Coastguard Worker         case InterpolatorType::STEP:   // Not continuous
200*ec779b8eSAndroid Build Coastguard Worker         case InterpolatorType::LINEAR: // C0
201*ec779b8eSAndroid Build Coastguard Worker         case InterpolatorType::CUBIC:  // C1
202*ec779b8eSAndroid Build Coastguard Worker         case InterpolatorType::CUBIC_MONOTONIC: // C1 + other constraints
203*ec779b8eSAndroid Build Coastguard Worker         // case InterpolatorType::CUBIC_C2:
204*ec779b8eSAndroid Build Coastguard Worker             mInterpolatorType = interpolatorType;
205*ec779b8eSAndroid Build Coastguard Worker             return NO_ERROR;
206*ec779b8eSAndroid Build Coastguard Worker         default:
207*ec779b8eSAndroid Build Coastguard Worker             ALOGE("invalid interpolatorType: %d", static_cast<int>(interpolatorType));
208*ec779b8eSAndroid Build Coastguard Worker             return BAD_VALUE;
209*ec779b8eSAndroid Build Coastguard Worker         }
210*ec779b8eSAndroid Build Coastguard Worker     }
211*ec779b8eSAndroid Build Coastguard Worker 
getFirstSlope()212*ec779b8eSAndroid Build Coastguard Worker     T getFirstSlope() const {
213*ec779b8eSAndroid Build Coastguard Worker         return mFirstSlope;
214*ec779b8eSAndroid Build Coastguard Worker     }
215*ec779b8eSAndroid Build Coastguard Worker 
setFirstSlope(T slope)216*ec779b8eSAndroid Build Coastguard Worker     void setFirstSlope(T slope) {
217*ec779b8eSAndroid Build Coastguard Worker         mFirstSlope = slope;
218*ec779b8eSAndroid Build Coastguard Worker     }
219*ec779b8eSAndroid Build Coastguard Worker 
getLastSlope()220*ec779b8eSAndroid Build Coastguard Worker     T getLastSlope() const {
221*ec779b8eSAndroid Build Coastguard Worker         return mLastSlope;
222*ec779b8eSAndroid Build Coastguard Worker     }
223*ec779b8eSAndroid Build Coastguard Worker 
setLastSlope(T slope)224*ec779b8eSAndroid Build Coastguard Worker     void setLastSlope(T slope) {
225*ec779b8eSAndroid Build Coastguard Worker         mLastSlope = slope;
226*ec779b8eSAndroid Build Coastguard Worker     }
227*ec779b8eSAndroid Build Coastguard Worker 
clearCache()228*ec779b8eSAndroid Build Coastguard Worker     void clearCache() {
229*ec779b8eSAndroid Build Coastguard Worker         mMemo.clear();
230*ec779b8eSAndroid Build Coastguard Worker     }
231*ec779b8eSAndroid Build Coastguard Worker 
232*ec779b8eSAndroid Build Coastguard Worker     // TODO(ytai): remove this method once it is not used.
writeToParcel(Parcel * parcel)233*ec779b8eSAndroid Build Coastguard Worker     status_t writeToParcel(Parcel *parcel) const {
234*ec779b8eSAndroid Build Coastguard Worker         media::InterpolatorConfig config;
235*ec779b8eSAndroid Build Coastguard Worker         writeToConfig(&config);
236*ec779b8eSAndroid Build Coastguard Worker         return config.writeToParcel(parcel);
237*ec779b8eSAndroid Build Coastguard Worker     }
238*ec779b8eSAndroid Build Coastguard Worker 
writeToConfig(media::InterpolatorConfig * config)239*ec779b8eSAndroid Build Coastguard Worker     void writeToConfig(media::InterpolatorConfig *config) const {
240*ec779b8eSAndroid Build Coastguard Worker         config->type = mInterpolatorType;
241*ec779b8eSAndroid Build Coastguard Worker         config->firstSlope = mFirstSlope;
242*ec779b8eSAndroid Build Coastguard Worker         config->lastSlope = mLastSlope;
243*ec779b8eSAndroid Build Coastguard Worker         for (const auto &pt : *this) {
244*ec779b8eSAndroid Build Coastguard Worker             config->xy.push_back(pt.first);
245*ec779b8eSAndroid Build Coastguard Worker             config->xy.push_back(pt.second);
246*ec779b8eSAndroid Build Coastguard Worker         }
247*ec779b8eSAndroid Build Coastguard Worker     }
248*ec779b8eSAndroid Build Coastguard Worker 
249*ec779b8eSAndroid Build Coastguard Worker     // TODO(ytai): remove this method once it is not used.
readFromParcel(const Parcel & parcel)250*ec779b8eSAndroid Build Coastguard Worker     status_t readFromParcel(const Parcel &parcel) {
251*ec779b8eSAndroid Build Coastguard Worker         media::InterpolatorConfig config;
252*ec779b8eSAndroid Build Coastguard Worker         status_t res = config.readFromParcel(&parcel);
253*ec779b8eSAndroid Build Coastguard Worker         if (res != NO_ERROR) {
254*ec779b8eSAndroid Build Coastguard Worker             return res;
255*ec779b8eSAndroid Build Coastguard Worker         }
256*ec779b8eSAndroid Build Coastguard Worker         return readFromConfig(config);
257*ec779b8eSAndroid Build Coastguard Worker     }
258*ec779b8eSAndroid Build Coastguard Worker 
readFromConfig(const media::InterpolatorConfig & config)259*ec779b8eSAndroid Build Coastguard Worker     status_t readFromConfig(const media::InterpolatorConfig &config) {
260*ec779b8eSAndroid Build Coastguard Worker         this->clear();
261*ec779b8eSAndroid Build Coastguard Worker         setInterpolatorType(config.type);
262*ec779b8eSAndroid Build Coastguard Worker         if ((config.xy.size() & 1) != 0) {
263*ec779b8eSAndroid Build Coastguard Worker             // xy size must be even.
264*ec779b8eSAndroid Build Coastguard Worker             return BAD_VALUE;
265*ec779b8eSAndroid Build Coastguard Worker         }
266*ec779b8eSAndroid Build Coastguard Worker         uint32_t size = config.xy.size() / 2;
267*ec779b8eSAndroid Build Coastguard Worker         mFirstSlope = config.firstSlope;
268*ec779b8eSAndroid Build Coastguard Worker         mLastSlope = config.lastSlope;
269*ec779b8eSAndroid Build Coastguard Worker 
270*ec779b8eSAndroid Build Coastguard Worker         // Note: We don't need to check size is within some bounds as
271*ec779b8eSAndroid Build Coastguard Worker         // the Parcel read will fail if size is incorrectly specified too large.
272*ec779b8eSAndroid Build Coastguard Worker         float lastx = 0.f; // initialization not needed, used for clang tidy
273*ec779b8eSAndroid Build Coastguard Worker         for (uint32_t i = 0; i < size; ++i) {
274*ec779b8eSAndroid Build Coastguard Worker             float x = config.xy[i * 2];
275*ec779b8eSAndroid Build Coastguard Worker             float y = config.xy[i * 2 + 1];
276*ec779b8eSAndroid Build Coastguard Worker             if ((i > 0 && !(x > lastx)) /* handle nan */
277*ec779b8eSAndroid Build Coastguard Worker                     || y != y /* handle nan */) {
278*ec779b8eSAndroid Build Coastguard Worker                 // This is a std::map object which imposes sorted order
279*ec779b8eSAndroid Build Coastguard Worker                 // automatically on emplace.
280*ec779b8eSAndroid Build Coastguard Worker                 // Nevertheless for reading from a Parcel,
281*ec779b8eSAndroid Build Coastguard Worker                 // we require that the points be specified monotonic in x.
282*ec779b8eSAndroid Build Coastguard Worker                 return BAD_VALUE;
283*ec779b8eSAndroid Build Coastguard Worker             }
284*ec779b8eSAndroid Build Coastguard Worker             this->emplace(x, y);
285*ec779b8eSAndroid Build Coastguard Worker             lastx = x;
286*ec779b8eSAndroid Build Coastguard Worker         }
287*ec779b8eSAndroid Build Coastguard Worker         return NO_ERROR;
288*ec779b8eSAndroid Build Coastguard Worker     }
289*ec779b8eSAndroid Build Coastguard Worker 
toString()290*ec779b8eSAndroid Build Coastguard Worker     std::string toString() const {
291*ec779b8eSAndroid Build Coastguard Worker         std::stringstream ss;
292*ec779b8eSAndroid Build Coastguard Worker         ss << "Interpolator{mInterpolatorType=" << media::toString(mInterpolatorType);
293*ec779b8eSAndroid Build Coastguard Worker         ss << ", mFirstSlope=" << mFirstSlope;
294*ec779b8eSAndroid Build Coastguard Worker         ss << ", mLastSlope=" << mLastSlope;
295*ec779b8eSAndroid Build Coastguard Worker         ss << ", {";
296*ec779b8eSAndroid Build Coastguard Worker         bool first = true;
297*ec779b8eSAndroid Build Coastguard Worker         for (const auto &pt : *this) {
298*ec779b8eSAndroid Build Coastguard Worker             if (first) {
299*ec779b8eSAndroid Build Coastguard Worker                 first = false;
300*ec779b8eSAndroid Build Coastguard Worker                 ss << "{";
301*ec779b8eSAndroid Build Coastguard Worker             } else {
302*ec779b8eSAndroid Build Coastguard Worker                 ss << ", {";
303*ec779b8eSAndroid Build Coastguard Worker             }
304*ec779b8eSAndroid Build Coastguard Worker             ss << pt.first << ", " << pt.second << "}";
305*ec779b8eSAndroid Build Coastguard Worker         }
306*ec779b8eSAndroid Build Coastguard Worker         ss << "}}";
307*ec779b8eSAndroid Build Coastguard Worker         return ss.str();
308*ec779b8eSAndroid Build Coastguard Worker     }
309*ec779b8eSAndroid Build Coastguard Worker 
310*ec779b8eSAndroid Build Coastguard Worker private:
constrainSlope(S slope,S maxSlope)311*ec779b8eSAndroid Build Coastguard Worker     static S constrainSlope(S slope, S maxSlope) {
312*ec779b8eSAndroid Build Coastguard Worker         if (maxSlope > 0) {
313*ec779b8eSAndroid Build Coastguard Worker             slope = std::min(slope, maxSlope);
314*ec779b8eSAndroid Build Coastguard Worker             slope = std::max(slope, S(0)); // not globally monotonic
315*ec779b8eSAndroid Build Coastguard Worker         } else {
316*ec779b8eSAndroid Build Coastguard Worker             slope = std::max(slope, maxSlope);
317*ec779b8eSAndroid Build Coastguard Worker             slope = std::min(slope, S(0)); // not globally monotonic
318*ec779b8eSAndroid Build Coastguard Worker         }
319*ec779b8eSAndroid Build Coastguard Worker         return slope;
320*ec779b8eSAndroid Build Coastguard Worker     }
321*ec779b8eSAndroid Build Coastguard Worker 
322*ec779b8eSAndroid Build Coastguard Worker     InterpolatorType mInterpolatorType;
323*ec779b8eSAndroid Build Coastguard Worker     bool mCache; // whether we cache spline coefficient computation
324*ec779b8eSAndroid Build Coastguard Worker 
325*ec779b8eSAndroid Build Coastguard Worker     // for cubic interpolation, the boundary conditions in slope.
326*ec779b8eSAndroid Build Coastguard Worker     S mFirstSlope;
327*ec779b8eSAndroid Build Coastguard Worker     S mLastSlope;
328*ec779b8eSAndroid Build Coastguard Worker 
329*ec779b8eSAndroid Build Coastguard Worker     // spline cubic polynomial coefficient cache
330*ec779b8eSAndroid Build Coastguard Worker     std::unordered_map<S, std::tuple<S /* c1 */, S /* c2 */, S /* c3 */>> mMemo;
331*ec779b8eSAndroid Build Coastguard Worker }; // Interpolator
332*ec779b8eSAndroid Build Coastguard Worker 
333*ec779b8eSAndroid Build Coastguard Worker } // namespace android
334*ec779b8eSAndroid Build Coastguard Worker 
335*ec779b8eSAndroid Build Coastguard Worker #pragma pop_macro("LOG_TAG")
336*ec779b8eSAndroid Build Coastguard Worker 
337*ec779b8eSAndroid Build Coastguard Worker #endif // ANDROID_INTERPOLATOR_H
338