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