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