lottie: handle trim path
[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::unique_ptr<LOTMaskItem>(new 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::unique_ptr<VDrawable>(new 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     mRoot->processTrimOperation();
611 }
612
613 LOTShapeLayerItem::~LOTShapeLayerItem()
614 {
615     delete mRoot;
616 }
617
618 LOTContentItem * LOTShapeLayerItem::createContentItem(LOTData *contentData)
619 {
620     switch(contentData->type()) {
621         case LOTData::Type::ShapeGroup: {
622             return new LOTContentGroupItem(static_cast<LOTShapeGroupData *>(contentData));
623             break;
624         }
625         case LOTData::Type::Rect: {
626             return new LOTRectItem(static_cast<LOTRectData *>(contentData));
627             break;
628         }
629         case LOTData::Type::Ellipse: {
630             return new LOTEllipseItem(static_cast<LOTEllipseData *>(contentData));
631             break;
632         }
633         case LOTData::Type::Shape: {
634             return new LOTShapeItem(static_cast<LOTShapeData *>(contentData));
635             break;
636         }
637         case LOTData::Type::Polystar: {
638             return new LOTPolystarItem(static_cast<LOTPolystarData *>(contentData));
639             break;
640         }
641         case LOTData::Type::Fill: {
642             return new LOTFillItem(static_cast<LOTFillData *>(contentData));
643             break;
644         }
645         case LOTData::Type::GFill: {
646             return new LOTGFillItem(static_cast<LOTGFillData *>(contentData));
647             break;
648         }
649         case LOTData::Type::Stroke: {
650             return new LOTStrokeItem(static_cast<LOTStrokeData *>(contentData));
651             break;
652         }
653         case LOTData::Type::GStroke: {
654             return new LOTGStrokeItem(static_cast<LOTGStrokeData *>(contentData));
655             break;
656         }
657         case LOTData::Type::Repeater: {
658                 return new LOTRepeaterItem(static_cast<LOTRepeaterData *>(contentData));
659                 break;
660             }
661         case LOTData::Type::Trim: {
662             return new LOTTrimItem(static_cast<LOTTrimData *>(contentData));
663             break;
664         }
665         default:
666             return nullptr;
667             break;
668     }
669 }
670
671 void LOTShapeLayerItem::updateContent()
672 {
673    mRoot->update(frameNo(), combinedMatrix(), combinedAlpha(), flag());
674 }
675
676 void LOTShapeLayerItem::renderList(std::vector<VDrawable *> &list)
677 {
678     if (!visible()) return;
679     mRoot->renderList(list);
680 }
681
682 LOTContentGroupItem::LOTContentGroupItem(LOTShapeGroupData *data):mData(data)
683 {
684    addChildren(mData);
685 }
686
687 void LOTContentGroupItem::addChildren(LOTGroupData *data)
688 {
689    if (!data) return;
690
691    for(auto i : data->mChildren) {
692       LOTData *data = i.get();
693       LOTContentItem *content = LOTShapeLayerItem::createContentItem(data);
694       if (content)
695          mContents.push_back(content);
696    }
697 }
698
699 LOTContentGroupItem::~LOTContentGroupItem()
700 {
701     for(auto i : mContents) {
702         delete i;
703     }
704 }
705
706
707 void LOTContentGroupItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
708 {
709    VMatrix m = parentMatrix;
710    float alpha = parentAlpha;
711    DirtyFlag newFlag = flag;
712
713    if (mData) {
714       // update the matrix and the flag
715       if ((flag & DirtyFlagBit::Matrix) || !mData->mTransform->staticMatrix() ) {
716          newFlag |= DirtyFlagBit::Matrix;
717       }
718       m = mData->mTransform->matrix(frameNo) * parentMatrix;
719       alpha *= mData->mTransform->opacity(frameNo);
720
721       if (!vCompare(alpha, parentAlpha)) {
722          newFlag |= DirtyFlagBit::Alpha;
723       }
724    }
725
726    for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
727       (*i)->update(frameNo, m, alpha, newFlag);
728    }
729 }
730
731 void LOTContentGroupItem::renderList(std::vector<VDrawable *> &list)
732 {
733     for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
734        (*i)->renderList(list);
735     }
736 }
737
738 void LOTContentGroupItem::processPaintOperation()
739 {
740    std::vector<LOTPaintDataItem *> list;
741    paintOperationHelper(list);
742 }
743
744 void LOTContentGroupItem::paintOperationHelper(std::vector<LOTPaintDataItem *> &list)
745 {
746    int curOpCount = list.size();
747    for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
748       auto child = *i;
749       if (auto pathNode = dynamic_cast<LOTPathDataItem *>(child)) {
750          // the node is a path data node add the paint operation list to it.
751          pathNode->addPaintOperation(list, curOpCount);
752       } else if (auto paintNode = dynamic_cast<LOTPaintDataItem *>(child)) {
753          // add it to the paint operation list
754          list.push_back(paintNode);
755       } else if (auto groupNode = dynamic_cast<LOTContentGroupItem *>(child)) {
756          // update the groups node with current list
757          groupNode->paintOperationHelper(list);
758       }
759    }
760    list.erase(list.begin() + curOpCount, list.end());
761 }
762
763 void LOTPathDataItem::addPaintOperation(std::vector<LOTPaintDataItem *> &list, int externalCount)
764 {
765     for(auto paintItem : list) {
766       bool sameGroup = (externalCount-- > 0) ? false : true;
767       mNodeList.push_back(std::unique_ptr<VDrawable>(new VDrawable()));
768       mRenderList.push_back(LOTRenderNode(this, paintItem, mNodeList.back().get(), sameGroup));
769     }
770 }
771
772 void LOTContentGroupItem::processTrimOperation()
773 {
774    std::vector<LOTTrimItem *> list;
775    trimOperationHelper(list);
776 }
777
778 void LOTContentGroupItem::trimOperationHelper(std::vector<LOTTrimItem *> &list)
779 {
780    int curOpCount = list.size();
781    for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
782       auto child = *i;
783       if (auto pathNode = dynamic_cast<LOTPathDataItem *>(child)) {
784          // the node is a path data node add the paint operation list to it.
785          pathNode->addTrimOperation(list);
786       } else if (auto trimNode = dynamic_cast<LOTTrimItem *>(child)) {
787          // add it to the trim operation list
788          list.push_back(trimNode);
789       } else if (auto groupNode = dynamic_cast<LOTContentGroupItem *>(child)) {
790          // update the groups node with current list
791          groupNode->trimOperationHelper(list);
792       }
793    }
794    list.erase(list.begin() + curOpCount, list.end());
795 }
796
797 void LOTPathDataItem::addTrimOperation(std::vector<LOTTrimItem *> &list)
798 {
799    for(auto trimItem : list) {
800           mTrimNodeRefs.push_back(trimItem);
801    }
802 }
803
804 void LOTPathDataItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
805 {
806    VPath tempPath;
807
808    mPathChanged = false;
809    mCombinedAlpha = parentAlpha;
810
811    // 1. update the local path if needed
812    if (!(mInit && mStaticPath)) {
813       mLocalPath = getPath(frameNo);
814       mInit = true;
815       mPathChanged = true;
816    }
817
818    tempPath = mLocalPath;
819
820    // 2. apply path operation if needed
821    if (mTrimNodeRefs.size() > 0)
822      {
823         //TODO apply more than one trim path if necessary
824         VPathMesure pm;
825         float s = mTrimNodeRefs.front()->getStart(frameNo);
826         float e = mTrimNodeRefs.front()->getEnd(frameNo);
827
828         pm.setPath(mLocalPath);
829         pm.setStart(s);
830         pm.setEnd(e);
831         tempPath = pm.getPath();
832
833         mPathChanged = true;
834      }
835
836    // 3. compute the final path with parentMatrix
837
838    if ((flag & DirtyFlagBit::Matrix) || mPathChanged) {
839       mFinalPath = tempPath;
840       mFinalPath.transform(parentMatrix);
841       mPathChanged = true;
842    }
843
844    // 2. update the rendernode list
845    for (const auto &i : mRenderList) {
846       i.drawable->mFlag = VDrawable::DirtyState::None;
847       i.paintNodeRef->updateRenderNode(i.pathNodeRef, i.drawable, i.sameGroup);
848       if (mPathChanged) {
849           i.drawable->mPath = mFinalPath;
850           i.drawable->mFlag |= VDrawable::DirtyState::Path;
851       }
852    }
853 }
854
855 void LOTPathDataItem::renderList(std::vector<VDrawable *> &list)
856 {
857    for (const auto &i : mRenderList) {
858        list.push_back(i.drawable);
859    }
860 }
861
862 VPath LOTPathDataItem::path() const
863 {
864    return mFinalPath;
865 }
866
867
868 LOTRectItem::LOTRectItem(LOTRectData *data):LOTPathDataItem(data->isStatic()),mData(data)
869 {
870 }
871
872 VPath LOTRectItem::getPath(int frameNo)
873 {
874    VPointF pos = mData->mPos.value(frameNo);
875    VPointF size = mData->mSize.value(frameNo);
876    float radius = mData->mRound.value(frameNo);
877    VRectF r(pos.x() - size.x()/2, pos.y() - size.y()/2, size.x(), size.y());
878
879    VPath path;
880    path.addRoundRect(r, radius, radius, mData->direction());
881
882    return path;
883 }
884
885 LOTEllipseItem::LOTEllipseItem(LOTEllipseData *data):LOTPathDataItem(data->isStatic()),mData(data)
886 {
887
888 }
889
890 VPath LOTEllipseItem::getPath(int frameNo)
891 {
892    VPointF pos = mData->mPos.value(frameNo);
893    VPointF size = mData->mSize.value(frameNo);
894    VRectF r(pos.x() - size.x()/2, pos.y() - size.y()/2, size.x(), size.y());
895
896    VPath path;
897    path.addOval(r, mData->direction());
898
899    return path;
900 }
901
902 LOTShapeItem::LOTShapeItem(LOTShapeData *data):LOTPathDataItem(data->isStatic()),mData(data)
903 {
904
905 }
906
907 VPath LOTShapeItem::getPath(int frameNo)
908 {
909     LottieShapeData shapeData = mData->mShape.value(frameNo);
910
911     if (shapeData.mPoints.empty())
912      return VPath();
913
914     VPath path;
915
916     int size = shapeData.mPoints.size();
917     const VPointF *points = shapeData.mPoints.data();
918     path.moveTo(points[0]);
919     for (int i = 1 ; i < size; i+=3) {
920        path.cubicTo(points[i], points[i+1], points[i+2]);
921     }
922     if (shapeData.mClosed)
923       path.close();
924
925    return path;
926 }
927
928
929 LOTPolystarItem::LOTPolystarItem(LOTPolystarData *data):LOTPathDataItem(data->isStatic()),mData(data)
930 {
931
932 }
933
934 VPath LOTPolystarItem::getPath(int frameNo)
935 {
936    VPointF pos = mData->mPos.value(frameNo);
937    float points = mData->mPointCount.value(frameNo);
938    float innerRadius = mData->mInnerRadius.value(frameNo);
939    float outerRadius = mData->mOuterRadius.value(frameNo);
940    float innerRoundness = mData->mInnerRoundness.value(frameNo);
941    float outerRoundness = mData->mOuterRoundness.value(frameNo);
942    float rotation = mData->mRotation.value(frameNo);
943
944    VPath path;
945    VMatrix m;
946
947    if (mData->mType == LOTPolystarData::PolyType::Star) {
948         path.addPolystarStar(0.0, 0.0, 0.0, points,
949                              innerRadius, outerRadius,
950                              innerRoundness, outerRoundness,
951                              mData->direction());
952    } else {
953         path.addPolystarPolygon(0.0, 0.0, 0.0, points,
954                                 outerRadius, outerRoundness,
955                                 mData->direction());
956    }
957
958    m.translate(pos.x(), pos.y()).rotate(rotation);
959    m.rotate(rotation);
960    path.transform(m);
961
962    return path;
963 }
964
965
966
967 /*
968  * PaintData Node handling
969  *
970  */
971
972 void LOTPaintDataItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
973 {
974    mContentChanged = false;
975    mParentAlpha = parentAlpha;
976    mParentMatrix = parentMatrix;
977    mFlag = flag;
978    mFrameNo = frameNo;
979    // 1. update the local content if needed
980   // if (!(mInit && mStaticContent)) {
981       mInit = true;
982       updateContent(frameNo);
983       mContentChanged = true;
984   // }
985 }
986
987
988 LOTFillItem::LOTFillItem(LOTFillData *data):LOTPaintDataItem(data->isStatic()),mData(data)
989 {
990 }
991
992 void LOTFillItem::updateContent(int frameNo)
993 {
994    LottieColor c = mData->mColor.value(frameNo);
995    float opacity = mData->opacity(frameNo);
996    mColor = c.toColor(opacity);
997    mFillRule = mData->fillRule();
998 }
999
1000 void LOTFillItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
1001 {
1002     VColor color = mColor;
1003     if (sameParent)
1004       color.setAlpha(color.a * pathNode->combinedAlpha());
1005     else
1006       color.setAlpha(color.a  * parentAlpha() * pathNode->combinedAlpha());
1007     VBrush brush(color);
1008     drawable->setBrush(brush);
1009     drawable->setFillRule(mFillRule);
1010 }
1011
1012
1013 LOTGFillItem::LOTGFillItem(LOTGFillData *data):LOTPaintDataItem(data->isStatic()),mData(data)
1014 {
1015 }
1016
1017 void LOTGFillItem::updateContent(int frameNo)
1018 {
1019     mData->update(mGradient, frameNo);
1020     mGradient->mMatrix = mParentMatrix;
1021     mFillRule = mData->fillRule();
1022 }
1023
1024 void LOTGFillItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
1025 {
1026     drawable->setBrush(VBrush(mGradient.get()));
1027     drawable->setFillRule(mFillRule);
1028 }
1029
1030 LOTStrokeItem::LOTStrokeItem(LOTStrokeData *data):LOTPaintDataItem(data->isStatic()),mData(data)
1031 {
1032     mDashArraySize = 0;
1033 }
1034
1035 void LOTStrokeItem::updateContent(int frameNo)
1036 {
1037     LottieColor c = mData->mColor.value(frameNo);
1038     float opacity = mData->opacity(frameNo);
1039     mColor = c.toColor(opacity);
1040     mCap = mData->capStyle();
1041     mJoin = mData->joinStyle();
1042     mMiterLimit = mData->meterLimit();
1043     mWidth = mData->width(frameNo);
1044     if (mData->hasDashInfo()) {
1045         mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
1046     }
1047 }
1048
1049 static float getScale(const VMatrix &matrix)
1050 {
1051     constexpr float SQRT_2 = 1.41421;
1052     VPointF p1(0,0);
1053     VPointF p2(SQRT_2,SQRT_2);
1054     p1 = matrix.map(p1);
1055     p2 = matrix.map(p2);
1056     VPointF final = p2 - p1;
1057
1058     return std::sqrt( final.x() * final.x() + final.y() * final.y());
1059 }
1060
1061 void LOTStrokeItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
1062 {
1063     VColor color = mColor;
1064     if (sameParent)
1065       color.setAlpha(color.a * pathNode->combinedAlpha());
1066     else
1067       color.setAlpha(color.a  * parentAlpha() * pathNode->combinedAlpha());
1068
1069     VBrush brush(color);
1070     drawable->setBrush(brush);
1071
1072     drawable->setStrokeInfo(mCap, mJoin, mMiterLimit,  mWidth * getScale(mParentMatrix));
1073     if (mDashArraySize) {
1074         drawable->setDashInfo(mDashArray, mDashArraySize);
1075     }
1076 }
1077
1078 LOTGStrokeItem::LOTGStrokeItem(LOTGStrokeData *data):LOTPaintDataItem(data->isStatic()),mData(data)
1079 {
1080     mDashArraySize = 0;
1081 }
1082
1083 void LOTGStrokeItem::updateContent(int frameNo)
1084 {
1085     mData->update(mGradient, frameNo);
1086     mGradient->mMatrix = mParentMatrix;
1087     mCap = mData->capStyle();
1088     mJoin = mData->joinStyle();
1089     mMiterLimit = mData->meterLimit();
1090     mWidth = mData->width(frameNo);
1091     if (mData->hasDashInfo()) {
1092         mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
1093     }
1094 }
1095
1096 void LOTGStrokeItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
1097 {
1098     drawable->setBrush(VBrush(mGradient.get()));
1099     drawable->setStrokeInfo(mCap, mJoin, mMiterLimit,  mWidth * getScale(mParentMatrix));
1100     if (mDashArraySize) {
1101         drawable->setDashInfo(mDashArray, mDashArraySize);
1102     }
1103 }
1104
1105 LOTTrimItem::LOTTrimItem(LOTTrimData *data):mData(data)
1106 {
1107 }
1108
1109 void LOTTrimItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
1110 {
1111 }
1112
1113 LOTRepeaterItem::LOTRepeaterItem(LOTRepeaterData *data):mData(data)
1114 {
1115
1116 }
1117
1118 void LOTRepeaterItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
1119 {
1120
1121 }
1122
1123 void LOTRepeaterItem::renderList(std::vector<VDrawable *> &list)
1124 {
1125
1126 }
1127