* 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,
}
};
-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
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
{
}
// 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);
}
}
}
{
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)
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;
{
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) {}
* 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,
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);
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();
}
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;
}
/*
* 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;
};
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")) {
} 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) {
}
}
continue;
- } else if (parseKeyFrameValue(key, keyframe.mValue)) {
+ } else if (parseKeyFrameValue(key, keyframe.value_)) {
continue;
} else if (0 == strcmp(key, "h")) {
parsed.hold = GetInt();
}
}
-
- 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.
}
}
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()) {