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