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