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