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