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