optimization: refactor Property class for size optimization.
authorSubhransu Mohanty <sub.mohanty@samsung.com>
Wed, 12 Aug 2020 02:09:44 +0000 (11:09 +0900)
committerJongmin Lee <jm105.lee@samsung.com>
Mon, 17 Aug 2020 22:22:02 +0000 (07:22 +0900)
As only Property<VPointF> in Transform object has the tangent info
added a tag in Property to differentiate form other property.
This will add tangent info to only property that require them.

src/lottie/lottiemodel.h
src/lottie/lottieparser.cpp

index 234d921..8513414 100644 (file)
@@ -8,8 +8,8 @@
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
 
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
 
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
@@ -150,165 +150,166 @@ struct PathData {
     }
 };
 
-template <typename T>
+template <typename T, typename Tag = void>
 struct Value {
-    T     mStartValue;
-    T     mEndValue;
-    T     at(float t) const { return lerp(mStartValue, mEndValue, t); }
+    T     start_;
+    T     end_;
+    T     at(float t) const { return lerp(start_, end_, t); }
     float angle(float) const { return 0; }
-    void cache(){}
+    void  cache() {}
 };
 
-template <>
-struct Value<VPointF> {
-    VPointF mStartValue;
-    VPointF mEndValue;
-    VPointF mInTangent;
-    VPointF mOutTangent;
-    float   mBezierLength{0};
-    bool    mPathKeyFrame{false};
-
-    void cache() {
-        if (mPathKeyFrame) {
-            mInTangent = mEndValue + mInTangent;
-            mOutTangent = mStartValue + mOutTangent;
-            mBezierLength = VBezier::fromPoints(mStartValue, mOutTangent,
-                                                mInTangent, mEndValue).length();
-            if (vIsZero(mBezierLength)) {
+struct Position;
+
+template <typename T>
+struct Value<T, Position> {
+    T     start_;
+    T     end_;
+    T     inTangent_;
+    T     outTangent_;
+    float length_{0};
+    bool  hasTangent_{false};
+
+    void cache()
+    {
+        if (hasTangent_) {
+            inTangent_ = end_ + inTangent_;
+            outTangent_ = start_ + outTangent_;
+            length_ = VBezier::fromPoints(start_, outTangent_, inTangent_, end_)
+                          .length();
+            if (vIsZero(length_)) {
                 // this segment has zero length.
                 // so disable expensive path computaion.
-                mPathKeyFrame = false;
+                hasTangent_ = false;
             }
         }
     }
 
-    VPointF at(float t) const
+    T at(float t) const
     {
-        if (mPathKeyFrame) {
+        if (hasTangent_) {
             /*
              * position along the path calcualated
              * using bezier at progress length (t * bezlen)
              */
             VBezier b =
-                VBezier::fromPoints(mStartValue, mOutTangent,
-                                    mInTangent, mEndValue);
-            return b.pointAt(b.tAtLength(t * mBezierLength, mBezierLength));
+                VBezier::fromPoints(start_, outTangent_, inTangent_, end_);
+            return b.pointAt(b.tAtLength(t * length_, length_));
         }
-        return lerp(mStartValue, mEndValue, t);
+        return lerp(start_, end_, t);
     }
 
     float angle(float t) const
     {
-        if (mPathKeyFrame) {
+        if (hasTangent_) {
             VBezier b =
-                VBezier::fromPoints(mStartValue, mOutTangent,
-                                    mInTangent, mEndValue);
-            return b.angleAt(b.tAtLength(t * mBezierLength, mBezierLength));
+                VBezier::fromPoints(start_, outTangent_, inTangent_, end_);
+            return b.angleAt(b.tAtLength(t * length_, length_));
         }
         return 0;
     }
 };
 
-template <typename T>
-class KeyFrame {
+template <typename T, typename Tag>
+class KeyFrames {
 public:
-    float progress(int frameNo) const
-    {
-        return mInterpolator ? mInterpolator->value((frameNo - mStartFrame) /
-                                                    (mEndFrame - mStartFrame))
-                             : 0;
-    }
-    T     value(int frameNo) const { return mValue.at(progress(frameNo)); }
-    float angle(int frameNo) const { return mValue.angle(progress(frameNo)); }
+    struct Frame {
+        float progress(int frameNo) const
+        {
+            return interpolator_ ? interpolator_->value((frameNo - start_) /
+                                                        (end_ - start_))
+                                 : 0;
+        }
+        T     value(int frameNo) const { return value_.at(progress(frameNo)); }
+        float angle(int frameNo) const
+        {
+            return value_.angle(progress(frameNo));
+        }
 
-public:
-    float          mStartFrame{0};
-    float          mEndFrame{0};
-    VInterpolator *mInterpolator{nullptr};
-    Value<T>       mValue;
-};
+        float          start_{0};
+        float          end_{0};
+        VInterpolator *interpolator_{nullptr};
+        Value<T, Tag>  value_;
+    };
 
-template <typename T>
-class DynamicProperty {
-public:
     T value(int frameNo) const
     {
-        if (mKeyFrames.front().mStartFrame >= frameNo)
-            return mKeyFrames.front().mValue.mStartValue;
-        if (mKeyFrames.back().mEndFrame <= frameNo)
-            return mKeyFrames.back().mValue.mEndValue;
+        if (frames_.front().start_ >= frameNo)
+            return frames_.front().value_.start_;
+        if (frames_.back().end_ <= frameNo) return frames_.back().value_.end_;
 
-        for (const auto &keyFrame : mKeyFrames) {
-            if (frameNo >= keyFrame.mStartFrame && frameNo < keyFrame.mEndFrame)
+        for (const auto &keyFrame : frames_) {
+            if (frameNo >= keyFrame.start_ && frameNo < keyFrame.end_)
                 return keyFrame.value(frameNo);
         }
-        return T();
+        return {};
     }
 
     float angle(int frameNo) const
     {
-        if ((mKeyFrames.front().mStartFrame >= frameNo) ||
-            (mKeyFrames.back().mEndFrame <= frameNo))
+        if ((frames_.front().start_ >= frameNo) ||
+            (frames_.back().end_ <= frameNo))
             return 0;
 
-        for (const auto &keyFrame : mKeyFrames) {
-            if (frameNo >= keyFrame.mStartFrame && frameNo < keyFrame.mEndFrame)
-                return keyFrame.angle(frameNo);
+        for (const auto &frame : frames_) {
+            if (frameNo >= frame.start_ && frameNo < frame.end_)
+                return frame.angle(frameNo);
         }
         return 0;
     }
 
     bool changed(int prevFrame, int curFrame) const
     {
-        auto first = mKeyFrames.front().mStartFrame;
-        auto last = mKeyFrames.back().mEndFrame;
+        auto first = frames_.front().start_;
+        auto last = frames_.back().end_;
 
         return !((first > prevFrame && first > curFrame) ||
                  (last < prevFrame && last < curFrame));
     }
-
-    void cache() {  for (auto &e : mKeyFrames) e.mValue.cache(); }
+    void cache()
+    {
+        for (auto &e : frames_) e.value_.cache();
+    }
 
 public:
-    std::vector<KeyFrame<T>> mKeyFrames;
+    std::vector<Frame> frames_;
 };
 
-template <typename T>
+template <typename T, typename Tag = void>
 class Property {
 public:
-    Property() { construct(impl.mValue, {}); }
-    explicit Property(T value) { construct(impl.mValue, std::move(value)); }
+    using Animation = KeyFrames<T, Tag>;
 
-    const DynamicProperty<T> &animation() const
-    {
-        return *(impl.mAnimInfo.get());
-    }
-    const T &value() const { return impl.mValue; }
+    Property() { construct(impl_.value_, {}); }
+    explicit Property(T value) { construct(impl_.value_, std::move(value)); }
+
+    const Animation &animation() const { return *(impl_.animation_.get()); }
+    const T &        value() const { return impl_.value_; }
 
-    DynamicProperty<T> &animation()
+    Animation &animation()
     {
-        if (mStatic) {
+        if (isValue_) {
             destroy();
-            construct(impl.mAnimInfo, std::make_unique<DynamicProperty<T>>());
-            mStatic = false;
+            construct(impl_.animation_, std::make_unique<Animation>());
+            isValue_ = false;
         }
-        return *(impl.mAnimInfo.get());
+        return *(impl_.animation_.get());
     }
 
     T &value()
     {
-        assert(mStatic);
-        return impl.mValue;
+        assert(isValue_);
+        return impl_.value_;
     }
 
     Property(Property &&other) noexcept
     {
-        if (!other.mStatic) {
-            construct(impl.mAnimInfo, std::move(other.impl.mAnimInfo));
-            mStatic = false;
+        if (!other.isValue_) {
+            construct(impl_.animation_, std::move(other.impl_.animation_));
+            isValue_ = false;
         } else {
-            construct(impl.mValue, std::move(other.impl.mValue));
-            mStatic = true;
+            construct(impl_.value_, std::move(other.impl_.value_));
+            isValue_ = true;
         }
     }
     // delete special member functions
@@ -316,9 +317,9 @@ public:
     Property &operator=(const Property &) = delete;
     Property &operator=(Property &&) = delete;
 
-    ~Property() noexcept { destroy(); }
+    ~Property() { destroy(); }
 
-    bool isStatic() const { return mStatic; }
+    bool isStatic() const { return isValue_; }
 
     T value(int frameNo) const
     {
@@ -326,25 +327,23 @@ public:
     }
 
     // special function only for type T=PathData
-    template <typename SpecialT = PathData>
+    template <typename forT = PathData>
     auto value(int frameNo, VPath &path) const ->
-        typename std::enable_if_t<std::is_same<T, SpecialT>::value, void>
+        typename std::enable_if_t<std::is_same<T, forT>::value, void>
     {
         if (isStatic()) {
             value().toPath(path);
         } else {
-            const auto &vec = animation().mKeyFrames;
-            if (vec.front().mStartFrame >= frameNo)
-                return vec.front().mValue.mStartValue.toPath(path);
-            if (vec.back().mEndFrame <= frameNo)
-                return vec.back().mValue.mEndValue.toPath(path);
+            const auto &vec = animation().frames_;
+            if (vec.front().start_ >= frameNo)
+                return vec.front().value_.start_.toPath(path);
+            if (vec.back().end_ <= frameNo)
+                return vec.back().value_.end_.toPath(path);
 
             for (const auto &keyFrame : vec) {
-                if (frameNo >= keyFrame.mStartFrame &&
-                    frameNo < keyFrame.mEndFrame) {
-                    PathData::lerp(keyFrame.mValue.mStartValue,
-                                   keyFrame.mValue.mEndValue,
-                                   keyFrame.progress(frameNo), path);
+                if (frameNo >= keyFrame.start_ && frameNo < keyFrame.end_) {
+                    T::lerp(keyFrame.value_.start_, keyFrame.value_.end_,
+                            keyFrame.progress(frameNo), path);
                 }
             }
         }
@@ -359,8 +358,11 @@ public:
     {
         return isStatic() ? false : animation().changed(prevFrame, curFrame);
     }
+    void cache()
+    {
+        if (!isStatic()) animation().cache();
+    }
 
-    void cache() { if (!isStatic()) animation().cache();}
 private:
     template <typename Tp>
     void construct(Tp &member, Tp &&val)
@@ -370,24 +372,24 @@ private:
 
     void destroy()
     {
-        if (mStatic) {
-            impl.mValue.~T();
+        if (isValue_) {
+            impl_.value_.~T();
         } else {
             using std::unique_ptr;
-            impl.mAnimInfo.~unique_ptr<DynamicProperty<T>>();
+            impl_.animation_.~unique_ptr<Animation>();
         }
     }
     union details {
-        std::unique_ptr<DynamicProperty<T>> mAnimInfo;
-        T                                   mValue;
+        std::unique_ptr<Animation> animation_;
+        T                          value_;
         details(){};
         details(const details &) = delete;
         details(details &&) = delete;
         details &operator=(details &&) = delete;
         details &operator=(const details &) = delete;
         ~details() noexcept {};
-    } impl;
-    bool mStatic{true};
+    } impl_;
+    bool isValue_{true};
 };
 
 class Path;
@@ -585,12 +587,12 @@ public:
         {
             if (!mExtra) mExtra = std::make_unique<Extra>();
         }
-        Property<float>        mRotation{0};       /* "r" */
-        Property<VPointF>      mScale{{100, 100}}; /* "s" */
-        Property<VPointF>      mPosition;          /* "p" */
-        Property<VPointF>      mAnchor;            /* "a" */
-        Property<float>        mOpacity{100};      /* "o" */
-        std::unique_ptr<Extra> mExtra;
+        Property<float>             mRotation{0};       /* "r" */
+        Property<VPointF>           mScale{{100, 100}}; /* "s" */
+        Property<VPointF, Position> mPosition;          /* "p" */
+        Property<VPointF>           mAnchor;            /* "a" */
+        Property<float>             mOpacity{100};      /* "o" */
+        std::unique_ptr<Extra>      mExtra;
     };
 
     Transform() : Object(Object::Type::Transform) {}
index c53d060..6a1a3cc 100644 (file)
@@ -8,8 +8,8 @@
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
 
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
 
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
@@ -253,14 +253,21 @@ public:
     void getValue(std::vector<VPointF> &v);
     void getValue(model::Repeater::Transform &);
 
+    template <typename T, typename Tag>
+    bool parseKeyFrameValue(const char *key, model::Value<T, Tag> &value)
+    {
+        return false;
+    }
+
     template <typename T>
-    bool parseKeyFrameValue(const char *key, model::Value<T> &value);
-    template <typename T>
-    void parseKeyFrame(model::DynamicProperty<T> &obj);
+    bool parseKeyFrameValue(const char *                      key,
+                            model::Value<T, model::Position> &value);
+    template <typename T, typename Tag>
+    void parseKeyFrame(model::KeyFrames<T, Tag> &obj);
     template <typename T>
     void parseProperty(model::Property<T> &obj);
-    template <typename T>
-    void parsePropertyHelper(model::Property<T> &obj);
+    template <typename T, typename Tag>
+    void parsePropertyHelper(model::Property<T, Tag> &obj);
 
     void parseShapeProperty(model::Property<model::PathData> &obj);
     void parseDashProperty(model::Dash &dash);
@@ -1463,11 +1470,11 @@ model::Repeater *LottieParserImpl::parseReapeaterObject()
             parseProperty(obj->mCopies);
             float maxCopy = 0.0;
             if (!obj->mCopies.isStatic()) {
-                for (auto &keyFrame : obj->mCopies.animation().mKeyFrames) {
-                    if (maxCopy < keyFrame.mValue.mStartValue)
-                        maxCopy = keyFrame.mValue.mStartValue;
-                    if (maxCopy < keyFrame.mValue.mEndValue)
-                        maxCopy = keyFrame.mValue.mEndValue;
+                for (auto &keyFrame : obj->mCopies.animation().frames_) {
+                    if (maxCopy < keyFrame.value_.start_)
+                        maxCopy = keyFrame.value_.start_;
+                    if (maxCopy < keyFrame.value_.end_)
+                        maxCopy = keyFrame.value_.end_;
                 }
             } else {
                 maxCopy = obj->mCopies.value();
@@ -1947,21 +1954,15 @@ VPointF LottieParserImpl::parseInperpolatorPoint()
 }
 
 template <typename T>
-bool LottieParserImpl::parseKeyFrameValue(const char *, model::Value<T> &)
-{
-    return false;
-}
-
-template <>
-bool LottieParserImpl::parseKeyFrameValue(const char *           key,
-                                          model::Value<VPointF> &value)
+bool LottieParserImpl::parseKeyFrameValue(
+    const char *key, model::Value<T, model::Position> &value)
 {
     if (0 == strcmp(key, "ti")) {
-        value.mPathKeyFrame = true;
-        getValue(value.mInTangent);
+        value.hasTangent_ = true;
+        getValue(value.inTangent_);
     } else if (0 == strcmp(key, "to")) {
-        value.mPathKeyFrame = true;
-        getValue(value.mOutTangent);
+        value.hasTangent_ = true;
+        getValue(value.outTangent_);
     } else {
         return false;
     }
@@ -1993,8 +1994,8 @@ VInterpolator *LottieParserImpl::interpolator(VPointF     inTangent,
 /*
  * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/multiDimensionalKeyframed.json
  */
-template <typename T>
-void LottieParserImpl::parseKeyFrame(model::DynamicProperty<T> &obj)
+template <typename T, typename Tag>
+void LottieParserImpl::parseKeyFrame(model::KeyFrames<T, Tag> &obj)
 {
     struct ParsedField {
         std::string interpolatorKey;
@@ -2005,10 +2006,10 @@ void LottieParserImpl::parseKeyFrame(model::DynamicProperty<T> &obj)
     };
 
     EnterObject();
-    ParsedField        parsed;
-    model::KeyFrame<T> keyframe;
-    VPointF            inTangent;
-    VPointF            outTangent;
+    ParsedField                              parsed;
+    typename model::KeyFrames<T, Tag>::Frame keyframe;
+    VPointF                                  inTangent;
+    VPointF                                  outTangent;
 
     while (const char *key = NextObjectKey()) {
         if (0 == strcmp(key, "i")) {
@@ -2017,14 +2018,14 @@ void LottieParserImpl::parseKeyFrame(model::DynamicProperty<T> &obj)
         } else if (0 == strcmp(key, "o")) {
             outTangent = parseInperpolatorPoint();
         } else if (0 == strcmp(key, "t")) {
-            keyframe.mStartFrame = GetDouble();
+            keyframe.start_ = GetDouble();
         } else if (0 == strcmp(key, "s")) {
             parsed.value = true;
-            getValue(keyframe.mValue.mStartValue);
+            getValue(keyframe.value_.start_);
             continue;
         } else if (0 == strcmp(key, "e")) {
             parsed.noEndValue = false;
-            getValue(keyframe.mValue.mEndValue);
+            getValue(keyframe.value_.end_);
             continue;
         } else if (0 == strcmp(key, "n")) {
             if (PeekType() == kStringType) {
@@ -2043,7 +2044,7 @@ void LottieParserImpl::parseKeyFrame(model::DynamicProperty<T> &obj)
                 }
             }
             continue;
-        } else if (parseKeyFrameValue(key, keyframe.mValue)) {
+        } else if (parseKeyFrameValue(key, keyframe.value_)) {
             continue;
         } else if (0 == strcmp(key, "h")) {
             parsed.hold = GetInt();
@@ -2056,29 +2057,26 @@ void LottieParserImpl::parseKeyFrame(model::DynamicProperty<T> &obj)
         }
     }
 
-
-    auto &list = obj.mKeyFrames;
-
+    auto &list = obj.frames_;
     if (!list.empty()) {
         // update the endFrame value of current keyframe
-        list.back().mEndFrame = keyframe.mStartFrame;
+        list.back().end_ = keyframe.start_;
         // if no end value provided, copy start value to previous frame
         if (parsed.value && parsed.noEndValue) {
-            list.back().mValue.mEndValue =
-                keyframe.mValue.mStartValue;
+            list.back().value_.end_ = keyframe.value_.start_;
         }
     }
 
     if (parsed.hold) {
-        keyframe.mValue.mEndValue = keyframe.mValue.mStartValue;
-        keyframe.mEndFrame = keyframe.mStartFrame;
+        keyframe.value_.end_ = keyframe.value_.start_;
+        keyframe.end_ = keyframe.start_;
         list.push_back(std::move(keyframe));
     } else if (parsed.interpolator) {
-        keyframe.mInterpolator = interpolator(
+        keyframe.interpolator_ = interpolator(
             inTangent, outTangent, std::move(parsed.interpolatorKey));
         list.push_back(std::move(keyframe));
     } else {
-        // Last frame ignore
+        // its the last frame discard.
     }
 }
 
@@ -2118,8 +2116,8 @@ void LottieParserImpl::parseShapeProperty(model::Property<model::PathData> &obj)
     obj.cache();
 }
 
-template <typename T>
-void LottieParserImpl::parsePropertyHelper(model::Property<T> &obj)
+template <typename T, typename Tag>
+void LottieParserImpl::parsePropertyHelper(model::Property<T, Tag> &obj)
 {
     if (PeekType() == kNumberType) {
         if (!obj.isStatic()) {