6 #include<unordered_map>
9 #include"vinterpolator.h"
17 class LOTCompositionData;
19 class LOTTransformData;
20 class LOTShapeGroupData;
25 class LOTRepeaterData;
31 class LottieShapeData;
32 class LOTPolystarData;
44 enum class LayerType {
56 LottieColor() = default;
57 LottieColor(float red, float green , float blue):r(red), g(green),b(blue){}
58 VColor toColor(float a=1){ return VColor((255 * r), (255 * g), (255 * b), (255 * a));}
59 friend inline LottieColor operator+(const LottieColor &c1, const LottieColor &c2);
60 friend inline LottieColor operator-(const LottieColor &c1, const LottieColor &c2);
67 inline LottieColor operator-(const LottieColor &c1, const LottieColor &c2)
69 return LottieColor(c1.r - c2.r, c1.g - c2.g, c1.b - c2.b);
71 inline LottieColor operator+(const LottieColor &c1, const LottieColor &c2)
73 return LottieColor(c1.r + c2.r, c1.g + c2.g, c1.b + c2.b);
76 inline const LottieColor operator*(const LottieColor &c, float m)
77 { return LottieColor(c.r*m, c.g*m, c.b*m); }
79 inline const LottieColor operator*(float m, const LottieColor &c)
80 { return LottieColor(c.r*m, c.g*m, c.b*m); }
85 void reserve(int size) {
86 mPoints.reserve(mPoints.size() + size);
88 void toPath(VPath& path) {
91 if (mPoints.empty()) return;
93 int size = mPoints.size();
94 const VPointF *points = mPoints.data();
95 /* reserve exact memory requirement at once
96 * ptSize = size + 1(size + close)
97 * elmSize = size/3 cubic + 1 move + 1 close
99 path.reserve(size + 1 , size/3 + 2);
100 path.moveTo(points[0]);
101 for (int i = 1 ; i < size; i+=3) {
102 path.cubicTo(points[i], points[i+1], points[i+2]);
108 std::vector<VPointF> mPoints;
109 bool mClosed = false; /* "c" */
115 inline T lerp(const T& start, const T& end, float t)
117 return start + t * (end - start);
120 inline LottieShapeData lerp(const LottieShapeData& start, const LottieShapeData& end, float t)
122 if (start.mPoints.size() != start.mPoints.size())
123 return LottieShapeData();
125 LottieShapeData result;
126 result.reserve(start.mPoints.size());
127 for (unsigned int i = 0 ; i < start.mPoints.size(); i++) {
128 result.mPoints.push_back(start.mPoints[i] + t * (end.mPoints[i] - start.mPoints[i]));
133 template <typename T>
134 struct LOTKeyFrameValue
138 T value(float t) const {
139 return lerp(mStartValue, mEndValue, t);
141 float angle(float ) const { return 0;}
145 struct LOTKeyFrameValue<VPointF>
151 bool mPathKeyFrame = false;
153 VPointF value(float t) const {
156 * position along the path calcualated
157 * using bezier at progress length (t * bezlen)
159 VBezier b = VBezier::fromPoints(mStartValue, mStartValue + mOutTangent,
160 mEndValue + mInTangent, mEndValue);
161 return b.pointAt(b.tAtLength(t * b.length()));
164 return lerp(mStartValue, mEndValue, t);
168 float angle(float t) const {
170 VBezier b = VBezier::fromPoints(mStartValue, mStartValue + mOutTangent,
171 mEndValue + mInTangent, mEndValue);
172 return b.angleAt(b.tAtLength(t * b.length()));
183 float progress(int frameNo) const {
184 return mInterpolator->value((frameNo - mStartFrame) / (mEndFrame - mStartFrame));
186 T value(int frameNo) const {
187 return mValue.value(progress(frameNo));
189 float angle(int frameNo) const {
190 return mValue.angle(progress(frameNo));
194 float mStartFrame{0};
196 std::shared_ptr<VInterpolator> mInterpolator;
197 LOTKeyFrameValue<T> mValue;
204 T value(int frameNo) const {
205 if (mKeyFrames.front().mStartFrame >= frameNo)
206 return mKeyFrames.front().mValue.mStartValue;
207 if(mKeyFrames.back().mEndFrame <= frameNo)
208 return mKeyFrames.back().mValue.mEndValue;
210 for(const auto &keyFrame : mKeyFrames) {
211 if (frameNo >= keyFrame.mStartFrame && frameNo < keyFrame.mEndFrame)
212 return keyFrame.value(frameNo);
217 float angle(int frameNo) const {
218 if ((mKeyFrames.front().mStartFrame >= frameNo) ||
219 (mKeyFrames.back().mEndFrame <= frameNo) )
222 for(const auto &keyFrame : mKeyFrames) {
223 if (frameNo >= keyFrame.mStartFrame && frameNo < keyFrame.mEndFrame)
224 return keyFrame.angle(frameNo);
229 bool changed(int prevFrame, int curFrame) {
230 int first = mKeyFrames.front().mStartFrame;
231 int last = mKeyFrames.back().mEndFrame;
233 if ((first > prevFrame && first > curFrame) ||
234 (last < prevFrame && last < curFrame)) {
242 std::vector<LOTKeyFrame<T>> mKeyFrames;
249 LOTAnimatable():mValue(),mAnimInfo(nullptr){}
250 LOTAnimatable(const T &value): mValue(value){}
251 bool isStatic() const {if (mAnimInfo) return false; else return true;}
252 T value(int frameNo) const {
253 return isStatic() ? mValue : mAnimInfo->value(frameNo);
255 float angle(int frameNo) const {
256 return isStatic() ? 0 : mAnimInfo->angle(frameNo);
258 bool changed(int prevFrame, int curFrame) {
259 return isStatic() ? false : mAnimInfo->changed(prevFrame, curFrame);
263 int mPropertyIndex{0}; /* "ix" */
264 std::unique_ptr<LOTAnimInfo<T>> mAnimInfo;
267 enum class LottieBlendMode
275 class LOTDataVisitor;
295 LOTData(LOTData::Type type): mType(type){}
296 inline LOTData::Type type() const {return mType;}
297 bool isStatic() const{return mStatic;}
298 void setStatic(bool value) {mStatic = value;}
299 bool hidden() const {return mHidden;}
306 class LOTGroupData: public LOTData
309 LOTGroupData(LOTData::Type type):LOTData(type){}
311 std::vector<std::shared_ptr<LOTData>> mChildren;
312 std::shared_ptr<LOTTransformData> mTransform;
315 class LOTShapeGroupData : public LOTGroupData
318 LOTShapeGroupData():LOTGroupData(LOTData::Type::ShapeGroup){}
324 int mAssetType; //lottie asset type (precomp/char/image)
325 std::string mRefId; // ref id
326 std::vector<std::shared_ptr<LOTData>> mLayers;
329 class LOTLayerData : public LOTGroupData
332 LOTLayerData():LOTGroupData(LOTData::Type::Layer){}
333 bool hasPathOperator() const noexcept {return mHasPathOperator;}
334 bool hasGradient() const noexcept {return mHasGradient;}
335 bool hasMask() const noexcept {return mHasMask;}
336 bool hasRepeater() const noexcept {return mHasRepeater;}
337 bool root() const noexcept {return mRoot;}
338 int id() const noexcept{ return mId;}
339 int parentId() const noexcept{ return mParentId;}
340 int inFrame() const noexcept{return mInFrame;}
341 int outFrame() const noexcept{return mOutFrame;}
342 int startFrame() const noexcept{return mStartFrame;}
343 int solidWidth() const noexcept{return mSolidLayer.mWidth;}
344 int solidHeight() const noexcept{return mSolidLayer.mHeight;}
345 LottieColor solidColor() const noexcept{return mSolidLayer.mColor;}
346 bool autoOrient() const noexcept{return mAutoOrient;}
347 int timeRemap(int frameNo) const;
355 MatteType mMatteType{MatteType::None};
357 LayerType mLayerType{LayerType::Null}; //lottie layer type (solid/shape/precomp)
358 int mParentId{-1}; // Lottie the id of the parent in the composition
359 int mId{-1}; // Lottie the group id used for parenting.
363 LottieBlendMode mBlendMode{LottieBlendMode::Normal};
364 float mTimeStreatch{1.0f};
365 std::string mPreCompRefId;
366 LOTAnimatable<float> mTimeRemap; /* "tm" */
367 SolidLayer mSolidLayer;
368 bool mHasPathOperator{false};
369 bool mHasMask{false};
370 bool mHasRepeater{false};
371 bool mHasGradient{false};
373 bool mAutoOrient{false};
374 std::vector<std::shared_ptr<LOTMaskData>> mMasks;
375 LOTCompositionData *mCompRef{nullptr};
378 class LOTCompositionData : public LOTData
381 LOTCompositionData():LOTData(LOTData::Type::Composition){}
382 double duration() const {
383 return isStatic() ? startFrame() :
384 frameDuration() / frameRate(); // in second
386 size_t frameAtPos(double pos) const {
387 if (pos < 0) pos = 0;
388 if (pos > 1) pos = 1;
389 return isStatic() ? 0 : pos * frameDuration();
391 long frameAtTime(double timeInSec) const {
392 return isStatic() ? startFrame() : frameAtPos(timeInSec / duration());
394 long frameDuration() const {return mEndFrame - mStartFrame -1;}
395 float frameRate() const {return mFrameRate;}
396 long startFrame() const {return mStartFrame;}
397 long endFrame() const {return mEndFrame;}
398 VSize size() const {return mSize;}
399 void processRepeaterObjects();
401 std::string mVersion;
405 float mFrameRate{60};
406 LottieBlendMode mBlendMode{LottieBlendMode::Normal};
407 std::shared_ptr<LOTLayerData> mRootLayer;
408 std::unordered_map<std::string,
409 std::shared_ptr<VInterpolator>> mInterpolatorCache;
410 std::unordered_map<std::string,
411 std::shared_ptr<LOTAsset>> mAssets;
416 * TimeRemap has the value in time domain(in sec)
417 * To get the proper mapping first we get the mapped time at the current frame Number
418 * then we need to convert mapped time to frame number using the composition time line
419 * Ex: at frame 10 the mappend time is 0.5(500 ms) which will be convert to frame number
420 * 30 if the frame rate is 60. or will result to frame number 15 if the frame rate is 30.
422 inline int LOTLayerData::timeRemap(int frameNo) const
424 frameNo = mTimeRemap.isStatic() ? frameNo :
425 mCompRef->frameAtTime(mTimeRemap.value(frameNo));
426 /* Apply time streatch if it has any.
427 * Time streatch is just a factor by which the animation will speedup or slow
428 * down with respect to the overal animation.
429 * Time streach factor is already applied to the layers inFrame and outFrame.
430 * @TODO need to find out if timestreatch also affects the in and out frame of the
431 * child layers or not. */
432 return frameNo / mTimeStreatch;
437 LOTAnimatable<float> mRx{0};
438 LOTAnimatable<float> mRy{0};
439 LOTAnimatable<float> mRz{0};
442 class LOTTransformData : public LOTData
445 LOTTransformData():LOTData(LOTData::Type::Transform),mScale({100, 100}){}
446 VMatrix matrix(int frameNo, bool autoOrient = false) const;
447 VMatrix matrixForRepeater(int frameNo, float multiplier) const;
448 float opacity(int frameNo) const { return mOpacity.value(frameNo)/100;}
449 float startOpacity(int frameNo) const { return mStartOpacity.value(frameNo)/100;}
450 float endOpacity(int frameNo) const { return mEndOpacity.value(frameNo)/100;}
452 bool staticMatrix() const {return mStaticMatrix;}
453 bool ddd() const {return m3D ? true : false;}
455 VMatrix computeMatrix(int frameNo, bool autoOrient = false) const;
457 std::unique_ptr<LOT3DData> m3D;
458 LOTAnimatable<float> mRotation{0}; /* "r" */
459 LOTAnimatable<VPointF> mScale; /* "s" */
460 LOTAnimatable<VPointF> mPosition; /* "p" */
461 LOTAnimatable<float> mX{0};
462 LOTAnimatable<float> mY{0};
463 LOTAnimatable<VPointF> mAnchor; /* "a" */
464 LOTAnimatable<float> mOpacity{100}; /* "o" */
465 LOTAnimatable<float> mSkew{0}; /* "sk" */
466 LOTAnimatable<float> mSkewAxis{0}; /* "sa" */
467 LOTAnimatable<float> mStartOpacity{100}; /* "so" */
468 LOTAnimatable<float> mEndOpacity{100}; /* "eo" */
469 bool mStaticMatrix{true};
470 bool mSeparate{false};
471 VMatrix mCachedMatrix;
474 class LOTFillData : public LOTData
477 LOTFillData():LOTData(LOTData::Type::Fill){}
478 float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
479 FillRule fillRule() const {return mFillRule;}
481 FillRule mFillRule{FillRule::Winding}; /* "r" */
482 LOTAnimatable<LottieColor> mColor; /* "c" */
483 LOTAnimatable<int> mOpacity{100}; /* "o" */
484 bool mEnabled{true}; /* "fillEnabled" */
487 struct LOTDashProperty
489 LOTAnimatable<float> mDashArray[5]; /* "d" "g" "o"*/
494 class LOTStrokeData : public LOTData
497 LOTStrokeData():LOTData(LOTData::Type::Stroke){}
498 float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
499 float width(int frameNo) const {return mWidth.value(frameNo);}
500 CapStyle capStyle() const {return mCapStyle;}
501 JoinStyle joinStyle() const {return mJoinStyle;}
502 float meterLimit() const{return mMeterLimit;}
503 bool hasDashInfo() const { return !(mDash.mDashCount == 0);}
504 int getDashInfo(int frameNo, float *array) const;
506 LOTAnimatable<LottieColor> mColor; /* "c" */
507 LOTAnimatable<int> mOpacity{100}; /* "o" */
508 LOTAnimatable<float> mWidth{0}; /* "w" */
509 CapStyle mCapStyle{CapStyle::Flat}; /* "lc" */
510 JoinStyle mJoinStyle{JoinStyle::Miter}; /* "lj" */
511 float mMeterLimit{0}; /* "ml" */
512 LOTDashProperty mDash;
513 bool mEnabled{true}; /* "fillEnabled" */
519 friend inline LottieGradient operator+(const LottieGradient &g1, const LottieGradient &g2);
520 friend inline LottieGradient operator-(const LottieGradient &g1, const LottieGradient &g2);
521 friend inline LottieGradient operator*(float m, const LottieGradient &g);
523 std::vector<float> mGradient;
526 inline LottieGradient operator+(const LottieGradient &g1, const LottieGradient &g2)
528 if (g1.mGradient.size() != g2.mGradient.size())
532 newG.mGradient = g1.mGradient;
534 auto g2It = g2.mGradient.begin();
535 for(auto &i : newG.mGradient) {
543 inline LottieGradient operator-(const LottieGradient &g1, const LottieGradient &g2)
545 if (g1.mGradient.size() != g2.mGradient.size())
548 newG.mGradient = g1.mGradient;
550 auto g2It = g2.mGradient.begin();
551 for(auto &i : newG.mGradient) {
559 inline LottieGradient operator*(float m, const LottieGradient &g)
562 newG.mGradient = g.mGradient;
564 for(auto &i : newG.mGradient) {
572 class LOTGradient : public LOTData
575 LOTGradient(LOTData::Type type):LOTData(type){}
576 inline float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
577 void update(std::unique_ptr<VGradient> &grad, int frameNo);
580 void populate(VGradientStops &stops, int frameNo);
582 int mGradientType{1}; /* "t" Linear=1 , Radial = 2*/
583 LOTAnimatable<VPointF> mStartPoint; /* "s" */
584 LOTAnimatable<VPointF> mEndPoint; /* "e" */
585 LOTAnimatable<float> mHighlightLength{0}; /* "h" */
586 LOTAnimatable<float> mHighlightAngle{0}; /* "a" */
587 LOTAnimatable<int> mOpacity{0}; /* "o" */
588 LOTAnimatable<LottieGradient> mGradient; /* "g" */
589 int mColorPoints{-1};
590 bool mEnabled{true}; /* "fillEnabled" */
593 class LOTGFillData : public LOTGradient
596 LOTGFillData():LOTGradient(LOTData::Type::GFill){}
597 FillRule fillRule() const {return mFillRule;}
599 FillRule mFillRule{FillRule::Winding}; /* "r" */
602 class LOTGStrokeData : public LOTGradient
605 LOTGStrokeData():LOTGradient(LOTData::Type::GStroke){}
606 float width(int frameNo) const {return mWidth.value(frameNo);}
607 CapStyle capStyle() const {return mCapStyle;}
608 JoinStyle joinStyle() const {return mJoinStyle;}
609 float meterLimit() const{return mMeterLimit;}
610 bool hasDashInfo() const { return !(mDash.mDashCount == 0);}
611 int getDashInfo(int frameNo, float *array) const;
613 LOTAnimatable<float> mWidth; /* "w" */
614 CapStyle mCapStyle{CapStyle::Flat}; /* "lc" */
615 JoinStyle mJoinStyle{JoinStyle::Miter}; /* "lj" */
616 float mMeterLimit{0}; /* "ml" */
617 LOTDashProperty mDash;
620 class LOTPath : public LOTData
623 LOTPath(LOTData::Type type):LOTData(type){}
624 VPath::Direction direction() { if (mDirection == 3) return VPath::Direction::CCW;
625 else return VPath::Direction::CW;}
630 class LOTShapeData : public LOTPath
633 LOTShapeData():LOTPath(LOTData::Type::Shape){}
636 LOTAnimatable<LottieShapeData> mShape;
649 float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
650 bool isStatic() const {return mIsStatic;}
652 LOTAnimatable<LottieShapeData> mShape;
653 LOTAnimatable<float> mOpacity;
655 bool mIsStatic{true};
656 LOTMaskData::Mode mMode;
659 class LOTRectData : public LOTPath
662 LOTRectData():LOTPath(LOTData::Type::Rect){}
664 LOTAnimatable<VPointF> mPos;
665 LOTAnimatable<VPointF> mSize;
666 LOTAnimatable<float> mRound{0};
669 class LOTEllipseData : public LOTPath
672 LOTEllipseData():LOTPath(LOTData::Type::Ellipse){}
674 LOTAnimatable<VPointF> mPos;
675 LOTAnimatable<VPointF> mSize;
678 class LOTPolystarData : public LOTPath
681 enum class PolyType {
685 LOTPolystarData():LOTPath(LOTData::Type::Polystar){}
687 LOTPolystarData::PolyType mType{PolyType::Polygon};
688 LOTAnimatable<VPointF> mPos;
689 LOTAnimatable<float> mPointCount{0};
690 LOTAnimatable<float> mInnerRadius{0};
691 LOTAnimatable<float> mOuterRadius{0};
692 LOTAnimatable<float> mInnerRoundness{0};
693 LOTAnimatable<float> mOuterRoundness{0};
694 LOTAnimatable<float> mRotation{0};
697 class LOTTrimData : public LOTData
704 enum class TrimType {
708 LOTTrimData():LOTData(LOTData::Type::Trim){}
709 Segment segment(int frameNo) const {
710 float start = mStart.value(frameNo)/100.0f;
711 float end = mEnd.value(frameNo)/100.0f;
712 float offset = fmod(mOffset.value(frameNo), 360.0f)/ 360.0f;
715 // normalize start, end value to 0 - 1
716 if (fabs(start) > 1) start = fmod(start, 1);
717 if (fabs(end) > 1) end = fmod(end, 1);
719 if (start >= 0 && end >= 0) {
720 s.start = std::min(start, end);
721 s.end = std::max(start, end);
722 } else if (start < 0 && end < 0) {
725 s.start = std::min(start, end);
726 s.end = std::max(start, end);
728 // one of them is -ve so it a loop
729 if (start < 0) start +=1;
730 if (end < 0) end +=1;
731 s.start = std::max(start, end);
732 s.end = std::min(start, end);
737 LOTTrimData::TrimType type() const {return mTrimType;}
739 LOTAnimatable<float> mStart{0};
740 LOTAnimatable<float> mEnd{0};
741 LOTAnimatable<float> mOffset{0};
742 LOTTrimData::TrimType mTrimType{TrimType::Simultaneously};
745 class LOTRepeaterData : public LOTGroupData
748 LOTRepeaterData():LOTGroupData(LOTData::Type::Repeater){}
749 bool hasMtrixChange(int /*frameNo*/) const {
750 return !(mTransform->isStatic() && mOffset.isStatic());
752 float copies(int frameNo) const {return mCopies.value(frameNo);}
753 float offset(int frameNo) const {return mOffset.value(frameNo);}
755 LOTAnimatable<float> mCopies{0};
756 LOTAnimatable<float> mOffset{0};
762 bool isStatic() const {return mRoot->isStatic();}
763 double duration() const {return mRoot->duration();}
764 size_t frameDuration() const {return mRoot->frameDuration();}
765 size_t frameRate() const {return mRoot->frameRate();}
766 size_t startFrame() const {return mRoot->startFrame();}
767 size_t endFrame() const {return mRoot->endFrame();}
768 size_t frameAtPos(double pos) const {return mRoot->frameAtPos(pos);}
770 std::shared_ptr<LOTCompositionData> mRoot;