lottie: give a make_unique wrapper till we move to c++14
[platform/core/uifw/lottie-player.git] / src / lottie / lottieitem.cpp
1 #include "lottieitem.h"
2 #include"vbitmap.h"
3 #include"vpainter.h"
4 #include"vraster.h"
5 #include"vdasher.h"
6 #include <cmath>
7
8 VDrawable::VDrawable():mFlag(DirtyState::All),
9                        mType(Type::Fill),
10                        mFillRule(FillRule::Winding)
11 {
12     mStroke.dashArraySize = 0;
13     mStroke.cap = CapStyle::Round;
14     mStroke.join= JoinStyle::Round;
15     mStroke.meterLimit = 10;
16     mStroke.enable = false;
17 }
18
19 void VDrawable::preprocess()
20 {
21     if (mFlag & (DirtyState::Path)) {
22         if (mStroke.enable) {
23             VPath newPath = mPath;
24             if (mStroke.dashArraySize) {
25                 VDasher dasher(mStroke.dashArray, mStroke.dashArraySize);
26                 newPath = dasher.dashed(mPath);
27             }
28             FTOutline *outline = VRaster::toFTOutline(newPath);
29             mRleTask = VRaster::instance().generateStrokeInfo(outline, mStroke.cap, mStroke.join,
30                                                               mStroke.width, mStroke.meterLimit);
31         } else {
32             FTOutline *outline = VRaster::toFTOutline(mPath);
33             mRleTask = VRaster::instance().generateFillInfo(outline, mFillRule);
34         }
35         mFlag &= ~DirtyFlag(DirtyState::Path);
36     }
37 }
38
39 VRle VDrawable::rle()
40 {
41     if (mRleTask.valid()) {
42         mRle = std::move(mRleTask.get());
43     }
44     return mRle;
45 }
46
47 void VDrawable::setStrokeInfo(CapStyle cap, JoinStyle join, float meterLimit, float strokeWidth)
48 {
49     mStroke.enable = true;
50     mStroke.cap = cap;
51     mStroke.join = join;
52     mStroke.meterLimit = meterLimit;
53     mStroke.width = strokeWidth;
54     mFlag |= DirtyState::Path;
55 }
56 void VDrawable::setDashInfo(float *array, int size)
57 {
58     mStroke.dashArray = array;
59     mStroke.dashArraySize = size;
60     mFlag |= DirtyState::Path;
61 }
62
63 void VDrawable::sync()
64 {
65     mCNode.mFlag = ChangeFlagNone;
66     if (mFlag & DirtyState::None) return;
67
68     if (mFlag & DirtyState::Path) {
69         const std::vector<VPath::Element> &elm = mPath.elements();
70         const std::vector<VPointF> &pts  = mPath.points();
71         const float *ptPtr = reinterpret_cast<const float *>(pts.data());
72         const char *elmPtr = reinterpret_cast<const char *>(elm.data());
73         mCNode.mPath.elmPtr = elmPtr;
74         mCNode.mPath.elmCount = elm.size();
75         mCNode.mPath.ptPtr = ptPtr;
76         mCNode.mPath.ptCount = 2 * pts.size();
77         mCNode.mFlag |= ChangeFlagPath;
78     }
79
80     if (mStroke.enable) {
81         mCNode.mStroke.width = mStroke.width;
82         mCNode.mStroke.meterLimit = mStroke.meterLimit;
83         mCNode.mStroke.enable = 1;
84
85         switch (mFillRule) {
86         case FillRule::EvenOdd:
87             mCNode.mFillRule = LOTNode::EvenOdd;
88             break;
89         default:
90             mCNode.mFillRule = LOTNode::Winding;
91             break;
92         }
93
94         switch (mStroke.cap) {
95         case CapStyle::Flat:
96             mCNode.mStroke.cap = LOTNode::FlatCap;
97             break;
98         case CapStyle::Square:
99             mCNode.mStroke.cap = LOTNode::SquareCap;
100             break;
101         case CapStyle::Round:
102             mCNode.mStroke.cap = LOTNode::RoundCap;
103             break;
104         default:
105             mCNode.mStroke.cap = LOTNode::FlatCap;
106             break;
107         }
108
109         switch (mStroke.join) {
110         case JoinStyle::Miter:
111             mCNode.mStroke.join = LOTNode::MiterJoin;
112             break;
113         case JoinStyle::Bevel:
114             mCNode.mStroke.join = LOTNode::BevelJoin;
115             break;
116         case JoinStyle::Round:
117             mCNode.mStroke.join = LOTNode::RoundJoin;
118             break;
119         default:
120             mCNode.mStroke.join = LOTNode::MiterJoin;
121             break;
122         }
123
124         mCNode.mStroke.dashArray = mStroke.dashArray;
125         mCNode.mStroke.dashArraySize = mStroke.dashArraySize;
126
127     } else {
128         mCNode.mStroke.enable = 0;
129     }
130
131     switch (mBrush.type()) {
132     case VBrush::Type::Solid:
133         mCNode.mType = LOTNode::BrushSolid;
134         mCNode.mColor.r = mBrush.mColor.r;
135         mCNode.mColor.g = mBrush.mColor.g;
136         mCNode.mColor.b = mBrush.mColor.b;
137         mCNode.mColor.a = mBrush.mColor.a;
138         break;
139     case VBrush::Type::LinearGradient:
140         mCNode.mType = LOTNode::BrushGradient;
141         mCNode.mGradient.type = LOTNode::Gradient::Linear;
142         mCNode.mGradient.start.x = mBrush.mGradient->linear.x1;
143         mCNode.mGradient.start.y = mBrush.mGradient->linear.y1;
144         mCNode.mGradient.end.x = mBrush.mGradient->linear.x2;
145         mCNode.mGradient.end.y = mBrush.mGradient->linear.y2;
146         break;
147     case VBrush::Type::RadialGradient:
148         mCNode.mType = LOTNode::BrushGradient;
149         mCNode.mGradient.type = LOTNode::Gradient::Radial;
150         mCNode.mGradient.center.x = mBrush.mGradient->radial.cx;
151         mCNode.mGradient.center.y = mBrush.mGradient->radial.cy;
152         mCNode.mGradient.focal.x = mBrush.mGradient->radial.fx;
153         mCNode.mGradient.focal.y = mBrush.mGradient->radial.fy;
154         mCNode.mGradient.cradius = mBrush.mGradient->radial.cradius;
155         mCNode.mGradient.fradius = mBrush.mGradient->radial.fradius;
156         break;
157     default:
158         break;
159     }
160 }
161
162 /* Lottie Layer Rules
163  * 1. time stretch is pre calculated and applied to all the properties of the lottilayer model and all its children
164  * 2. The frame property could be reversed using,time-reverse layer property in AE. which means (start frame > endFrame)
165  * 3.
166  */
167
168 LOTCompItem::LOTCompItem(LOTModel *model):mRootModel(model), mUpdateViewBox(false),mCurFrameNo(-1)
169 {
170    // 1. build layer item list
171    mCompData = model->mRoot.get();
172    for(auto i : mCompData->mChildren) {
173       LOTLayerData *layerData = dynamic_cast<LOTLayerData *>(i.get());
174       if (layerData) {
175          LOTLayerItem *layerItem = LOTCompItem::createLayerItem(layerData);
176          if (layerItem) {
177             mLayers.push_back(layerItem);
178             mLayerMap[layerItem->id()] = layerItem;
179          }
180       }
181    }
182
183    //2. update parent layer
184    for(auto i : mLayers) {
185       int id = i->parentId();
186       if (id >=0) {
187          auto search = mLayerMap.find(id);
188          if (search != mLayerMap.end()) {
189            LOTLayerItem *parentLayer = search->second;
190            i->setParentLayer(parentLayer);
191          }
192       }
193    }
194    //3. update static property of each layer
195    for(auto i : mLayers) {
196       i->updateStaticProperty();
197    }
198
199    mViewSize = mCompData->size();
200 }
201
202 LOTCompItem::~LOTCompItem()
203 {
204     for(auto i : mLayers) {
205        delete i;
206     }
207 }
208
209 LOTLayerItem *
210 LOTCompItem::createLayerItem(LOTLayerData *layerData)
211 {
212     switch(layerData->mLayerType) {
213         case LayerType::Precomp: {
214             return new LOTCompLayerItem(layerData);
215             break;
216         }
217         case LayerType::Solid: {
218             return new LOTSolidLayerItem(layerData);
219             break;
220         }
221         case LayerType::Shape: {
222             return new LOTShapeLayerItem(layerData);
223             break;
224         }
225         case LayerType::Null: {
226             return new LOTNullLayerItem(layerData);
227             break;
228         }
229         default:
230             return nullptr;
231             break;
232     }
233 }
234
235 void LOTCompItem::resize(const VSize &size)
236 {
237    if (mViewSize == size) return;
238    mViewSize = size;
239    mUpdateViewBox = true;
240 }
241
242 VSize LOTCompItem::size() const
243 {
244    return mViewSize;
245 }
246
247 bool LOTCompItem::update(int frameNo)
248 {
249    VMatrix m;
250    float sx, sy;
251
252    // check if cached frame is same as requested frame.
253    if (!mUpdateViewBox && (mCurFrameNo == frameNo)) return false;
254
255    sx = mViewSize.width() / float(mCompData->size().width());
256    sy = mViewSize.height() / float(mCompData->size().height());
257    float scale = fmin(sx, sy);
258    m.scale(scale, scale);
259
260    // update the layer from back to front
261    for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
262       LOTLayerItem *layer = *i;
263       layer->update(frameNo, m, 1.0);
264    }
265    buildRenderList();
266    mCurFrameNo = frameNo;
267    mUpdateViewBox = false;
268    return true;
269 }
270
271 void LOTCompItem::buildRenderList()
272 {
273     mDrawableList.clear();
274     for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
275        LOTLayerItem *layer = *i;
276        layer->renderList(mDrawableList);
277     }
278
279     mRenderList.clear();
280     for(auto i : mDrawableList) {
281         i->sync();
282         mRenderList.push_back(&i->mCNode);
283     }
284 }
285
286 const std::vector<LOTNode *>& LOTCompItem::renderList() const
287 {
288     return mRenderList;
289 }
290
291 bool LOTCompItem::render(const LOTBuffer &buffer)
292 {
293     VBitmap bitmap((uchar *)buffer.buffer, buffer.width, buffer.height,
294                    buffer.bytesPerLine, VBitmap::Format::ARGB32_Premultiplied, nullptr, nullptr);
295
296     /* schedule all preprocess task for this frame at once.
297      */
298     for (auto &e : mDrawableList) {
299         e->preprocess();
300     }
301
302     VPainter painter(&bitmap);
303     VRle mask;
304     for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
305        LOTLayerItem *layer = *i;
306        layer->render(&painter, mask);
307     }
308
309     return true;
310 }
311
312 void LOTMaskItem::update(int frameNo, const VMatrix &parentMatrix,
313                          float parentAlpha, const DirtyFlag &flag)
314 {
315     if (mData->mShape.isStatic()) {
316         if (mLocalPath.isEmpty()) {
317             mLocalPath = mData->mShape.value(frameNo).toPath();
318         }
319     } else {
320         mLocalPath = mData->mShape.value(frameNo).toPath();
321     }
322     float opacity = mData->opacity(frameNo);
323     opacity = opacity * parentAlpha;
324     mCombinedAlpha = opacity;
325
326     VPath path = mLocalPath;
327     path.transform(parentMatrix);
328
329     FTOutline *outline = VRaster::toFTOutline(path);
330     mRleTask = VRaster::instance().generateFillInfo(outline);
331 }
332
333 VRle LOTMaskItem::rle()
334 {
335     if (mRleTask.valid()) {
336         mRle = std::move(mRleTask.get());
337         if (!vCompare(mCombinedAlpha, 1.0f))
338             mRle = mRle * (mCombinedAlpha * 255);
339         if (mData->mInv)
340             mRle = ~mRle;
341     }
342     return mRle;
343 }
344
345 void LOTLayerItem::render(VPainter *painter, const VRle &inheritMask)
346 {
347     std::vector<VDrawable *> list;
348     renderList(list);
349     VRle mask = inheritMask;
350     if (hasMask()) {
351         if (mask.isEmpty())
352             mask = maskRle(painter->clipBoundingRect());
353         else
354             mask = mask & inheritMask;
355     }
356
357     for(auto i : list) {
358         painter->setBrush(i->mBrush);
359         if (!mask.isEmpty()) {
360             VRle rle = i->rle() & mask;
361             painter->drawRle(VPoint(), rle);
362         } else {
363             painter->drawRle(VPoint(), i->rle());
364         }
365     }
366 }
367
368 VRle LOTLayerItem::maskRle(const VRect &clipRect)
369 {
370
371     VRle rle;
372     for (auto &i : mMasks) {
373         switch (i->maskMode()) {
374             case LOTMaskData::Mode::Add: {
375                 rle = rle + i->rle();
376                 break;
377             }
378             case LOTMaskData::Mode::Substarct: {
379                 if (rle.isEmpty() && !clipRect.isEmpty())
380                     rle = VRle::toRle(clipRect);
381                 rle = rle - i->rle();
382                 break;
383             }
384             case LOTMaskData::Mode::Intersect: {
385                 rle = rle & i->rle();
386                 break;
387             }
388             default:
389                 break;
390         }
391     }
392     return rle;
393 }
394
395 LOTLayerItem::LOTLayerItem(LOTLayerData *layerData):mLayerData(layerData),
396                                                     mParentLayer(nullptr),
397                                                     mPrecompLayer(nullptr),
398                                                     mCombinedAlpha(0.0f),
399                                                     mFrameNo(-1),
400                                                     mDirtyFlag(DirtyFlagBit::All)
401 {
402     if (mLayerData->mHasMask) {
403         for (auto i : mLayerData->mMasks) {
404             mMasks.push_back(std::make_unique<LOTMaskItem>(i.get()));
405         }
406     }
407 }
408
409 void LOTLayerItem::updateStaticProperty()
410 {
411    if (mParentLayer)
412      mParentLayer->updateStaticProperty();
413
414    mStatic = mLayerData->isStatic();
415    mStatic = mParentLayer ? (mStatic & mParentLayer->isStatic()) : mStatic;
416    mStatic = mPrecompLayer ? (mStatic & mPrecompLayer->isStatic()) : mStatic;
417 }
418
419 void LOTLayerItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha)
420 {
421    mFrameNo = frameNo;
422    // 1. check if the layer is part of the current frame
423    if (!visible()) return;
424
425    // 2. calculate the parent matrix and alpha
426    VMatrix m = matrix(frameNo) * parentMatrix;
427    float alpha = parentAlpha * opacity(frameNo);
428
429    //6. update the mask
430    if (hasMask()) {
431        for (auto &i : mMasks)
432            i->update(frameNo, m, alpha, mDirtyFlag);
433    }
434
435    // 3. update the dirty flag based on the change
436    if (!mCombinedMatrix.fuzzyCompare(m)) {
437        mDirtyFlag |= DirtyFlagBit::Matrix;
438    }
439    if (!vCompare(mCombinedAlpha, alpha)) {
440        mDirtyFlag |= DirtyFlagBit::Alpha;
441    }
442    mCombinedMatrix = m;
443    mCombinedAlpha = alpha;
444
445    // 4. if no parent property change and layer is static then nothing to do.
446    if ((flag() & DirtyFlagBit::None) && isStatic())
447       return;
448
449    //5. update the content of the layer
450    updateContent();
451
452    //6. reset the dirty flag
453    mDirtyFlag = DirtyFlagBit::None;
454 }
455
456 float
457 LOTLayerItem::opacity(int frameNo) const
458 {
459    return mLayerData->mTransform->opacity(frameNo);
460 }
461
462 VMatrix
463 LOTLayerItem::matrix(int frameNo) const
464 {
465     if (mParentLayer)
466         return mLayerData->mTransform->matrix(frameNo) * mParentLayer->matrix(frameNo);
467     else
468         return mLayerData->mTransform->matrix(frameNo);
469 }
470
471 bool LOTLayerItem::visible() const
472 {
473    if (frameNo() >= mLayerData->inFrame() && frameNo() < mLayerData->outFrame())
474       return true;
475    else
476       return false;
477 }
478
479
480
481 LOTCompLayerItem::LOTCompLayerItem(LOTLayerData *layerModel):LOTLayerItem(layerModel)
482 {
483    for(auto i : mLayerData->mChildren) {
484       LOTLayerData *layerModel = dynamic_cast<LOTLayerData *>(i.get());
485       if (layerModel) {
486          LOTLayerItem *layerItem = LOTCompItem::createLayerItem(layerModel);
487          if (layerItem) {
488             mLayers.push_back(layerItem);
489             mLayerMap[layerItem->id()] = layerItem;
490          }
491       }
492    }
493
494    //2. update parent layer
495    for(auto i : mLayers) {
496       int id = i->parentId();
497       if (id >=0) {
498          auto search = mLayerMap.find(id);
499          if (search != mLayerMap.end()) {
500            LOTLayerItem *parentLayer = search->second;
501            i->setParentLayer(parentLayer);
502          }
503       }
504       i->setPrecompLayer(this);
505    }
506 }
507
508 void LOTCompLayerItem::updateStaticProperty()
509 {
510     LOTLayerItem::updateStaticProperty();
511
512     for(auto i : mLayers) {
513        i->updateStaticProperty();
514     }
515 }
516
517 void LOTCompLayerItem::render(VPainter *painter, const VRle &inheritMask)
518 {
519     VRle mask = inheritMask;
520
521     if (hasMask()) {
522         if (mask.isEmpty())
523             mask = maskRle(painter->clipBoundingRect());
524         else
525             mask = mask & inheritMask;
526     }
527
528     for(auto i : mLayers) {
529        i->render(painter, mask);
530     }
531 }
532
533 LOTCompLayerItem::~LOTCompLayerItem()
534 {
535     for(auto i : mLayers) {
536        delete i;
537     }
538 }
539
540 void LOTCompLayerItem::updateContent()
541 {
542     // update the layer from back to front
543     for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
544        LOTLayerItem *layer = *i;
545        layer->update(frameNo(), combinedMatrix(), combinedAlpha());
546     }
547 }
548
549 void LOTCompLayerItem::renderList(std::vector<VDrawable *> &list)
550 {
551     if (!visible()) return;
552
553     // update the layer from back to front
554     for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
555        LOTLayerItem *layer = *i;
556        layer->renderList(list);
557     }
558 }
559
560 LOTSolidLayerItem::LOTSolidLayerItem(LOTLayerData *layerData):LOTLayerItem(layerData)
561 {
562
563 }
564
565 void LOTSolidLayerItem::updateContent()
566 {
567    if (!mRenderNode) {
568       mRenderNode = std::make_unique<VDrawable>();
569       mRenderNode->mType = VDrawable::Type::Fill;
570       mRenderNode->mFlag |= VDrawable::DirtyState::All;
571    }
572
573    if (flag() & DirtyFlagBit::Matrix) {
574        VPath path;
575        path.addRect(VRectF(0, 0, mLayerData->solidWidth(), mLayerData->solidHeight()));
576        path.transform(combinedMatrix());
577        mRenderNode->mFlag |= VDrawable::DirtyState::Path;
578        mRenderNode->mPath = path;
579    }
580    if (flag() & DirtyFlagBit::Alpha) {
581        LottieColor color = mLayerData->solidColor();
582        VBrush brush(color.toColor(combinedAlpha()));
583        mRenderNode->setBrush(brush);
584        mRenderNode->mFlag |= VDrawable::DirtyState::Brush;
585    }
586 }
587
588 void LOTSolidLayerItem::renderList(std::vector<VDrawable *> &list)
589 {
590     if (!visible()) return;
591
592     list.push_back(mRenderNode.get());
593 }
594
595 LOTNullLayerItem::LOTNullLayerItem(LOTLayerData *layerData):LOTLayerItem(layerData)
596 {
597
598 }
599 void LOTNullLayerItem::updateContent()
600 {
601
602 }
603
604
605 LOTShapeLayerItem::LOTShapeLayerItem(LOTLayerData *layerData):LOTLayerItem(layerData)
606 {
607     mRoot = new LOTContentGroupItem(nullptr);
608     mRoot->addChildren(layerData);
609     mRoot->processPaintOperation();
610     if (layerData->hasPathOperator())
611       mRoot->processTrimOperation();
612 }
613
614 LOTShapeLayerItem::~LOTShapeLayerItem()
615 {
616     delete mRoot;
617 }
618
619 LOTContentItem * LOTShapeLayerItem::createContentItem(LOTData *contentData)
620 {
621     switch(contentData->type()) {
622         case LOTData::Type::ShapeGroup: {
623             return new LOTContentGroupItem(static_cast<LOTShapeGroupData *>(contentData));
624             break;
625         }
626         case LOTData::Type::Rect: {
627             return new LOTRectItem(static_cast<LOTRectData *>(contentData));
628             break;
629         }
630         case LOTData::Type::Ellipse: {
631             return new LOTEllipseItem(static_cast<LOTEllipseData *>(contentData));
632             break;
633         }
634         case LOTData::Type::Shape: {
635             return new LOTShapeItem(static_cast<LOTShapeData *>(contentData));
636             break;
637         }
638         case LOTData::Type::Polystar: {
639             return new LOTPolystarItem(static_cast<LOTPolystarData *>(contentData));
640             break;
641         }
642         case LOTData::Type::Fill: {
643             return new LOTFillItem(static_cast<LOTFillData *>(contentData));
644             break;
645         }
646         case LOTData::Type::GFill: {
647             return new LOTGFillItem(static_cast<LOTGFillData *>(contentData));
648             break;
649         }
650         case LOTData::Type::Stroke: {
651             return new LOTStrokeItem(static_cast<LOTStrokeData *>(contentData));
652             break;
653         }
654         case LOTData::Type::GStroke: {
655             return new LOTGStrokeItem(static_cast<LOTGStrokeData *>(contentData));
656             break;
657         }
658         case LOTData::Type::Repeater: {
659                 return new LOTRepeaterItem(static_cast<LOTRepeaterData *>(contentData));
660                 break;
661             }
662         case LOTData::Type::Trim: {
663             return new LOTTrimItem(static_cast<LOTTrimData *>(contentData));
664             break;
665         }
666         default:
667             return nullptr;
668             break;
669     }
670 }
671
672 void LOTShapeLayerItem::updateContent()
673 {
674    mRoot->update(frameNo(), combinedMatrix(), combinedAlpha(), flag());
675 }
676
677 void LOTShapeLayerItem::renderList(std::vector<VDrawable *> &list)
678 {
679     if (!visible()) return;
680     mRoot->renderList(list);
681 }
682
683 LOTContentGroupItem::LOTContentGroupItem(LOTShapeGroupData *data):mData(data)
684 {
685    addChildren(mData);
686 }
687
688 void LOTContentGroupItem::addChildren(LOTGroupData *data)
689 {
690    if (!data) return;
691
692    for(auto i : data->mChildren) {
693       LOTData *data = i.get();
694       LOTContentItem *content = LOTShapeLayerItem::createContentItem(data);
695       if (content)
696          mContents.push_back(content);
697    }
698 }
699
700 LOTContentGroupItem::~LOTContentGroupItem()
701 {
702     for(auto i : mContents) {
703         delete i;
704     }
705 }
706
707
708 void LOTContentGroupItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
709 {
710    VMatrix m = parentMatrix;
711    float alpha = parentAlpha;
712    DirtyFlag newFlag = flag;
713
714    if (mData) {
715       // update the matrix and the flag
716       if ((flag & DirtyFlagBit::Matrix) || !mData->mTransform->staticMatrix() ) {
717          newFlag |= DirtyFlagBit::Matrix;
718       }
719       m = mData->mTransform->matrix(frameNo) * parentMatrix;
720       alpha *= mData->mTransform->opacity(frameNo);
721
722       if (!vCompare(alpha, parentAlpha)) {
723          newFlag |= DirtyFlagBit::Alpha;
724       }
725    }
726
727    for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
728       (*i)->update(frameNo, m, alpha, newFlag);
729    }
730 }
731
732 void LOTContentGroupItem::renderList(std::vector<VDrawable *> &list)
733 {
734     for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
735        (*i)->renderList(list);
736     }
737 }
738
739 void LOTContentGroupItem::processPaintOperation()
740 {
741    std::vector<LOTPaintDataItem *> list;
742    paintOperationHelper(list);
743 }
744
745 void LOTContentGroupItem::paintOperationHelper(std::vector<LOTPaintDataItem *> &list)
746 {
747    int curOpCount = list.size();
748    for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
749       auto child = *i;
750       if (auto pathNode = dynamic_cast<LOTPathDataItem *>(child)) {
751          // the node is a path data node add the paint operation list to it.
752          pathNode->addPaintOperation(list, curOpCount);
753       } else if (auto paintNode = dynamic_cast<LOTPaintDataItem *>(child)) {
754          // add it to the paint operation list
755          list.push_back(paintNode);
756       } else if (auto groupNode = dynamic_cast<LOTContentGroupItem *>(child)) {
757          // update the groups node with current list
758          groupNode->paintOperationHelper(list);
759       }
760    }
761    list.erase(list.begin() + curOpCount, list.end());
762 }
763
764 void LOTPathDataItem::addPaintOperation(std::vector<LOTPaintDataItem *> &list, int externalCount)
765 {
766     for(auto paintItem : list) {
767       bool sameGroup = (externalCount-- > 0) ? false : true;
768       mNodeList.push_back(std::make_unique<VDrawable>());
769       mRenderList.push_back(LOTRenderNode(this, paintItem, mNodeList.back().get(), sameGroup));
770     }
771 }
772
773 void LOTContentGroupItem::processTrimOperation()
774 {
775    std::vector<LOTTrimItem *> list;
776    trimOperationHelper(list);
777 }
778
779 void LOTContentGroupItem::trimOperationHelper(std::vector<LOTTrimItem *> &list)
780 {
781    int curOpCount = list.size();
782    for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
783       auto child = *i;
784       if (auto pathNode = dynamic_cast<LOTPathDataItem *>(child)) {
785          // the node is a path data node add the trim operation list to it.
786          pathNode->addTrimOperation(list);
787       } else if (auto trimNode = dynamic_cast<LOTTrimItem *>(child)) {
788          // add it to the trim operation list
789          list.push_back(trimNode);
790       } else if (auto groupNode = dynamic_cast<LOTContentGroupItem *>(child)) {
791          // update the groups node with current list
792          groupNode->trimOperationHelper(list);
793       }
794    }
795    list.erase(list.begin() + curOpCount, list.end());
796 }
797
798 void LOTPathDataItem::addTrimOperation(std::vector<LOTTrimItem *> &list)
799 {
800    for(auto trimItem : list) {
801       mTrimNodeRefs.push_back(trimItem);
802    }
803 }
804
805 void LOTPathDataItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
806 {
807    VPath tempPath;
808
809    mPathChanged = false;
810    mCombinedAlpha = parentAlpha;
811
812    // 1. update the local path if needed
813    if (!(mInit && mStaticPath)) {
814       mLocalPath = getPath(frameNo);
815       mInit = true;
816       mPathChanged = true;
817    }
818
819    tempPath = mLocalPath;
820
821    // 2. apply path operation if needed
822    if (mTrimNodeRefs.size() > 0)
823      {
824         //TODO apply more than one trim path if necessary
825         VPathMesure pm;
826         float s = mTrimNodeRefs.front()->getStart(frameNo);
827         float e = mTrimNodeRefs.front()->getEnd(frameNo);
828
829         pm.setPath(mLocalPath);
830         pm.setStart(s);
831         pm.setEnd(e);
832         tempPath = pm.getPath();
833
834         mPathChanged = true;
835      }
836
837    // 3. compute the final path with parentMatrix
838
839    if ((flag & DirtyFlagBit::Matrix) || mPathChanged) {
840       mFinalPath = tempPath;
841       mFinalPath.transform(parentMatrix);
842       mPathChanged = true;
843    }
844
845    // 2. update the rendernode list
846    for (const auto &i : mRenderList) {
847       i.drawable->mFlag = VDrawable::DirtyState::None;
848       i.paintNodeRef->updateRenderNode(i.pathNodeRef, i.drawable, i.sameGroup);
849       if (mPathChanged) {
850           i.drawable->mPath = mFinalPath;
851           i.drawable->mFlag |= VDrawable::DirtyState::Path;
852       }
853    }
854 }
855
856 void LOTPathDataItem::renderList(std::vector<VDrawable *> &list)
857 {
858    for (const auto &i : mRenderList) {
859        list.push_back(i.drawable);
860    }
861 }
862
863 VPath LOTPathDataItem::path() const
864 {
865    return mFinalPath;
866 }
867
868
869 LOTRectItem::LOTRectItem(LOTRectData *data):LOTPathDataItem(data->isStatic()),mData(data)
870 {
871 }
872
873 VPath LOTRectItem::getPath(int frameNo)
874 {
875    VPointF pos = mData->mPos.value(frameNo);
876    VPointF size = mData->mSize.value(frameNo);
877    float radius = mData->mRound.value(frameNo);
878    VRectF r(pos.x() - size.x()/2, pos.y() - size.y()/2, size.x(), size.y());
879
880    VPath path;
881    path.addRoundRect(r, radius, radius, mData->direction());
882
883    return path;
884 }
885
886 LOTEllipseItem::LOTEllipseItem(LOTEllipseData *data):LOTPathDataItem(data->isStatic()),mData(data)
887 {
888
889 }
890
891 VPath LOTEllipseItem::getPath(int frameNo)
892 {
893    VPointF pos = mData->mPos.value(frameNo);
894    VPointF size = mData->mSize.value(frameNo);
895    VRectF r(pos.x() - size.x()/2, pos.y() - size.y()/2, size.x(), size.y());
896
897    VPath path;
898    path.addOval(r, mData->direction());
899
900    return path;
901 }
902
903 LOTShapeItem::LOTShapeItem(LOTShapeData *data):LOTPathDataItem(data->isStatic()),mData(data)
904 {
905
906 }
907
908 VPath LOTShapeItem::getPath(int frameNo)
909 {
910     LottieShapeData shapeData = mData->mShape.value(frameNo);
911
912     if (shapeData.mPoints.empty())
913      return VPath();
914
915     VPath path;
916
917     int size = shapeData.mPoints.size();
918     const VPointF *points = shapeData.mPoints.data();
919     path.moveTo(points[0]);
920     for (int i = 1 ; i < size; i+=3) {
921        path.cubicTo(points[i], points[i+1], points[i+2]);
922     }
923     if (shapeData.mClosed)
924       path.close();
925
926    return path;
927 }
928
929
930 LOTPolystarItem::LOTPolystarItem(LOTPolystarData *data):LOTPathDataItem(data->isStatic()),mData(data)
931 {
932
933 }
934
935 VPath LOTPolystarItem::getPath(int frameNo)
936 {
937    VPointF pos = mData->mPos.value(frameNo);
938    float points = mData->mPointCount.value(frameNo);
939    float innerRadius = mData->mInnerRadius.value(frameNo);
940    float outerRadius = mData->mOuterRadius.value(frameNo);
941    float innerRoundness = mData->mInnerRoundness.value(frameNo);
942    float outerRoundness = mData->mOuterRoundness.value(frameNo);
943    float rotation = mData->mRotation.value(frameNo);
944
945    VPath path;
946    VMatrix m;
947
948    if (mData->mType == LOTPolystarData::PolyType::Star) {
949         path.addPolystarStar(0.0, 0.0, 0.0, points,
950                              innerRadius, outerRadius,
951                              innerRoundness, outerRoundness,
952                              mData->direction());
953    } else {
954         path.addPolystarPolygon(0.0, 0.0, 0.0, points,
955                                 outerRadius, outerRoundness,
956                                 mData->direction());
957    }
958
959    m.translate(pos.x(), pos.y()).rotate(rotation);
960    m.rotate(rotation);
961    path.transform(m);
962
963    return path;
964 }
965
966
967
968 /*
969  * PaintData Node handling
970  *
971  */
972
973 void LOTPaintDataItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
974 {
975    mContentChanged = false;
976    mParentAlpha = parentAlpha;
977    mParentMatrix = parentMatrix;
978    mFlag = flag;
979    mFrameNo = frameNo;
980    // 1. update the local content if needed
981   // if (!(mInit && mStaticContent)) {
982       mInit = true;
983       updateContent(frameNo);
984       mContentChanged = true;
985   // }
986 }
987
988
989 LOTFillItem::LOTFillItem(LOTFillData *data):LOTPaintDataItem(data->isStatic()),mData(data)
990 {
991 }
992
993 void LOTFillItem::updateContent(int frameNo)
994 {
995    LottieColor c = mData->mColor.value(frameNo);
996    float opacity = mData->opacity(frameNo);
997    mColor = c.toColor(opacity);
998    mFillRule = mData->fillRule();
999 }
1000
1001 void LOTFillItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
1002 {
1003     VColor color = mColor;
1004     if (sameParent)
1005       color.setAlpha(color.a * pathNode->combinedAlpha());
1006     else
1007       color.setAlpha(color.a  * parentAlpha() * pathNode->combinedAlpha());
1008     VBrush brush(color);
1009     drawable->setBrush(brush);
1010     drawable->setFillRule(mFillRule);
1011 }
1012
1013
1014 LOTGFillItem::LOTGFillItem(LOTGFillData *data):LOTPaintDataItem(data->isStatic()),mData(data)
1015 {
1016 }
1017
1018 void LOTGFillItem::updateContent(int frameNo)
1019 {
1020     mData->update(mGradient, frameNo);
1021     mGradient->mMatrix = mParentMatrix;
1022     mFillRule = mData->fillRule();
1023 }
1024
1025 void LOTGFillItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
1026 {
1027     drawable->setBrush(VBrush(mGradient.get()));
1028     drawable->setFillRule(mFillRule);
1029 }
1030
1031 LOTStrokeItem::LOTStrokeItem(LOTStrokeData *data):LOTPaintDataItem(data->isStatic()),mData(data)
1032 {
1033     mDashArraySize = 0;
1034 }
1035
1036 void LOTStrokeItem::updateContent(int frameNo)
1037 {
1038     LottieColor c = mData->mColor.value(frameNo);
1039     float opacity = mData->opacity(frameNo);
1040     mColor = c.toColor(opacity);
1041     mCap = mData->capStyle();
1042     mJoin = mData->joinStyle();
1043     mMiterLimit = mData->meterLimit();
1044     mWidth = mData->width(frameNo);
1045     if (mData->hasDashInfo()) {
1046         mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
1047     }
1048 }
1049
1050 static float getScale(const VMatrix &matrix)
1051 {
1052     constexpr float SQRT_2 = 1.41421;
1053     VPointF p1(0,0);
1054     VPointF p2(SQRT_2,SQRT_2);
1055     p1 = matrix.map(p1);
1056     p2 = matrix.map(p2);
1057     VPointF final = p2 - p1;
1058
1059     return std::sqrt( final.x() * final.x() + final.y() * final.y());
1060 }
1061
1062 void LOTStrokeItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
1063 {
1064     VColor color = mColor;
1065     if (sameParent)
1066       color.setAlpha(color.a * pathNode->combinedAlpha());
1067     else
1068       color.setAlpha(color.a  * parentAlpha() * pathNode->combinedAlpha());
1069
1070     VBrush brush(color);
1071     drawable->setBrush(brush);
1072
1073     drawable->setStrokeInfo(mCap, mJoin, mMiterLimit,  mWidth * getScale(mParentMatrix));
1074     if (mDashArraySize) {
1075         drawable->setDashInfo(mDashArray, mDashArraySize);
1076     }
1077 }
1078
1079 LOTGStrokeItem::LOTGStrokeItem(LOTGStrokeData *data):LOTPaintDataItem(data->isStatic()),mData(data)
1080 {
1081     mDashArraySize = 0;
1082 }
1083
1084 void LOTGStrokeItem::updateContent(int frameNo)
1085 {
1086     mData->update(mGradient, frameNo);
1087     mGradient->mMatrix = mParentMatrix;
1088     mCap = mData->capStyle();
1089     mJoin = mData->joinStyle();
1090     mMiterLimit = mData->meterLimit();
1091     mWidth = mData->width(frameNo);
1092     if (mData->hasDashInfo()) {
1093         mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
1094     }
1095 }
1096
1097 void LOTGStrokeItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
1098 {
1099     drawable->setBrush(VBrush(mGradient.get()));
1100     drawable->setStrokeInfo(mCap, mJoin, mMiterLimit,  mWidth * getScale(mParentMatrix));
1101     if (mDashArraySize) {
1102         drawable->setDashInfo(mDashArray, mDashArraySize);
1103     }
1104 }
1105
1106 LOTTrimItem::LOTTrimItem(LOTTrimData *data):mData(data)
1107 {
1108 }
1109
1110 void LOTTrimItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
1111 {
1112 }
1113
1114 LOTRepeaterItem::LOTRepeaterItem(LOTRepeaterData *data):mData(data)
1115 {
1116
1117 }
1118
1119 void LOTRepeaterItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
1120 {
1121
1122 }
1123
1124 void LOTRepeaterItem::renderList(std::vector<VDrawable *> &list)
1125 {
1126
1127 }
1128