lottianimation: fix incorrect animation in time stretch.
[platform/core/uifw/lottie-player.git] / src / lottie / lottiemodel.h
1 #ifndef LOTModel_H
2 #define LOTModel_H
3
4 #include<vector>
5 #include<memory>
6 #include<unordered_map>
7 #include"vpoint.h"
8 #include"vrect.h"
9 #include"vinterpolator.h"
10 #include"vmatrix.h"
11 #include"vbezier.h"
12 #include"vbrush.h"
13 #include"vpath.h"
14
15 V_USE_NAMESPACE
16
17 class LOTCompositionData;
18 class LOTLayerData;
19 class LOTTransformData;
20 class LOTShapeGroupData;
21 class LOTShapeData;
22 class LOTRectData;
23 class LOTEllipseData;
24 class LOTTrimData;
25 class LOTRepeaterData;
26 class LOTFillData;
27 class LOTStrokeData;
28 class LOTGroupData;
29 class LOTGFillData;
30 class LOTGStrokeData;
31 class LottieShapeData;
32 class LOTPolystarData;
33 class LOTMaskData;
34
35 enum class MatteType
36 {
37     None = 0,
38     Alpha = 1,
39     AlphaInv,
40     Luma,
41     LumaInv
42 };
43
44 enum class LayerType {
45     Precomp = 0,
46     Solid = 1,
47     Image = 2,
48     Null = 3,
49     Shape = 4,
50     Text = 5
51 };
52
53 class LottieColor
54 {
55 public:
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);
61 public:
62     float r{1};
63     float g{1};
64     float b{1};
65 };
66
67 inline LottieColor operator-(const LottieColor &c1, const LottieColor &c2)
68 {
69     return LottieColor(c1.r - c2.r, c1.g - c2.g, c1.b - c2.b);
70 }
71 inline LottieColor operator+(const LottieColor &c1, const LottieColor &c2)
72 {
73     return LottieColor(c1.r + c2.r, c1.g + c2.g, c1.b + c2.b);
74 }
75
76 inline const LottieColor operator*(const LottieColor &c, float m)
77 { return LottieColor(c.r*m, c.g*m, c.b*m); }
78
79 inline const LottieColor operator*(float m, const LottieColor &c)
80 { return LottieColor(c.r*m, c.g*m, c.b*m); }
81
82 class LottieShapeData
83 {
84 public:
85     void reserve(int size) {
86         mPoints.reserve(mPoints.size() + size);
87     }
88     void toPath(VPath& path) {
89         path.reset();
90
91         if (mPoints.empty()) return;
92
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
98          */
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]);
103         }
104         if (mClosed)
105           path.close();
106     }
107 public:
108     std::vector<VPointF>    mPoints;
109     bool                     mClosed = false;   /* "c" */
110 };
111
112
113
114 template<typename T>
115 inline T lerp(const T& start, const T& end, float t)
116 {
117     return start + t * (end - start);
118 }
119
120 inline LottieShapeData lerp(const LottieShapeData& start, const LottieShapeData& end, float t)
121 {
122     if (start.mPoints.size() != start.mPoints.size())
123        return LottieShapeData();
124
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]));
129     }
130    return result;
131 }
132
133 template <typename T>
134 struct LOTKeyFrameValue
135 {
136     T mStartValue;
137     T mEndValue;
138     T value(float t) const {
139         return lerp(mStartValue, mEndValue, t);
140     }
141 };
142
143 template <>
144 struct LOTKeyFrameValue<VPointF>
145 {
146     VPointF mStartValue;
147     VPointF mEndValue;
148     VPointF mInTangent;
149     VPointF mOutTangent;
150     bool    mPathKeyFrame = false;
151
152     VPointF value(float t) const {
153         if (mPathKeyFrame) {
154             return VBezier::fromPoints(mStartValue, mStartValue + mOutTangent,
155                                        mEndValue + mInTangent, mEndValue).pointAt(t);
156         } else {
157             return lerp(mStartValue, mEndValue, t);
158         }
159     }
160 };
161
162
163 template<typename T>
164 class LOTKeyFrame
165 {
166 public:
167     T value(int frameNo) const {
168         float progress = mInterpolator->value(float(frameNo - mStartFrame) / float(mEndFrame - mStartFrame));
169         return mValue.value(progress);
170     }
171
172 public:
173     int                 mStartFrame{0};
174     int                 mEndFrame{0};
175     std::shared_ptr<VInterpolator> mInterpolator;
176     LOTKeyFrameValue<T>  mValue;
177 };
178
179 template<typename T>
180 class LOTAnimInfo
181 {
182 public:
183     T value(int frameNo) const {
184         if (mKeyFrames.front().mStartFrame >= frameNo)
185             return mKeyFrames.front().mValue.mStartValue;
186         if(mKeyFrames.back().mEndFrame <= frameNo)
187             return mKeyFrames.back().mValue.mEndValue;
188
189         for(auto keyFrame : mKeyFrames) {
190             if (frameNo >= keyFrame.mStartFrame && frameNo < keyFrame.mEndFrame)
191                 return keyFrame.value(frameNo);
192         }
193         return T();
194     }
195 public:
196     std::vector<LOTKeyFrame<T>>    mKeyFrames;
197 };
198
199 template<typename T>
200 class LOTAnimatable
201 {
202 public:
203     LOTAnimatable():mValue(),mAnimInfo(nullptr){}
204     LOTAnimatable(const T &value): mValue(value){}
205     bool isStatic() const {if (mAnimInfo) return false; else return true;}
206     T value(int frameNo) const{
207         if (isStatic())
208             return mValue;
209         else
210             return mAnimInfo->value(frameNo);
211     }
212 public:
213     T                                    mValue;
214     int                                  mPropertyIndex; /* "ix" */
215     std::unique_ptr<LOTAnimInfo<T>>   mAnimInfo;
216 };
217
218 enum class LottieBlendMode
219 {
220     Normal = 0,
221     Multiply = 1,
222     Screen = 2,
223     OverLay = 3
224 };
225
226 class LOTDataVisitor;
227 class LOTData
228 {
229 public:
230     enum class Type {
231         Composition = 1,
232         Layer,
233         ShapeGroup,
234         Transform,
235         Fill,
236         Stroke,
237         GFill,
238         GStroke,
239         Rect,
240         Ellipse,
241         Shape,
242         Polystar,
243         Trim,
244         Repeater
245     };
246     LOTData(LOTData::Type  type): mType(type){}
247     inline LOTData::Type type() const {return mType;}
248     bool isStatic() const{return mStatic;}
249     void setStatic(bool value) {mStatic = value;}
250 public:
251     bool                mStatic{true};
252     LOTData::Type       mType;
253 };
254
255 class LOTGroupData: public LOTData
256 {
257 public:
258     LOTGroupData(LOTData::Type  type):LOTData(type){}
259 public:
260     std::vector<std::shared_ptr<LOTData>>  mChildren;
261     std::shared_ptr<LOTTransformData>      mTransform;
262 };
263
264 class LOTShapeGroupData : public LOTGroupData
265 {
266 public:
267     LOTShapeGroupData():LOTGroupData(LOTData::Type::ShapeGroup){}
268 };
269
270 class LOTLayerData;
271 struct LOTAsset
272 {
273     int                                          mAssetType; //lottie asset type  (precomp/char/image)
274     std::string                                  mRefId; // ref id
275     std::vector<std::shared_ptr<LOTData>>   mLayers;
276 };
277
278 class LOTLayerData : public LOTGroupData
279 {
280 public:
281     LOTLayerData():LOTGroupData(LOTData::Type::Layer){}
282     bool hasPathOperator() const noexcept {return mHasPathOperator;}
283     bool hasGradient() const noexcept {return mHasGradient;}
284     bool hasMask() const noexcept {return mHasMask;}
285     bool hasRepeater() const noexcept {return mHasRepeater;}
286     bool root() const noexcept {return mRoot;}
287     int id() const noexcept{ return mId;}
288     int parentId() const noexcept{ return mParentId;}
289     int inFrame() const noexcept{return mInFrame;}
290     int outFrame() const noexcept{return mOutFrame;}
291     int startFrame() const noexcept{return mStartFrame;}
292     int solidWidth() const noexcept{return mSolidLayer.mWidth;}
293     int solidHeight() const noexcept{return mSolidLayer.mHeight;}
294     LottieColor solidColor() const noexcept{return mSolidLayer.mColor;}
295     int timeRemap(int frameNo) const;
296 public:
297     struct SolidLayer {
298         int            mWidth{0};
299         int            mHeight{0};
300         LottieColor    mColor;
301     };
302
303     MatteType            mMatteType{MatteType::None};
304     VRect                mBound;
305     LayerType            mLayerType; //lottie layer type  (solid/shape/precomp)
306     int                  mParentId{-1}; // Lottie the id of the parent in the composition
307     int                  mId{-1};  // Lottie the group id  used for parenting.
308     long                 mInFrame{0};
309     long                 mOutFrame{0};
310     long                 mStartFrame{0};
311     LottieBlendMode      mBlendMode;
312     float                mTimeStreatch{1.0f};
313     std::string          mPreCompRefId;
314     LOTAnimatable<float> mTimeRemap;  /* "tm" */
315     SolidLayer           mSolidLayer;
316     bool                 mHasPathOperator{false};
317     bool                 mHasMask{false};
318     bool                 mHasRepeater{false};
319     bool                 mHasGradient{false};
320     bool                 mRoot{false};
321     std::vector<std::shared_ptr<LOTMaskData>>  mMasks;
322     LOTCompositionData   *mCompRef;
323 };
324
325 class LOTCompositionData : public LOTData
326 {
327 public:
328     LOTCompositionData():LOTData(LOTData::Type::Composition){}
329     double duration() const {
330         return isStatic() ? startFrame() :
331                             frameDuration() / frameRate(); // in second
332     }
333     size_t frameAtPos(double pos) const {
334         if (pos < 0) pos = 0;
335         if (pos > 1) pos = 1;
336         return isStatic() ? 0 : pos * frameDuration();
337     }
338     long frameAtTime(double timeInSec) const {
339         return isStatic() ? startFrame() : frameAtPos(timeInSec / duration());
340     }
341     long frameDuration() const {return mEndFrame - mStartFrame -1;}
342     float frameRate() const {return mFrameRate;}
343     long startFrame() const {return mStartFrame;}
344     long endFrame() const {return mEndFrame;}
345     VSize size() const {return mSize;}
346     void processRepeaterObjects();
347 public:
348     std::string          mVersion;
349     VSize                mSize;
350     long                 mStartFrame{0};
351     long                 mEndFrame{0};
352     float                mFrameRate;
353     LottieBlendMode      mBlendMode;
354     std::shared_ptr<LOTLayerData> mRootLayer;
355     std::unordered_map<std::string,
356                        std::shared_ptr<VInterpolator>> mInterpolatorCache;
357     std::unordered_map<std::string,
358                        std::shared_ptr<LOTAsset>>    mAssets;
359
360 };
361
362 /**
363  * TimeRemap has the value in time domain(in sec)
364  * To get the proper mapping first we get the mapped time at the current frame Number
365  * then we need to convert mapped time to frame number using the composition time line
366  * Ex: at frame 10 the mappend time is 0.5(500 ms) which will be convert to frame number
367  * 30 if the frame rate is 60. or will result to frame number 15 if the frame rate is 30.
368  */
369 inline int LOTLayerData::timeRemap(int frameNo) const
370 {
371     frameNo = mTimeRemap.isStatic() ? frameNo :
372               mCompRef->frameAtTime(mTimeRemap.value(frameNo));
373     /* Apply time streatch if it has any.
374      * Time streatch is just a factor by which the animation will speedup or slow
375      * down with respect to the overal animation.
376      * Time streach factor is already applied to the layers inFrame and outFrame.
377      * @TODO need to find out if timestreatch also affects the in and out frame of the
378      * child layers or not. */
379     return frameNo / mTimeStreatch;
380 }
381
382 class LOTTransformData : public LOTData
383 {
384 public:
385     LOTTransformData():LOTData(LOTData::Type::Transform),mScale({100, 100}){}
386     VMatrix matrix(int frameNo) const;
387     float    opacity(int frameNo) const;
388     void cacheMatrix();
389     bool staticMatrix() const {return mStaticMatrix;}
390 private:
391     VMatrix computeMatrix(int frameNo) const;
392 public:
393     LOTAnimatable<float>     mRotation{0};  /* "r" */
394     LOTAnimatable<VPointF>   mScale;     /* "s" */
395     LOTAnimatable<VPointF>   mPosition;  /* "p" */
396     LOTAnimatable<float>     mX{0};
397     LOTAnimatable<float>     mY{0};
398     LOTAnimatable<VPointF>   mAnchor;    /* "a" */
399     LOTAnimatable<float>     mOpacity{100};   /* "o" */
400     LOTAnimatable<float>     mSkew{0};      /* "sk" */
401     LOTAnimatable<float>     mSkewAxis{0};  /* "sa" */
402     bool                     mStaticMatrix{true};
403     bool                     mSeparate{false};
404     VMatrix                  mCachedMatrix;
405 };
406
407 class LOTFillData : public LOTData
408 {
409 public:
410     LOTFillData():LOTData(LOTData::Type::Fill){}
411     float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
412     FillRule fillRule() const {return mFillRule;}
413 public:
414     FillRule                       mFillRule{FillRule::Winding}; /* "r" */
415     LOTAnimatable<LottieColor>     mColor;   /* "c" */
416     LOTAnimatable<int>             mOpacity{100};  /* "o" */
417     bool                           mEnabled{true}; /* "fillEnabled" */
418 };
419
420 struct LOTDashProperty
421 {
422     LOTAnimatable<float>     mDashArray[5]; /* "d" "g" "o"*/
423     int                      mDashCount{0};
424     bool                     mStatic{true};
425 };
426
427 class LOTStrokeData : public LOTData
428 {
429 public:
430     LOTStrokeData():LOTData(LOTData::Type::Stroke){}
431     float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
432     float width(int frameNo) const {return mWidth.value(frameNo);}
433     CapStyle capStyle() const {return mCapStyle;}
434     JoinStyle joinStyle() const {return mJoinStyle;}
435     float meterLimit() const{return mMeterLimit;}
436     bool hasDashInfo() const { return !(mDash.mDashCount == 0);}
437     int getDashInfo(int frameNo, float *array) const;
438 public:
439     LOTAnimatable<LottieColor>        mColor;      /* "c" */
440     LOTAnimatable<int>                mOpacity{100};    /* "o" */
441     LOTAnimatable<float>              mWidth{0};      /* "w" */
442     CapStyle                          mCapStyle;   /* "lc" */
443     JoinStyle                         mJoinStyle;  /* "lj" */
444     float                             mMeterLimit{0}; /* "ml" */
445     LOTDashProperty                   mDash;
446     bool                              mEnabled{true}; /* "fillEnabled" */
447 };
448
449 class LottieGradient
450 {
451 public:
452     friend inline LottieGradient operator+(const LottieGradient &g1, const LottieGradient &g2);
453     friend inline LottieGradient operator-(const LottieGradient &g1, const LottieGradient &g2);
454     friend inline LottieGradient operator*(float m, const LottieGradient &g);
455 public:
456     std::vector<float>    mGradient;
457 };
458
459 inline LottieGradient operator+(const LottieGradient &g1, const LottieGradient &g2)
460 {
461     if (g1.mGradient.size() != g2.mGradient.size())
462         return g1;
463
464     LottieGradient newG;
465     newG.mGradient = g1.mGradient;
466
467     auto g2It = g2.mGradient.begin();
468     for(auto &i : newG.mGradient) {
469         i = i + *g2It;
470         g2It++;
471     }
472
473     return newG;
474 }
475
476 inline LottieGradient operator-(const LottieGradient &g1, const LottieGradient &g2)
477 {
478     if (g1.mGradient.size() != g2.mGradient.size())
479         return g1;
480     LottieGradient newG;
481     newG.mGradient = g1.mGradient;
482
483     auto g2It = g2.mGradient.begin();
484     for(auto &i : newG.mGradient) {
485         i = i - *g2It;
486         g2It++;
487     }
488
489     return newG;
490 }
491
492 inline LottieGradient operator*(float m, const LottieGradient &g)
493 {
494     LottieGradient newG;
495     newG.mGradient = g.mGradient;
496
497     for(auto &i : newG.mGradient) {
498         i = i * m;
499     }
500     return newG;
501 }
502
503
504
505 class LOTGradient : public LOTData
506 {
507 public:
508     LOTGradient(LOTData::Type  type):LOTData(type){}
509     inline float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
510     void update(std::unique_ptr<VGradient> &grad, int frameNo);
511
512 private:
513     void populate(VGradientStops &stops, int frameNo);
514 public:
515     int                                 mGradientType;        /* "t" Linear=1 , Radial = 2*/
516     LOTAnimatable<VPointF>              mStartPoint;          /* "s" */
517     LOTAnimatable<VPointF>              mEndPoint;            /* "e" */
518     LOTAnimatable<float>                mHighlightLength{0};     /* "h" */
519     LOTAnimatable<float>                mHighlightAngle{0};      /* "a" */
520     LOTAnimatable<int>                  mOpacity{0};             /* "o" */
521     LOTAnimatable<LottieGradient>       mGradient;            /* "g" */
522     int                                 mColorPoints{-1};
523     bool                                mEnabled{true};      /* "fillEnabled" */
524 };
525
526 class LOTGFillData : public LOTGradient
527 {
528 public:
529     LOTGFillData():LOTGradient(LOTData::Type::GFill){}
530     FillRule fillRule() const {return mFillRule;}
531 public:
532     FillRule                       mFillRule{FillRule::Winding}; /* "r" */
533 };
534
535 class LOTGStrokeData : public LOTGradient
536 {
537 public:
538     LOTGStrokeData():LOTGradient(LOTData::Type::GStroke){}
539     float width(int frameNo) const {return mWidth.value(frameNo);}
540     CapStyle capStyle() const {return mCapStyle;}
541     JoinStyle joinStyle() const {return mJoinStyle;}
542     float meterLimit() const{return mMeterLimit;}
543     bool hasDashInfo() const { return !(mDash.mDashCount == 0);}
544     int getDashInfo(int frameNo, float *array) const;
545 public:
546     LOTAnimatable<float>           mWidth;       /* "w" */
547     CapStyle                       mCapStyle;    /* "lc" */
548     JoinStyle                      mJoinStyle;   /* "lj" */
549     float                          mMeterLimit{0};  /* "ml" */
550     LOTDashProperty                mDash;
551 };
552
553 class LOTPath : public LOTData
554 {
555 public:
556     LOTPath(LOTData::Type  type):LOTData(type){}
557     VPath::Direction direction() { if (mDirection == 3) return VPath::Direction::CCW;
558                                    else return VPath::Direction::CW;}
559 public:
560     int                                    mDirection{1};
561 };
562
563 class LOTShapeData : public LOTPath
564 {
565 public:
566     LOTShapeData():LOTPath(LOTData::Type::Shape){}
567     void process();
568 public:
569     LOTAnimatable<LottieShapeData>    mShape;
570 };
571
572 class LOTMaskData
573 {
574 public:
575     enum class Mode {
576       None,
577       Add,
578       Substarct,
579       Intersect,
580       Difference
581     };
582     float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
583     bool isStatic() const {return mIsStatic;}
584 public:
585     LOTAnimatable<LottieShapeData>    mShape;
586     LOTAnimatable<float>              mOpacity;
587     bool                              mInv{false};
588     bool                              mIsStatic{true};
589     LOTMaskData::Mode                 mMode;
590 };
591
592 class LOTRectData : public LOTPath
593 {
594 public:
595     LOTRectData():LOTPath(LOTData::Type::Rect){}
596 public:
597     LOTAnimatable<VPointF>    mPos;
598     LOTAnimatable<VPointF>    mSize;
599     LOTAnimatable<float>      mRound{0};
600 };
601
602 class LOTEllipseData : public LOTPath
603 {
604 public:
605     LOTEllipseData():LOTPath(LOTData::Type::Ellipse){}
606 public:
607     LOTAnimatable<VPointF>   mPos;
608     LOTAnimatable<VPointF>   mSize;
609 };
610
611 class LOTPolystarData : public LOTPath
612 {
613 public:
614     enum class PolyType {
615         Star = 1,
616         Polygon = 2
617     };
618     LOTPolystarData():LOTPath(LOTData::Type::Polystar){}
619 public:
620     LOTPolystarData::PolyType     mType{PolyType::Polygon};
621     LOTAnimatable<VPointF>        mPos;
622     LOTAnimatable<float>          mPointCount{0};
623     LOTAnimatable<float>          mInnerRadius{0};
624     LOTAnimatable<float>          mOuterRadius{0};
625     LOTAnimatable<float>          mInnerRoundness{0};
626     LOTAnimatable<float>          mOuterRoundness{0};
627     LOTAnimatable<float>          mRotation{0};
628 };
629
630 class LOTTrimData : public LOTData
631 {
632 public:
633     enum class TrimType {
634         Simultaneously,
635         Individually
636     };
637     LOTTrimData():LOTData(LOTData::Type::Trim){}
638     float start(int frameNo) const {return mStart.value(frameNo)/100.0f;}
639     float end(int frameNo) const {return mEnd.value(frameNo)/100.0f;}
640     float offset(int frameNo) const {return fmod(mOffset.value(frameNo), 360.0f)/ 360.0f;}
641     LOTTrimData::TrimType type() const {return mTrimType;}
642 public:
643     LOTAnimatable<float>             mStart{0};
644     LOTAnimatable<float>             mEnd{0};
645     LOTAnimatable<float>             mOffset{0};
646     LOTTrimData::TrimType            mTrimType{TrimType::Simultaneously};
647 };
648
649 class LOTRepeaterData : public LOTGroupData
650 {
651 public:
652     LOTRepeaterData():LOTGroupData(LOTData::Type::Repeater){}
653 public:
654     LOTAnimatable<float>             mCopies{0};
655     LOTAnimatable<float>             mOffset{0};
656 };
657
658 class LOTModel
659 {
660 public:
661    bool  isStatic() const {return mRoot->isStatic();}
662    double duration() const {return mRoot->duration();}
663    size_t frameDuration() const {return mRoot->frameDuration();}
664    size_t frameRate() const {return mRoot->frameRate();}
665    size_t startFrame() const {return mRoot->startFrame();}
666    size_t endFrame() const {return mRoot->endFrame();}
667    size_t frameAtPos(double pos) const {return mRoot->frameAtPos(pos);}
668 public:
669     std::shared_ptr<LOTCompositionData> mRoot;
670 };
671
672 #endif // LOTModel_H