lottie/optimization: reduce temporary object by calling *= operator.
[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);
424    m *= parentMatrix;
425    float alpha = parentAlpha * opacity(frameNo);
426
427    //6. update the mask
428    if (hasMask()) {
429        for (auto &i : mMasks)
430            i->update(frameNo, m, alpha, mDirtyFlag);
431    }
432
433    // 3. update the dirty flag based on the change
434    if (!mCombinedMatrix.fuzzyCompare(m)) {
435        mDirtyFlag |= DirtyFlagBit::Matrix;
436    }
437    if (!vCompare(mCombinedAlpha, alpha)) {
438        mDirtyFlag |= DirtyFlagBit::Alpha;
439    }
440    mCombinedMatrix = m;
441    mCombinedAlpha = alpha;
442
443    // 4. if no parent property change and layer is static then nothing to do.
444    if ((flag() & DirtyFlagBit::None) && isStatic())
445       return;
446
447    //5. update the content of the layer
448    updateContent();
449
450    //6. reset the dirty flag
451    mDirtyFlag = DirtyFlagBit::None;
452 }
453
454 float
455 LOTLayerItem::opacity(int frameNo) const
456 {
457    return mLayerData->mTransform->opacity(frameNo);
458 }
459
460 VMatrix
461 LOTLayerItem::matrix(int frameNo) const
462 {
463     if (mParentLayer)
464         return mLayerData->mTransform->matrix(frameNo) * mParentLayer->matrix(frameNo);
465     else
466         return mLayerData->mTransform->matrix(frameNo);
467 }
468
469 bool LOTLayerItem::visible() const
470 {
471    if (frameNo() >= mLayerData->inFrame() && frameNo() < mLayerData->outFrame())
472       return true;
473    else
474       return false;
475 }
476
477
478
479 LOTCompLayerItem::LOTCompLayerItem(LOTLayerData *layerModel):LOTLayerItem(layerModel)
480 {
481    for(auto i : mLayerData->mChildren) {
482       LOTLayerData *layerModel = dynamic_cast<LOTLayerData *>(i.get());
483       if (layerModel) {
484          LOTLayerItem *layerItem = LOTCompItem::createLayerItem(layerModel);
485          if (layerItem) {
486             mLayers.push_back(layerItem);
487             mLayerMap[layerItem->id()] = layerItem;
488          }
489       }
490    }
491
492    //2. update parent layer
493    for(auto i : mLayers) {
494       int id = i->parentId();
495       if (id >=0) {
496          auto search = mLayerMap.find(id);
497          if (search != mLayerMap.end()) {
498            LOTLayerItem *parentLayer = search->second;
499            i->setParentLayer(parentLayer);
500          }
501       }
502       i->setPrecompLayer(this);
503    }
504 }
505
506 void LOTCompLayerItem::updateStaticProperty()
507 {
508     LOTLayerItem::updateStaticProperty();
509
510     for(auto i : mLayers) {
511        i->updateStaticProperty();
512     }
513 }
514
515 void LOTCompLayerItem::render(VPainter *painter, const VRle &inheritMask)
516 {
517     VRle mask = inheritMask;
518
519     if (hasMask()) {
520         if (mask.isEmpty())
521             mask = maskRle(painter->clipBoundingRect());
522         else
523             mask = mask & inheritMask;
524     }
525
526     for(auto i : mLayers) {
527        i->render(painter, mask);
528     }
529 }
530
531 LOTCompLayerItem::~LOTCompLayerItem()
532 {
533     for(auto i : mLayers) {
534        delete i;
535     }
536 }
537
538 void LOTCompLayerItem::updateContent()
539 {
540     // update the layer from back to front
541     for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
542        LOTLayerItem *layer = *i;
543        layer->update(frameNo(), combinedMatrix(), combinedAlpha());
544     }
545 }
546
547 void LOTCompLayerItem::renderList(std::vector<VDrawable *> &list)
548 {
549     if (!visible()) return;
550
551     // update the layer from back to front
552     for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
553        LOTLayerItem *layer = *i;
554        layer->renderList(list);
555     }
556 }
557
558 LOTSolidLayerItem::LOTSolidLayerItem(LOTLayerData *layerData):LOTLayerItem(layerData)
559 {
560
561 }
562
563 void LOTSolidLayerItem::updateContent()
564 {
565    if (!mRenderNode) {
566       mRenderNode = std::make_unique<VDrawable>();
567       mRenderNode->mType = VDrawable::Type::Fill;
568       mRenderNode->mFlag |= VDrawable::DirtyState::All;
569    }
570
571    if (flag() & DirtyFlagBit::Matrix) {
572        VPath path;
573        path.addRect(VRectF(0, 0, mLayerData->solidWidth(), mLayerData->solidHeight()));
574        path.transform(combinedMatrix());
575        mRenderNode->mFlag |= VDrawable::DirtyState::Path;
576        mRenderNode->mPath = path;
577    }
578    if (flag() & DirtyFlagBit::Alpha) {
579        LottieColor color = mLayerData->solidColor();
580        VBrush brush(color.toColor(combinedAlpha()));
581        mRenderNode->setBrush(brush);
582        mRenderNode->mFlag |= VDrawable::DirtyState::Brush;
583    }
584 }
585
586 void LOTSolidLayerItem::renderList(std::vector<VDrawable *> &list)
587 {
588     if (!visible()) return;
589
590     list.push_back(mRenderNode.get());
591 }
592
593 LOTNullLayerItem::LOTNullLayerItem(LOTLayerData *layerData):LOTLayerItem(layerData)
594 {
595
596 }
597 void LOTNullLayerItem::updateContent()
598 {
599
600 }
601
602
603 LOTShapeLayerItem::LOTShapeLayerItem(LOTLayerData *layerData):LOTLayerItem(layerData)
604 {
605     mRoot = new LOTContentGroupItem(nullptr);
606     mRoot->addChildren(layerData);
607     mRoot->processPaintOperation();
608     if (layerData->hasPathOperator())
609       mRoot->processTrimOperation();
610 }
611
612 LOTShapeLayerItem::~LOTShapeLayerItem()
613 {
614     delete mRoot;
615 }
616
617 LOTContentItem * LOTShapeLayerItem::createContentItem(LOTData *contentData)
618 {
619     switch(contentData->type()) {
620         case LOTData::Type::ShapeGroup: {
621             return new LOTContentGroupItem(static_cast<LOTShapeGroupData *>(contentData));
622             break;
623         }
624         case LOTData::Type::Rect: {
625             return new LOTRectItem(static_cast<LOTRectData *>(contentData));
626             break;
627         }
628         case LOTData::Type::Ellipse: {
629             return new LOTEllipseItem(static_cast<LOTEllipseData *>(contentData));
630             break;
631         }
632         case LOTData::Type::Shape: {
633             return new LOTShapeItem(static_cast<LOTShapeData *>(contentData));
634             break;
635         }
636         case LOTData::Type::Polystar: {
637             return new LOTPolystarItem(static_cast<LOTPolystarData *>(contentData));
638             break;
639         }
640         case LOTData::Type::Fill: {
641             return new LOTFillItem(static_cast<LOTFillData *>(contentData));
642             break;
643         }
644         case LOTData::Type::GFill: {
645             return new LOTGFillItem(static_cast<LOTGFillData *>(contentData));
646             break;
647         }
648         case LOTData::Type::Stroke: {
649             return new LOTStrokeItem(static_cast<LOTStrokeData *>(contentData));
650             break;
651         }
652         case LOTData::Type::GStroke: {
653             return new LOTGStrokeItem(static_cast<LOTGStrokeData *>(contentData));
654             break;
655         }
656         case LOTData::Type::Repeater: {
657                 return new LOTRepeaterItem(static_cast<LOTRepeaterData *>(contentData));
658                 break;
659             }
660         case LOTData::Type::Trim: {
661             return new LOTTrimItem(static_cast<LOTTrimData *>(contentData));
662             break;
663         }
664         default:
665             return nullptr;
666             break;
667     }
668 }
669
670 void LOTShapeLayerItem::updateContent()
671 {
672    mRoot->update(frameNo(), combinedMatrix(), combinedAlpha(), flag());
673 }
674
675 void LOTShapeLayerItem::renderList(std::vector<VDrawable *> &list)
676 {
677     if (!visible()) return;
678     mRoot->renderList(list);
679 }
680
681 LOTContentGroupItem::LOTContentGroupItem(LOTShapeGroupData *data):mData(data)
682 {
683    addChildren(mData);
684 }
685
686 void LOTContentGroupItem::addChildren(LOTGroupData *data)
687 {
688    if (!data) return;
689
690    for(auto i : data->mChildren) {
691       LOTData *data = i.get();
692       LOTContentItem *content = LOTShapeLayerItem::createContentItem(data);
693       if (content)
694          mContents.push_back(content);
695    }
696 }
697
698 LOTContentGroupItem::~LOTContentGroupItem()
699 {
700     for(auto i : mContents) {
701         delete i;
702     }
703 }
704
705
706 void LOTContentGroupItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
707 {
708    VMatrix m = parentMatrix;
709    float alpha = parentAlpha;
710    DirtyFlag newFlag = flag;
711
712    if (mData) {
713       // update the matrix and the flag
714       if ((flag & DirtyFlagBit::Matrix) || !mData->mTransform->staticMatrix() ) {
715          newFlag |= DirtyFlagBit::Matrix;
716       }
717       m = mData->mTransform->matrix(frameNo);
718       m *= 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::make_unique<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 trim 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     return mData->mShape.value(frameNo).toPath();
910 }
911
912
913 LOTPolystarItem::LOTPolystarItem(LOTPolystarData *data):LOTPathDataItem(data->isStatic()),mData(data)
914 {
915
916 }
917
918 VPath LOTPolystarItem::getPath(int frameNo)
919 {
920    VPointF pos = mData->mPos.value(frameNo);
921    float points = mData->mPointCount.value(frameNo);
922    float innerRadius = mData->mInnerRadius.value(frameNo);
923    float outerRadius = mData->mOuterRadius.value(frameNo);
924    float innerRoundness = mData->mInnerRoundness.value(frameNo);
925    float outerRoundness = mData->mOuterRoundness.value(frameNo);
926    float rotation = mData->mRotation.value(frameNo);
927
928    VPath path;
929    VMatrix m;
930
931    if (mData->mType == LOTPolystarData::PolyType::Star) {
932         path.addPolystar(points, innerRadius, outerRadius,
933                          innerRoundness, outerRoundness,
934                          0.0, 0.0, 0.0, mData->direction());
935    } else {
936         path.addPolygon(points, outerRadius, outerRoundness,
937                         0.0, 0.0, 0.0, mData->direction());
938    }
939
940    m.translate(pos.x(), pos.y()).rotate(rotation);
941    m.rotate(rotation);
942    path.transform(m);
943
944    return path;
945 }
946
947
948
949 /*
950  * PaintData Node handling
951  *
952  */
953
954 void LOTPaintDataItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
955 {
956    mContentChanged = false;
957    mParentAlpha = parentAlpha;
958    mParentMatrix = parentMatrix;
959    mFlag = flag;
960    mFrameNo = frameNo;
961    // 1. update the local content if needed
962   // if (!(mInit && mStaticContent)) {
963       mInit = true;
964       updateContent(frameNo);
965       mContentChanged = true;
966   // }
967 }
968
969
970 LOTFillItem::LOTFillItem(LOTFillData *data):LOTPaintDataItem(data->isStatic()),mData(data)
971 {
972 }
973
974 void LOTFillItem::updateContent(int frameNo)
975 {
976    LottieColor c = mData->mColor.value(frameNo);
977    float opacity = mData->opacity(frameNo);
978    mColor = c.toColor(opacity);
979    mFillRule = mData->fillRule();
980 }
981
982 void LOTFillItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
983 {
984     VColor color = mColor;
985     if (sameParent)
986       color.setAlpha(color.a * pathNode->combinedAlpha());
987     else
988       color.setAlpha(color.a  * parentAlpha() * pathNode->combinedAlpha());
989     VBrush brush(color);
990     drawable->setBrush(brush);
991     drawable->setFillRule(mFillRule);
992 }
993
994
995 LOTGFillItem::LOTGFillItem(LOTGFillData *data):LOTPaintDataItem(data->isStatic()),mData(data)
996 {
997 }
998
999 void LOTGFillItem::updateContent(int frameNo)
1000 {
1001     mData->update(mGradient, frameNo);
1002     mGradient->mMatrix = mParentMatrix;
1003     mFillRule = mData->fillRule();
1004 }
1005
1006 void LOTGFillItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
1007 {
1008     drawable->setBrush(VBrush(mGradient.get()));
1009     drawable->setFillRule(mFillRule);
1010 }
1011
1012 LOTStrokeItem::LOTStrokeItem(LOTStrokeData *data):LOTPaintDataItem(data->isStatic()),mData(data)
1013 {
1014     mDashArraySize = 0;
1015 }
1016
1017 void LOTStrokeItem::updateContent(int frameNo)
1018 {
1019     LottieColor c = mData->mColor.value(frameNo);
1020     float opacity = mData->opacity(frameNo);
1021     mColor = c.toColor(opacity);
1022     mCap = mData->capStyle();
1023     mJoin = mData->joinStyle();
1024     mMiterLimit = mData->meterLimit();
1025     mWidth = mData->width(frameNo);
1026     if (mData->hasDashInfo()) {
1027         mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
1028     }
1029 }
1030
1031 static float getScale(const VMatrix &matrix)
1032 {
1033     constexpr float SQRT_2 = 1.41421;
1034     VPointF p1(0,0);
1035     VPointF p2(SQRT_2,SQRT_2);
1036     p1 = matrix.map(p1);
1037     p2 = matrix.map(p2);
1038     VPointF final = p2 - p1;
1039
1040     return std::sqrt( final.x() * final.x() + final.y() * final.y());
1041 }
1042
1043 void LOTStrokeItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
1044 {
1045     VColor color = mColor;
1046     if (sameParent)
1047       color.setAlpha(color.a * pathNode->combinedAlpha());
1048     else
1049       color.setAlpha(color.a  * parentAlpha() * pathNode->combinedAlpha());
1050
1051     VBrush brush(color);
1052     drawable->setBrush(brush);
1053
1054     drawable->setStrokeInfo(mCap, mJoin, mMiterLimit,  mWidth * getScale(mParentMatrix));
1055     if (mDashArraySize) {
1056         drawable->setDashInfo(mDashArray, mDashArraySize);
1057     }
1058 }
1059
1060 LOTGStrokeItem::LOTGStrokeItem(LOTGStrokeData *data):LOTPaintDataItem(data->isStatic()),mData(data)
1061 {
1062     mDashArraySize = 0;
1063 }
1064
1065 void LOTGStrokeItem::updateContent(int frameNo)
1066 {
1067     mData->update(mGradient, frameNo);
1068     mGradient->mMatrix = mParentMatrix;
1069     mCap = mData->capStyle();
1070     mJoin = mData->joinStyle();
1071     mMiterLimit = mData->meterLimit();
1072     mWidth = mData->width(frameNo);
1073     if (mData->hasDashInfo()) {
1074         mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
1075     }
1076 }
1077
1078 void LOTGStrokeItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
1079 {
1080     drawable->setBrush(VBrush(mGradient.get()));
1081     drawable->setStrokeInfo(mCap, mJoin, mMiterLimit,  mWidth * getScale(mParentMatrix));
1082     if (mDashArraySize) {
1083         drawable->setDashInfo(mDashArray, mDashArraySize);
1084     }
1085 }
1086
1087 LOTTrimItem::LOTTrimItem(LOTTrimData *data):mData(data)
1088 {
1089 }
1090
1091 void LOTTrimItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
1092 {
1093 }
1094
1095 LOTRepeaterItem::LOTRepeaterItem(LOTRepeaterData *data):mData(data)
1096 {
1097
1098 }
1099
1100 void LOTRepeaterItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
1101 {
1102
1103 }
1104
1105 void LOTRepeaterItem::renderList(std::vector<VDrawable *> &list)
1106 {
1107
1108 }
1109