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