diff options
author | 2021-04-12 20:48:21 +0000 | |
---|---|---|
committer | 2021-04-12 20:48:21 +0000 | |
commit | c5b6959fb922b430d48015c80f1e322b08f267ce (patch) | |
tree | 26e664a265aa3456cd5ef79891120818c73d1af5 | |
parent | fc191ab4c232ea567562831b0690d8f9fe84fb00 (diff) | |
parent | 7406527817222f0a9367458bee87a8bfd506d1d7 (diff) |
Merge "hwui's own impl for interpolator" into sc-dev
-rw-r--r-- | libs/hwui/Android.bp | 1 | ||||
-rw-r--r-- | libs/hwui/SkiaInterpolator.cpp | 273 | ||||
-rw-r--r-- | libs/hwui/SkiaInterpolator.h | 119 | ||||
-rw-r--r-- | libs/hwui/jni/Interpolator.cpp | 16 |
4 files changed, 401 insertions, 8 deletions
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index c75b21f739cd..9270901bcacd 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -503,6 +503,7 @@ cc_defaults { "RenderProperties.cpp", "RootRenderNode.cpp", "SkiaCanvas.cpp", + "SkiaInterpolator.cpp", "VectorDrawable.cpp", ], diff --git a/libs/hwui/SkiaInterpolator.cpp b/libs/hwui/SkiaInterpolator.cpp new file mode 100644 index 000000000000..0695dd1ab218 --- /dev/null +++ b/libs/hwui/SkiaInterpolator.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkiaInterpolator.h" + +#include "include/core/SkMath.h" +#include "include/private/SkFixed.h" +#include "include/private/SkMalloc.h" +#include "include/private/SkTo.h" +#include "src/core/SkTSearch.h" + +typedef int Dot14; +#define Dot14_ONE (1 << 14) +#define Dot14_HALF (1 << 13) + +#define Dot14ToFloat(x) ((x) / 16384.f) + +static inline Dot14 Dot14Mul(Dot14 a, Dot14 b) { + return (a * b + Dot14_HALF) >> 14; +} + +static inline Dot14 eval_cubic(Dot14 t, Dot14 A, Dot14 B, Dot14 C) { + return Dot14Mul(Dot14Mul(Dot14Mul(C, t) + B, t) + A, t); +} + +static inline Dot14 pin_and_convert(float x) { + if (x <= 0) { + return 0; + } + if (x >= SK_Scalar1) { + return Dot14_ONE; + } + return SkScalarToFixed(x) >> 2; +} + +static float SkUnitCubicInterp(float value, float bx, float by, float cx, float cy) { + // pin to the unit-square, and convert to 2.14 + Dot14 x = pin_and_convert(value); + + if (x == 0) return 0; + if (x == Dot14_ONE) return SK_Scalar1; + + Dot14 b = pin_and_convert(bx); + Dot14 c = pin_and_convert(cx); + + // Now compute our coefficients from the control points + // t -> 3b + // t^2 -> 3c - 6b + // t^3 -> 3b - 3c + 1 + Dot14 A = 3 * b; + Dot14 B = 3 * (c - 2 * b); + Dot14 C = 3 * (b - c) + Dot14_ONE; + + // Now search for a t value given x + Dot14 t = Dot14_HALF; + Dot14 dt = Dot14_HALF; + for (int i = 0; i < 13; i++) { + dt >>= 1; + Dot14 guess = eval_cubic(t, A, B, C); + if (x < guess) { + t -= dt; + } else { + t += dt; + } + } + + // Now we have t, so compute the coeff for Y and evaluate + b = pin_and_convert(by); + c = pin_and_convert(cy); + A = 3 * b; + B = 3 * (c - 2 * b); + C = 3 * (b - c) + Dot14_ONE; + return SkFixedToScalar(eval_cubic(t, A, B, C) << 2); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +SkiaInterpolatorBase::SkiaInterpolatorBase() { + fStorage = nullptr; + fTimes = nullptr; + SkDEBUGCODE(fTimesArray = nullptr;) +} + +SkiaInterpolatorBase::~SkiaInterpolatorBase() { + if (fStorage) { + sk_free(fStorage); + } +} + +void SkiaInterpolatorBase::reset(int elemCount, int frameCount) { + fFlags = 0; + fElemCount = SkToU8(elemCount); + fFrameCount = SkToS16(frameCount); + fRepeat = SK_Scalar1; + if (fStorage) { + sk_free(fStorage); + fStorage = nullptr; + fTimes = nullptr; + SkDEBUGCODE(fTimesArray = nullptr); + } +} + +/* Each value[] run is formatted as: + <time (in msec)> + <blend> + <data[fElemCount]> + + Totaling fElemCount+2 entries per keyframe +*/ + +bool SkiaInterpolatorBase::getDuration(SkMSec* startTime, SkMSec* endTime) const { + if (fFrameCount == 0) { + return false; + } + + if (startTime) { + *startTime = fTimes[0].fTime; + } + if (endTime) { + *endTime = fTimes[fFrameCount - 1].fTime; + } + return true; +} + +float SkiaInterpolatorBase::ComputeRelativeT(SkMSec time, SkMSec prevTime, SkMSec nextTime, + const float blend[4]) { + SkASSERT(time > prevTime && time < nextTime); + + float t = (float)(time - prevTime) / (float)(nextTime - prevTime); + return blend ? SkUnitCubicInterp(t, blend[0], blend[1], blend[2], blend[3]) : t; +} + +SkiaInterpolatorBase::Result SkiaInterpolatorBase::timeToT(SkMSec time, float* T, int* indexPtr, + bool* exactPtr) const { + SkASSERT(fFrameCount > 0); + Result result = kNormal_Result; + if (fRepeat != SK_Scalar1) { + SkMSec startTime = 0, endTime = 0; // initialize to avoid warning + this->getDuration(&startTime, &endTime); + SkMSec totalTime = endTime - startTime; + SkMSec offsetTime = time - startTime; + endTime = SkScalarFloorToInt(fRepeat * totalTime); + if (offsetTime >= endTime) { + float fraction = SkScalarFraction(fRepeat); + offsetTime = fraction == 0 && fRepeat > 0 + ? totalTime + : (SkMSec)SkScalarFloorToInt(fraction * totalTime); + result = kFreezeEnd_Result; + } else { + int mirror = fFlags & kMirror; + offsetTime = offsetTime % (totalTime << mirror); + if (offsetTime > totalTime) { // can only be true if fMirror is true + offsetTime = (totalTime << 1) - offsetTime; + } + } + time = offsetTime + startTime; + } + + int index = SkTSearch<SkMSec>(&fTimes[0].fTime, fFrameCount, time, sizeof(SkTimeCode)); + + bool exact = true; + + if (index < 0) { + index = ~index; + if (index == 0) { + result = kFreezeStart_Result; + } else if (index == fFrameCount) { + if (fFlags & kReset) { + index = 0; + } else { + index -= 1; + } + result = kFreezeEnd_Result; + } else { + exact = false; + } + } + SkASSERT(index < fFrameCount); + const SkTimeCode* nextTime = &fTimes[index]; + SkMSec nextT = nextTime[0].fTime; + if (exact) { + *T = 0; + } else { + SkMSec prevT = nextTime[-1].fTime; + *T = ComputeRelativeT(time, prevT, nextT, nextTime[-1].fBlend); + } + *indexPtr = index; + *exactPtr = exact; + return result; +} + +SkiaInterpolator::SkiaInterpolator() { + INHERITED::reset(0, 0); + fValues = nullptr; + SkDEBUGCODE(fScalarsArray = nullptr;) +} + +SkiaInterpolator::SkiaInterpolator(int elemCount, int frameCount) { + SkASSERT(elemCount > 0); + this->reset(elemCount, frameCount); +} + +void SkiaInterpolator::reset(int elemCount, int frameCount) { + INHERITED::reset(elemCount, frameCount); + fStorage = sk_malloc_throw((sizeof(float) * elemCount + sizeof(SkTimeCode)) * frameCount); + fTimes = (SkTimeCode*)fStorage; + fValues = (float*)((char*)fStorage + sizeof(SkTimeCode) * frameCount); +#ifdef SK_DEBUG + fTimesArray = (SkTimeCode(*)[10])fTimes; + fScalarsArray = (float(*)[10])fValues; +#endif +} + +#define SK_Fixed1Third (SK_Fixed1 / 3) +#define SK_Fixed2Third (SK_Fixed1 * 2 / 3) + +static const float gIdentityBlend[4] = {0.33333333f, 0.33333333f, 0.66666667f, 0.66666667f}; + +bool SkiaInterpolator::setKeyFrame(int index, SkMSec time, const float values[], + const float blend[4]) { + SkASSERT(values != nullptr); + + if (blend == nullptr) { + blend = gIdentityBlend; + } + + bool success = ~index == SkTSearch<SkMSec>(&fTimes->fTime, index, time, sizeof(SkTimeCode)); + SkASSERT(success); + if (success) { + SkTimeCode* timeCode = &fTimes[index]; + timeCode->fTime = time; + memcpy(timeCode->fBlend, blend, sizeof(timeCode->fBlend)); + float* dst = &fValues[fElemCount * index]; + memcpy(dst, values, fElemCount * sizeof(float)); + } + return success; +} + +SkiaInterpolator::Result SkiaInterpolator::timeToValues(SkMSec time, float values[]) const { + float T; + int index; + bool exact; + Result result = timeToT(time, &T, &index, &exact); + if (values) { + const float* nextSrc = &fValues[index * fElemCount]; + + if (exact) { + memcpy(values, nextSrc, fElemCount * sizeof(float)); + } else { + SkASSERT(index > 0); + + const float* prevSrc = nextSrc - fElemCount; + + for (int i = fElemCount - 1; i >= 0; --i) { + values[i] = SkScalarInterp(prevSrc[i], nextSrc[i], T); + } + } + } + return result; +} diff --git a/libs/hwui/SkiaInterpolator.h b/libs/hwui/SkiaInterpolator.h new file mode 100644 index 000000000000..c03f502528be --- /dev/null +++ b/libs/hwui/SkiaInterpolator.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SkiaInterpolator_DEFINED +#define SkiaInterpolator_DEFINED + +#include "include/private/SkTo.h" + +class SkiaInterpolatorBase { +public: + enum Result { kNormal_Result, kFreezeStart_Result, kFreezeEnd_Result }; + +protected: + SkiaInterpolatorBase(); + ~SkiaInterpolatorBase(); + +public: + void reset(int elemCount, int frameCount); + + /** Return the start and end time for this interpolator. + If there are no key frames, return false. + @param startTime If not null, returns the time (in milliseconds) of the + first keyframe. If there are no keyframes, this param + is ignored (left unchanged). + @param endTime If not null, returns the time (in milliseconds) of the + last keyframe. If there are no keyframes, this parameter + is ignored (left unchanged). + @return True if there are key frames, or false if there are none. + */ + bool getDuration(uint32_t* startTime, uint32_t* endTime) const; + + /** Set the whether the repeat is mirrored. + @param mirror If true, the odd repeats interpolate from the last key + frame and the first. + */ + void setMirror(bool mirror) { fFlags = SkToU8((fFlags & ~kMirror) | (int)mirror); } + + /** Set the repeat count. The repeat count may be fractional. + @param repeatCount Multiplies the total time by this scalar. + */ + void setRepeatCount(float repeatCount) { fRepeat = repeatCount; } + + /** Set the whether the repeat is mirrored. + @param reset If true, the odd repeats interpolate from the last key + frame and the first. + */ + void setReset(bool reset) { fFlags = SkToU8((fFlags & ~kReset) | (int)reset); } + + Result timeToT(uint32_t time, float* T, int* index, bool* exact) const; + +protected: + enum Flags { kMirror = 1, kReset = 2, kHasBlend = 4 }; + static float ComputeRelativeT(uint32_t time, uint32_t prevTime, uint32_t nextTime, + const float blend[4] = nullptr); + int16_t fFrameCount; + uint8_t fElemCount; + uint8_t fFlags; + float fRepeat; + struct SkTimeCode { + uint32_t fTime; + float fBlend[4]; + }; + SkTimeCode* fTimes; // pointer into fStorage + void* fStorage; +#ifdef SK_DEBUG + SkTimeCode (*fTimesArray)[10]; +#endif +}; + +class SkiaInterpolator : public SkiaInterpolatorBase { +public: + SkiaInterpolator(); + SkiaInterpolator(int elemCount, int frameCount); + + void reset(int elemCount, int frameCount); + + /** Add or replace a key frame, copying the values[] data into the + interpolator. + @param index The index of this frame (frames must be ordered by time) + @param time The millisecond time for this frame + @param values The array of values [elemCount] for this frame. The data + is copied into the interpolator. + @param blend A positive scalar specifying how to blend between this + and the next key frame. [0...1) is a cubic lag/log/lag + blend (slow to change at the beginning and end) + 1 is a linear blend (default) + */ + bool setKeyFrame(int index, uint32_t time, const float values[], + const float blend[4] = nullptr); + + /** Return the computed values given the specified time. Return whether + those values are the result of pinning to either the first + (kFreezeStart) or last (kFreezeEnd), or from interpolated the two + nearest key values (kNormal). + @param time The time to sample (in milliseconds) + @param (may be null) where to write the computed values. + */ + Result timeToValues(uint32_t time, float values[] = nullptr) const; + +private: + float* fValues; // pointer into fStorage + + using INHERITED = SkiaInterpolatorBase; +}; + +#endif diff --git a/libs/hwui/jni/Interpolator.cpp b/libs/hwui/jni/Interpolator.cpp index 146d634a297c..fc3d70b87f5a 100644 --- a/libs/hwui/jni/Interpolator.cpp +++ b/libs/hwui/jni/Interpolator.cpp @@ -1,26 +1,26 @@ #include "GraphicsJNI.h" -#include "SkInterpolator.h" +#include "SkiaInterpolator.h" static jlong Interpolator_constructor(JNIEnv* env, jobject clazz, jint valueCount, jint frameCount) { - return reinterpret_cast<jlong>(new SkInterpolator(valueCount, frameCount)); + return reinterpret_cast<jlong>(new SkiaInterpolator(valueCount, frameCount)); } static void Interpolator_destructor(JNIEnv* env, jobject clazz, jlong interpHandle) { - SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle); + SkiaInterpolator* interp = reinterpret_cast<SkiaInterpolator*>(interpHandle); delete interp; } static void Interpolator_reset(JNIEnv* env, jobject clazz, jlong interpHandle, jint valueCount, jint frameCount) { - SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle); + SkiaInterpolator* interp = reinterpret_cast<SkiaInterpolator*>(interpHandle); interp->reset(valueCount, frameCount); } static void Interpolator_setKeyFrame(JNIEnv* env, jobject clazz, jlong interpHandle, jint index, jint msec, jfloatArray valueArray, jfloatArray blendArray) { - SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle); + SkiaInterpolator* interp = reinterpret_cast<SkiaInterpolator*>(interpHandle); AutoJavaFloatArray autoValues(env, valueArray); AutoJavaFloatArray autoBlend(env, blendArray, 4); @@ -36,7 +36,7 @@ static void Interpolator_setKeyFrame(JNIEnv* env, jobject clazz, jlong interpHan static void Interpolator_setRepeatMirror(JNIEnv* env, jobject clazz, jlong interpHandle, jfloat repeatCount, jboolean mirror) { - SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle); + SkiaInterpolator* interp = reinterpret_cast<SkiaInterpolator*>(interpHandle); if (repeatCount > 32000) repeatCount = 32000; @@ -46,8 +46,8 @@ static void Interpolator_setRepeatMirror(JNIEnv* env, jobject clazz, jlong inter static jint Interpolator_timeToValues(JNIEnv* env, jobject clazz, jlong interpHandle, jint msec, jfloatArray valueArray) { - SkInterpolator* interp = reinterpret_cast<SkInterpolator*>(interpHandle); - SkInterpolatorBase::Result result; + SkiaInterpolator* interp = reinterpret_cast<SkiaInterpolator*>(interpHandle); + SkiaInterpolator::Result result; float* values = valueArray ? env->GetFloatArrayElements(valueArray, NULL) : NULL; result = interp->timeToValues(msec, (SkScalar*)values); |