Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c5b6959f authored by Mike Reed's avatar Mike Reed Committed by Android (Google) Code Review
Browse files

Merge "hwui's own impl for interpolator" into sc-dev

parents fc191ab4 74065278
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -503,6 +503,7 @@ cc_defaults {
        "RenderProperties.cpp",
        "RootRenderNode.cpp",
        "SkiaCanvas.cpp",
        "SkiaInterpolator.cpp",
        "VectorDrawable.cpp",
    ],

+273 −0
Original line number Diff line number Diff line
/*
 * 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;
}
+119 −0
Original line number Diff line number Diff line
/*
 * 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
+8 −8
Original line number Diff line number Diff line
#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);