lottie: Added TimeStreatch feature to lottie.
[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) *
303                mParentLayer->matrix(frameNo);
304     else
305         return mLayerData->mTransform->matrix(frameNo);
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()), combinedMatrix(), combinedAlpha());
403     }
404 }
405
406 void LOTCompLayerItem::renderList(std::vector<VDrawable *> &list)
407 {
408     if (!visible()) return;
409
410     for (const auto &layer : mLayers) {
411         layer->renderList(list);
412     }
413 }
414
415 LOTSolidLayerItem::LOTSolidLayerItem(LOTLayerData *layerData)
416     : LOTLayerItem(layerData)
417 {
418 }
419
420 void LOTSolidLayerItem::updateContent()
421 {
422     if (!mRenderNode) {
423         mRenderNode = std::make_unique<LOTDrawable>();
424         mRenderNode->mType = VDrawable::Type::Fill;
425         mRenderNode->mFlag |= VDrawable::DirtyState::All;
426     }
427
428     if (flag() & DirtyFlagBit::Matrix) {
429         VPath path;
430         path.addRect(
431             VRectF(0, 0, mLayerData->solidWidth(), mLayerData->solidHeight()));
432         path.transform(combinedMatrix());
433         mRenderNode->mFlag |= VDrawable::DirtyState::Path;
434         mRenderNode->mPath = path;
435     }
436     if (flag() & DirtyFlagBit::Alpha) {
437         LottieColor color = mLayerData->solidColor();
438         VBrush      brush(color.toColor(combinedAlpha()));
439         mRenderNode->setBrush(brush);
440         mRenderNode->mFlag |= VDrawable::DirtyState::Brush;
441     }
442 }
443
444 void LOTSolidLayerItem::renderList(std::vector<VDrawable *> &list)
445 {
446     if (!visible()) return;
447
448     list.push_back(mRenderNode.get());
449 }
450
451 LOTNullLayerItem::LOTNullLayerItem(LOTLayerData *layerData)
452     : LOTLayerItem(layerData)
453 {
454 }
455 void LOTNullLayerItem::updateContent() {}
456
457 LOTShapeLayerItem::LOTShapeLayerItem(LOTLayerData *layerData)
458     : LOTLayerItem(layerData)
459 {
460     mRoot = std::make_unique<LOTContentGroupItem>(nullptr);
461     mRoot->addChildren(layerData);
462
463     std::vector<LOTPathDataItem *> list;
464     mRoot->processPaintItems(list);
465
466     if (layerData->hasPathOperator()) {
467         list.clear();
468         mRoot->processTrimItems(list);
469     }
470 }
471
472 std::unique_ptr<LOTContentItem>
473 LOTShapeLayerItem::createContentItem(LOTData *contentData)
474 {
475     switch (contentData->type()) {
476     case LOTData::Type::ShapeGroup: {
477         return std::make_unique<LOTContentGroupItem>(
478             static_cast<LOTShapeGroupData *>(contentData));
479         break;
480     }
481     case LOTData::Type::Rect: {
482         return std::make_unique<LOTRectItem>(static_cast<LOTRectData *>(contentData));
483         break;
484     }
485     case LOTData::Type::Ellipse: {
486         return std::make_unique<LOTEllipseItem>(static_cast<LOTEllipseData *>(contentData));
487         break;
488     }
489     case LOTData::Type::Shape: {
490         return std::make_unique<LOTShapeItem>(static_cast<LOTShapeData *>(contentData));
491         break;
492     }
493     case LOTData::Type::Polystar: {
494         return std::make_unique<LOTPolystarItem>(static_cast<LOTPolystarData *>(contentData));
495         break;
496     }
497     case LOTData::Type::Fill: {
498         return std::make_unique<LOTFillItem>(static_cast<LOTFillData *>(contentData));
499         break;
500     }
501     case LOTData::Type::GFill: {
502         return std::make_unique<LOTGFillItem>(static_cast<LOTGFillData *>(contentData));
503         break;
504     }
505     case LOTData::Type::Stroke: {
506         return std::make_unique<LOTStrokeItem>(static_cast<LOTStrokeData *>(contentData));
507         break;
508     }
509     case LOTData::Type::GStroke: {
510         return std::make_unique<LOTGStrokeItem>(static_cast<LOTGStrokeData *>(contentData));
511         break;
512     }
513     case LOTData::Type::Repeater: {
514         return std::make_unique<LOTRepeaterItem>(static_cast<LOTRepeaterData *>(contentData));
515         break;
516     }
517     case LOTData::Type::Trim: {
518         return std::make_unique<LOTTrimItem>(static_cast<LOTTrimData *>(contentData));
519         break;
520     }
521     default:
522         return nullptr;
523         break;
524     }
525 }
526
527 void LOTShapeLayerItem::updateContent()
528 {
529     mRoot->update(frameNo(), combinedMatrix(), combinedAlpha(), flag());
530
531     if (mLayerData->hasPathOperator()) {
532         mRoot->applyTrim();
533     }
534 }
535
536 void LOTShapeLayerItem::renderList(std::vector<VDrawable *> &list)
537 {
538     if (!visible()) return;
539     mRoot->renderList(list);
540 }
541
542 LOTContentGroupItem::LOTContentGroupItem(LOTShapeGroupData *data) : mData(data)
543 {
544     addChildren(mData);
545 }
546
547 void LOTContentGroupItem::addChildren(LOTGroupData *data)
548 {
549     if (!data) return;
550
551     for (auto &i : data->mChildren) {
552         auto content = LOTShapeLayerItem::createContentItem(i.get());
553         if (content) {
554             content->setParent(this);
555             mContents.push_back(std::move(content));
556         }
557     }
558
559     // keep the content in back-to-front order.
560     std::reverse(mContents.begin(), mContents.end());
561 }
562
563 void LOTContentGroupItem::update(int frameNo, const VMatrix &parentMatrix,
564                                  float parentAlpha, const DirtyFlag &flag)
565 {
566     VMatrix   m = parentMatrix;
567     float     alpha = parentAlpha;
568     DirtyFlag newFlag = flag;
569
570     if (mData) {
571         // update the matrix and the flag
572         if ((flag & DirtyFlagBit::Matrix) ||
573             !mData->mTransform->staticMatrix()) {
574             newFlag |= DirtyFlagBit::Matrix;
575         }
576         m = mData->mTransform->matrix(frameNo);
577         m *= parentMatrix;
578         alpha *= mData->mTransform->opacity(frameNo);
579
580         if (!vCompare(alpha, parentAlpha)) {
581             newFlag |= DirtyFlagBit::Alpha;
582         }
583     }
584
585     mMatrix = m;
586
587     for (const auto &content : mContents) {
588         content->update(frameNo, m, alpha, newFlag);
589     }
590 }
591
592 void LOTContentGroupItem::applyTrim()
593 {
594     for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
595         auto content = (*i).get();
596         if (auto trim = dynamic_cast<LOTTrimItem *>(content)) {
597             trim->update();
598         } else if (auto group = dynamic_cast<LOTContentGroupItem *>(content)) {
599             group->applyTrim();
600         }
601     }
602 }
603
604 void LOTContentGroupItem::renderList(std::vector<VDrawable *> &list)
605 {
606     for (const auto &content : mContents) {
607         content->renderList(list);
608     }
609 }
610
611 void LOTContentGroupItem::processPaintItems(
612     std::vector<LOTPathDataItem *> &list)
613 {
614     int curOpCount = list.size();
615     for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
616         auto content = (*i).get();
617         if (auto pathNode = dynamic_cast<LOTPathDataItem *>(content)) {
618             // add it to the list
619             list.push_back(pathNode);
620         } else if (auto paintNode = dynamic_cast<LOTPaintDataItem *>(content)) {
621             // the node is a paint data node update the path list of the paint item.
622             paintNode->addPathItems(list, curOpCount);
623         } else if (auto groupNode =
624                        dynamic_cast<LOTContentGroupItem *>(content)) {
625             // update the groups node with current list
626             groupNode->processPaintItems(list);
627         }
628     }
629 }
630
631 void LOTContentGroupItem::processTrimItems(
632     std::vector<LOTPathDataItem *> &list)
633 {
634     int curOpCount = list.size();
635     for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
636         auto content = (*i).get();
637         if (auto pathNode = dynamic_cast<LOTPathDataItem *>(content)) {
638             // add it to the list
639             list.push_back(pathNode);
640         } else if (auto trimNode = dynamic_cast<LOTTrimItem *>(content)) {
641             // the node is a paint data node update the path list of the paint item.
642             trimNode->addPathItems(list, curOpCount);
643         } else if (auto groupNode =
644                        dynamic_cast<LOTContentGroupItem *>(content)) {
645             // update the groups node with current list
646             groupNode->processTrimItems(list);
647         }
648     }
649 }
650
651 void LOTPathDataItem::update(int frameNo, const VMatrix &,
652                              float, const DirtyFlag &flag)
653 {
654     mPathChanged = false;
655
656     // 1. update the local path if needed
657     if (hasChanged(frameNo)) {
658         updatePath(mLocalPath, frameNo);
659         mPathChanged = true;
660         mNeedUpdate = true;
661     }
662
663     mTemp = mLocalPath;
664
665     // 3. compute the final path with parentMatrix
666     if ((flag & DirtyFlagBit::Matrix) || mPathChanged) {
667         mPathChanged = true;
668     }
669 }
670
671 const VPath & LOTPathDataItem::finalPath()
672 {
673     if (mPathChanged || mNeedUpdate) {
674         mFinalPath.clone(mTemp);
675         mFinalPath.transform(static_cast<LOTContentGroupItem *>(parent())->matrix());
676         mNeedUpdate = false;
677     }
678     return mFinalPath;
679 }
680 LOTRectItem::LOTRectItem(LOTRectData *data)
681     : LOTPathDataItem(data->isStatic()), mData(data)
682 {
683 }
684
685 void LOTRectItem::updatePath(VPath& path, int frameNo)
686 {
687     VPointF pos = mData->mPos.value(frameNo);
688     VPointF size = mData->mSize.value(frameNo);
689     float   roundness = mData->mRound.value(frameNo);
690     VRectF  r(pos.x() - size.x() / 2, pos.y() - size.y() / 2, size.x(),
691              size.y());
692
693     path.reset();
694     path.addRoundRect(r, roundness, roundness, mData->direction());
695     updateCache(frameNo, pos, size, roundness);
696 }
697
698 LOTEllipseItem::LOTEllipseItem(LOTEllipseData *data)
699     : LOTPathDataItem(data->isStatic()), mData(data)
700 {
701 }
702
703 void LOTEllipseItem::updatePath(VPath& path, int frameNo)
704 {
705     VPointF pos = mData->mPos.value(frameNo);
706     VPointF size = mData->mSize.value(frameNo);
707     VRectF  r(pos.x() - size.x() / 2, pos.y() - size.y() / 2, size.x(),
708              size.y());
709
710     path.reset();
711     path.addOval(r, mData->direction());
712     updateCache(frameNo, pos, size);
713 }
714
715 LOTShapeItem::LOTShapeItem(LOTShapeData *data)
716     : LOTPathDataItem(data->isStatic()), mData(data)
717 {
718 }
719
720 void LOTShapeItem::updatePath(VPath& path, int frameNo)
721 {
722     mData->mShape.value(frameNo).toPath(path);
723 }
724
725 LOTPolystarItem::LOTPolystarItem(LOTPolystarData *data)
726     : LOTPathDataItem(data->isStatic()), mData(data)
727 {
728 }
729
730 void LOTPolystarItem::updatePath(VPath& path, int frameNo)
731 {
732     VPointF pos = mData->mPos.value(frameNo);
733     float   points = mData->mPointCount.value(frameNo);
734     float   innerRadius = mData->mInnerRadius.value(frameNo);
735     float   outerRadius = mData->mOuterRadius.value(frameNo);
736     float   innerRoundness = mData->mInnerRoundness.value(frameNo);
737     float   outerRoundness = mData->mOuterRoundness.value(frameNo);
738     float   rotation = mData->mRotation.value(frameNo);
739
740     path.reset();
741     VMatrix m;
742
743     if (mData->mType == LOTPolystarData::PolyType::Star) {
744         path.addPolystar(points, innerRadius, outerRadius, innerRoundness,
745                          outerRoundness, 0.0, 0.0, 0.0, mData->direction());
746     } else {
747         path.addPolygon(points, outerRadius, outerRoundness, 0.0, 0.0, 0.0,
748                         mData->direction());
749     }
750
751     m.translate(pos.x(), pos.y()).rotate(rotation);
752     m.rotate(rotation);
753     path.transform(m);
754     updateCache(frameNo, pos, points, innerRadius, outerRadius,
755                 innerRoundness, outerRoundness, rotation);
756 }
757
758 /*
759  * PaintData Node handling
760  *
761  */
762 LOTPaintDataItem::LOTPaintDataItem(bool staticContent):mDrawable(std::make_unique<LOTDrawable>()),
763                                                        mStaticContent(staticContent){}
764
765 void LOTPaintDataItem::update(int frameNo, const VMatrix &parentMatrix,
766                               float parentAlpha, const DirtyFlag &flag)
767 {
768     mRenderNodeUpdate = true;
769     mParentAlpha = parentAlpha;
770     mFlag = flag;
771     mFrameNo = frameNo;
772
773     updateContent(frameNo);
774 }
775
776 void LOTPaintDataItem::updateRenderNode()
777 {
778     bool dirty = false;
779     for (auto &i : mPathItems) {
780         if (i->dirty()) {
781             dirty = true;
782             break;
783         }
784     }
785
786     if (dirty) {
787         mPath.reset();
788
789         for (auto &i : mPathItems) {
790             mPath.addPath(i->finalPath());
791         }
792         mDrawable->setPath(mPath);
793     } else {
794         if (mDrawable->mFlag & VDrawable::DirtyState::Path)
795             mDrawable->mPath = mPath;
796     }
797 }
798
799 void LOTPaintDataItem::renderList(std::vector<VDrawable *> &list)
800 {
801     if (mRenderNodeUpdate) {
802         updateRenderNode();
803         LOTPaintDataItem::updateRenderNode();
804         mRenderNodeUpdate = false;
805     }
806     list.push_back(mDrawable.get());
807 }
808
809
810 void LOTPaintDataItem::addPathItems(std::vector<LOTPathDataItem *> &list, int startOffset)
811 {
812     std::copy(list.begin() + startOffset, list.end(), back_inserter(mPathItems));
813 }
814
815
816 LOTFillItem::LOTFillItem(LOTFillData *data)
817     : LOTPaintDataItem(data->isStatic()), mData(data)
818 {
819 }
820
821 void LOTFillItem::updateContent(int frameNo)
822 {
823     LottieColor c = mData->mColor.value(frameNo);
824     float       opacity = mData->opacity(frameNo);
825     mColor = c.toColor(opacity);
826     mFillRule = mData->fillRule();
827 }
828
829 void LOTFillItem::updateRenderNode()
830 {
831     VColor color = mColor;
832
833     color.setAlpha(color.a * parentAlpha());
834     VBrush brush(color);
835     mDrawable->setBrush(brush);
836     mDrawable->setFillRule(mFillRule);
837 }
838
839 LOTGFillItem::LOTGFillItem(LOTGFillData *data)
840     : LOTPaintDataItem(data->isStatic()), mData(data)
841 {
842 }
843
844 void LOTGFillItem::updateContent(int frameNo)
845 {
846     mData->update(mGradient, frameNo);
847     mGradient->mMatrix = static_cast<LOTContentGroupItem *>(parent())->matrix();
848     mFillRule = mData->fillRule();
849 }
850
851 void LOTGFillItem::updateRenderNode()
852 {
853     mDrawable->setBrush(VBrush(mGradient.get()));
854     mDrawable->setFillRule(mFillRule);
855 }
856
857 LOTStrokeItem::LOTStrokeItem(LOTStrokeData *data)
858     : LOTPaintDataItem(data->isStatic()), mData(data)
859 {
860     mDashArraySize = 0;
861 }
862
863 void LOTStrokeItem::updateContent(int frameNo)
864 {
865     LottieColor c = mData->mColor.value(frameNo);
866     float       opacity = mData->opacity(frameNo);
867     mColor = c.toColor(opacity);
868     mCap = mData->capStyle();
869     mJoin = mData->joinStyle();
870     mMiterLimit = mData->meterLimit();
871     mWidth = mData->width(frameNo);
872     if (mData->hasDashInfo()) {
873         mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
874     }
875 }
876
877 static float getScale(const VMatrix &matrix)
878 {
879     constexpr float SQRT_2 = 1.41421;
880     VPointF         p1(0, 0);
881     VPointF         p2(SQRT_2, SQRT_2);
882     p1 = matrix.map(p1);
883     p2 = matrix.map(p2);
884     VPointF final = p2 - p1;
885
886     return std::sqrt(final.x() * final.x() + final.y() * final.y()) / 2.0;
887 }
888
889 void LOTStrokeItem::updateRenderNode()
890 {
891     VColor color = mColor;
892
893     color.setAlpha(color.a * parentAlpha());
894     VBrush brush(color);
895     mDrawable->setBrush(brush);
896     float scale = getScale(static_cast<LOTContentGroupItem *>(parent())->matrix());
897     mDrawable->setStrokeInfo(mCap, mJoin, mMiterLimit,
898                             mWidth * scale);
899     if (mDashArraySize) {
900         for (int i = 0 ; i < mDashArraySize ; i++)
901             mDashArray[i] *= scale;
902         mDrawable->setDashInfo(mDashArray, mDashArraySize);
903     }
904 }
905
906 LOTGStrokeItem::LOTGStrokeItem(LOTGStrokeData *data)
907     : LOTPaintDataItem(data->isStatic()), mData(data)
908 {
909     mDashArraySize = 0;
910 }
911
912 void LOTGStrokeItem::updateContent(int frameNo)
913 {
914     mData->update(mGradient, frameNo);
915     mGradient->mMatrix = static_cast<LOTContentGroupItem *>(parent())->matrix();
916     mCap = mData->capStyle();
917     mJoin = mData->joinStyle();
918     mMiterLimit = mData->meterLimit();
919     mWidth = mData->width(frameNo);
920     if (mData->hasDashInfo()) {
921         mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
922     }
923 }
924
925 void LOTGStrokeItem::updateRenderNode()
926 {
927     float scale = getScale(mGradient->mMatrix);
928     mDrawable->setBrush(VBrush(mGradient.get()));
929     mDrawable->setStrokeInfo(mCap, mJoin, mMiterLimit,
930                             mWidth * scale);
931     if (mDashArraySize) {
932         for (int i = 0 ; i < mDashArraySize ; i++)
933             mDashArray[i] *= scale;
934         mDrawable->setDashInfo(mDashArray, mDashArraySize);
935     }
936 }
937
938 LOTTrimItem::LOTTrimItem(LOTTrimData *data) : mData(data) {}
939
940 void LOTTrimItem::update(int frameNo, const VMatrix &/*parentMatrix*/,
941                          float /*parentAlpha*/, const DirtyFlag &/*flag*/)
942 {
943     mDirty = false;
944
945     if (mCache.mFrameNo == frameNo) return;
946
947     float   start = mData->start(frameNo);
948     float   end = mData->end(frameNo);
949     float   offset = mData->offset(frameNo);
950
951     if (!(vCompare(mCache.mStart, start) && vCompare(mCache.mEnd, end) &&
952           vCompare(mCache.mOffset, offset))) {
953         mDirty = true;
954         mCache.mStart = start;
955         mCache.mEnd = end;
956         mCache.mOffset = offset;
957     }
958     mCache.mFrameNo = frameNo;
959 }
960
961 void LOTTrimItem::update()
962 {
963     // when both path and trim are not dirty
964     if (!(mDirty || pathDirty())) return;
965
966     //@TODO take the offset and trim type into account.
967     for (auto &i : mPathItems) {
968         VPathMesure pm;
969         pm.setStart(mCache.mStart);
970         pm.setEnd(mCache.mEnd);
971         pm.setOffset(mCache.mOffset);
972         i->updatePath(pm.trim(i->localPath()));
973     }
974 }
975
976
977 void LOTTrimItem::addPathItems(std::vector<LOTPathDataItem *> &list, int startOffset)
978 {
979     std::copy(list.begin() + startOffset, list.end(), back_inserter(mPathItems));
980 }
981
982
983 LOTRepeaterItem::LOTRepeaterItem(LOTRepeaterData *data) : mData(data) {}
984
985 void LOTRepeaterItem::update(int /*frameNo*/, const VMatrix &/*parentMatrix*/,
986                              float /*parentAlpha*/, const DirtyFlag &/*flag*/)
987 {
988 }
989
990 void LOTRepeaterItem::renderList(std::vector<VDrawable *> &/*list*/) {}
991
992 void LOTDrawable::sync()
993 {
994     if (!mCNode) mCNode = std::make_unique<LOTNode>();
995
996     mCNode->mFlag = ChangeFlagNone;
997     if (mFlag & DirtyState::None) return;
998
999     if (mFlag & DirtyState::Path) {
1000         const std::vector<VPath::Element> &elm = mPath.elements();
1001         const std::vector<VPointF> &       pts = mPath.points();
1002         const float *ptPtr = reinterpret_cast<const float *>(pts.data());
1003         const char * elmPtr = reinterpret_cast<const char *>(elm.data());
1004         mCNode->mPath.elmPtr = elmPtr;
1005         mCNode->mPath.elmCount = elm.size();
1006         mCNode->mPath.ptPtr = ptPtr;
1007         mCNode->mPath.ptCount = 2 * pts.size();
1008         mCNode->mFlag |= ChangeFlagPath;
1009     }
1010
1011     if (mStroke.enable) {
1012         mCNode->mStroke.width = mStroke.width;
1013         mCNode->mStroke.meterLimit = mStroke.meterLimit;
1014         mCNode->mStroke.enable = 1;
1015
1016         switch (mFillRule) {
1017         case FillRule::EvenOdd:
1018             mCNode->mFillRule = LOTFillRule::FillEvenOdd;
1019             break;
1020         default:
1021             mCNode->mFillRule = LOTFillRule::FillWinding;
1022             break;
1023         }
1024
1025         switch (mStroke.cap) {
1026         case CapStyle::Flat:
1027             mCNode->mStroke.cap = LOTCapStyle::CapFlat;
1028             break;
1029         case CapStyle::Square:
1030             mCNode->mStroke.cap = LOTCapStyle::CapSquare;
1031             break;
1032         case CapStyle::Round:
1033             mCNode->mStroke.cap = LOTCapStyle::CapRound;
1034             break;
1035         default:
1036             mCNode->mStroke.cap = LOTCapStyle::CapFlat;
1037             break;
1038         }
1039
1040         switch (mStroke.join) {
1041         case JoinStyle::Miter:
1042             mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
1043             break;
1044         case JoinStyle::Bevel:
1045             mCNode->mStroke.join = LOTJoinStyle::JoinBevel;
1046             break;
1047         case JoinStyle::Round:
1048             mCNode->mStroke.join = LOTJoinStyle::JoinRound;
1049             break;
1050         default:
1051             mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
1052             break;
1053         }
1054
1055         mCNode->mStroke.dashArray = mStroke.mDash.data();
1056         mCNode->mStroke.dashArraySize = mStroke.mDash.size();
1057
1058     } else {
1059         mCNode->mStroke.enable = 0;
1060     }
1061
1062     switch (mBrush.type()) {
1063     case VBrush::Type::Solid:
1064         mCNode->mType = LOTBrushType::BrushSolid;
1065         mCNode->mColor.r = mBrush.mColor.r;
1066         mCNode->mColor.g = mBrush.mColor.g;
1067         mCNode->mColor.b = mBrush.mColor.b;
1068         mCNode->mColor.a = mBrush.mColor.a;
1069         break;
1070     case VBrush::Type::LinearGradient:
1071         mCNode->mType = LOTBrushType::BrushGradient;
1072         mCNode->mGradient.type = LOTGradientType::GradientLinear;
1073         mCNode->mGradient.start.x = mBrush.mGradient->linear.x1;
1074         mCNode->mGradient.start.y = mBrush.mGradient->linear.y1;
1075         mCNode->mGradient.end.x = mBrush.mGradient->linear.x2;
1076         mCNode->mGradient.end.y = mBrush.mGradient->linear.y2;
1077         break;
1078     case VBrush::Type::RadialGradient:
1079         mCNode->mType = LOTBrushType::BrushGradient;
1080         mCNode->mGradient.type = LOTGradientType::GradientRadial;
1081         mCNode->mGradient.center.x = mBrush.mGradient->radial.cx;
1082         mCNode->mGradient.center.y = mBrush.mGradient->radial.cy;
1083         mCNode->mGradient.focal.x = mBrush.mGradient->radial.fx;
1084         mCNode->mGradient.focal.y = mBrush.mGradient->radial.fy;
1085         mCNode->mGradient.cradius = mBrush.mGradient->radial.cradius;
1086         mCNode->mGradient.fradius = mBrush.mGradient->radial.fradius;
1087         break;
1088     default:
1089         break;
1090     }
1091 }