lottie/example: Added image_test.json(resource with image resource )
[platform/core/uifw/lottie-player.git] / src / lottie / lottieitem.cpp
1 /* 
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
3  * 
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  * 
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  * 
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "lottieitem.h"
20 #include <cmath>
21 #include <algorithm>
22 #include "vbitmap.h"
23 #include "vdasher.h"
24 #include "vpainter.h"
25 #include "vraster.h"
26 #include "vimageloader.h"
27
28 /* Lottie Layer Rules
29  * 1. time stretch is pre calculated and applied to all the properties of the
30  * lottilayer model and all its children
31  * 2. The frame property could be reversed using,time-reverse layer property in
32  * AE. which means (start frame > endFrame) 3.
33  */
34
35 LOTCompItem::LOTCompItem(LOTModel *model)
36     : mRootModel(model), mUpdateViewBox(false), mCurFrameNo(-1)
37 {
38     mCompData = model->mRoot.get();
39     mRootLayer = createLayerItem(mCompData->mRootLayer.get());
40     mRootLayer->updateStaticProperty();
41     mViewSize = mCompData->size();
42 }
43
44 std::unique_ptr<LOTLayerItem>
45 LOTCompItem::createLayerItem(LOTLayerData *layerData)
46 {
47     switch (layerData->mLayerType) {
48     case LayerType::Precomp: {
49         return std::make_unique<LOTCompLayerItem>(layerData);
50         break;
51     }
52     case LayerType::Solid: {
53         return std::make_unique<LOTSolidLayerItem>(layerData);
54         break;
55     }
56     case LayerType::Shape: {
57         return std::make_unique<LOTShapeLayerItem>(layerData);
58         break;
59     }
60     case LayerType::Null: {
61         return std::make_unique<LOTNullLayerItem>(layerData);
62         break;
63     }
64     case LayerType::Image: {
65         return std::make_unique<LOTImageLayerItem>(layerData);
66         break;
67     }
68     default:
69         return nullptr;
70         break;
71     }
72 }
73
74 void LOTCompItem::resize(const VSize &size)
75 {
76     if (mViewSize == size) return;
77     mViewSize = size;
78     mUpdateViewBox = true;
79 }
80
81 VSize LOTCompItem::size() const
82 {
83     return mViewSize;
84 }
85
86 bool LOTCompItem::update(int frameNo)
87 {
88     // check if cached frame is same as requested frame.
89     if (!mUpdateViewBox && (mCurFrameNo == frameNo)) return false;
90
91     /*
92      * if viewbox dosen't scale exactly to the viewport
93      * we scale the viewbox keeping AspectRatioPreserved and then align the
94      * viewbox to the viewport using AlignCenter rule.
95      */
96     VSize viewPort = mViewSize;
97     VSize viewBox = mCompData->size();
98
99     float sx = float(viewPort.width()) / viewBox.width();
100     float sy = float(viewPort.height()) / viewBox.height();
101     float scale = fmin(sx, sy);
102     float tx = (viewPort.width() - viewBox.width() * scale) * 0.5;
103     float ty = (viewPort.height() - viewBox.height() * scale) * 0.5;
104
105     VMatrix m;
106     m.translate(tx, ty).scale(scale, scale);
107     mRootLayer->update(frameNo, m, 1.0);
108
109     mCurFrameNo = frameNo;
110     mUpdateViewBox = false;
111     return true;
112 }
113
114 void LOTCompItem::buildRenderTree()
115 {
116     mRootLayer->buildLayerNode();
117 }
118
119 const LOTLayerNode * LOTCompItem::renderTree() const
120 {
121     return mRootLayer->layerNode();
122 }
123
124 bool LOTCompItem::render(const lottie::Surface &surface)
125 {
126     VBitmap bitmap(reinterpret_cast<uchar *>(surface.buffer()),
127                    surface.width(), surface.height(),
128                    surface.bytesPerLine(), VBitmap::Format::ARGB32_Premultiplied);
129
130     /* schedule all preprocess task for this frame at once.
131      */
132     mDrawableList.clear();
133     mRootLayer->renderList(mDrawableList);
134     VRect clip(0, 0, surface.width(), surface.height());
135     for (auto &e : mDrawableList) {
136         e->preprocess(clip);
137     }
138
139     VPainter painter(&bitmap);
140     mRootLayer->render(&painter, {}, {});
141
142     return true;
143 }
144
145 void LOTMaskItem::update(int frameNo, const VMatrix &parentMatrix,
146                          float parentAlpha, const DirtyFlag &flag)
147 {
148     if (flag.testFlag(DirtyFlagBit::None) && mData->isStatic()) return;
149
150     if (mData->mShape.isStatic()) {
151         if (mLocalPath.empty()) {
152             mData->mShape.value(frameNo).toPath(mLocalPath);
153         }
154     } else {
155         mData->mShape.value(frameNo).toPath(mLocalPath);
156     }
157     float opacity = mData->opacity(frameNo);
158     opacity = opacity * parentAlpha;
159     mCombinedAlpha = opacity;
160
161     mFinalPath.clone(mLocalPath);
162     mFinalPath.transform(parentMatrix);
163
164     VPath tmp = mFinalPath;
165
166     if (!mRleFuture) mRleFuture = std::make_shared<VSharedState<VRle>>();
167
168     mRleFuture->reuse();
169     VRaster::generateFillInfo(mRleFuture, std::move(tmp), std::move(mRle));
170     mRle = VRle();
171 }
172
173 VRle LOTMaskItem::rle()
174 {
175     if (mRleFuture && mRleFuture->valid()) {
176         mRle = mRleFuture->get();
177         if (!vCompare(mCombinedAlpha, 1.0f))
178             mRle *= (mCombinedAlpha * 255);
179         if (mData->mInv) mRle.invert();
180     }
181     return mRle;
182 }
183
184 void LOTLayerItem::buildLayerNode()
185 {
186     if (!mLayerCNode) {
187         mLayerCNode = std::make_unique<LOTLayerNode>();
188         mLayerCNode->mMaskList.ptr = nullptr;
189         mLayerCNode->mMaskList.size = 0;
190         mLayerCNode->mLayerList.ptr = nullptr;
191         mLayerCNode->mLayerList.size = 0;
192         mLayerCNode->mNodeList.ptr = nullptr;
193         mLayerCNode->mNodeList.size = 0;
194         mLayerCNode->mMatte = MatteNone;
195         mLayerCNode->mVisible = 0;
196         mLayerCNode->mClipPath.ptPtr = nullptr;
197         mLayerCNode->mClipPath.elmPtr = nullptr;
198         mLayerCNode->mClipPath.ptCount = 0;
199         mLayerCNode->mClipPath.elmCount = 0;
200     }
201     mLayerCNode->mVisible = visible();
202     // update matte
203     if (hasMatte()) {
204         switch (mLayerData->mMatteType) {
205         case MatteType::Alpha:
206             mLayerCNode->mMatte = MatteAlpha;
207             break;
208         case MatteType::AlphaInv:
209             mLayerCNode->mMatte = MatteAlphaInv;
210             break;
211         case MatteType::Luma:
212             mLayerCNode->mMatte = MatteLuma;
213             break;
214         case MatteType::LumaInv:
215             mLayerCNode->mMatte = MatteLumaInv;
216             break;
217         default:
218             mLayerCNode->mMatte = MatteNone;
219             break;
220         }
221     }
222     if (mLayerMask) {
223         mMasksCNode.clear();
224         for (const auto &mask : mLayerMask->mMasks) {
225             LOTMask cNode;
226             const std::vector<VPath::Element> &elm = mask.mFinalPath.elements();
227             const std::vector<VPointF> &       pts = mask.mFinalPath.points();
228             const float *ptPtr = reinterpret_cast<const float *>(pts.data());
229             const char * elmPtr = reinterpret_cast<const char *>(elm.data());
230             cNode.mPath.ptPtr = ptPtr;
231             cNode.mPath.ptCount = pts.size();
232             cNode.mPath.elmPtr = elmPtr;
233             cNode.mPath.elmCount = elm.size();
234             switch (mask.maskMode()) {
235             case LOTMaskData::Mode::Add:
236                 cNode.mMode = MaskModeAdd;
237                 break;
238             case LOTMaskData::Mode::Substarct:
239                 cNode.mMode = MaskModeSubstract;
240                 break;
241             case LOTMaskData::Mode::Intersect:
242                 cNode.mMode = MaskModeIntersect;
243                 break;
244             case LOTMaskData::Mode::Difference:
245                 cNode.mMode = MaskModeDifference;
246                 break;
247             default:
248                 cNode.mMode = MaskModeAdd;
249                 break;
250             }
251             mMasksCNode.push_back(std::move(cNode));
252         }
253         mLayerCNode->mMaskList.ptr = mMasksCNode.data();
254         mLayerCNode->mMaskList.size = mMasksCNode.size();
255     }
256 }
257
258 void LOTLayerItem::render(VPainter *painter, const VRle &inheritMask, const VRle &matteRle)
259 {
260     mDrawableList.clear();
261     renderList(mDrawableList);
262
263     VRle mask;
264     if (mLayerMask) {
265         mask = mLayerMask->maskRle(painter->clipBoundingRect());
266         if (!inheritMask.empty())
267             mask = mask & inheritMask;
268         // if resulting mask is empty then return.
269         if (mask.empty())
270             return;
271     } else {
272         mask = inheritMask;
273     }
274
275     for (auto &i : mDrawableList) {
276         painter->setBrush(i->mBrush);
277         VRle rle = i->rle();
278         if (matteRle.empty()) {
279             if (mask.empty()) {
280                 // no mask no matte
281                 painter->drawRle(VPoint(), rle);
282             } else {
283                 // only mask
284                 painter->drawRle(rle, mask);
285             }
286
287         } else {
288             if (!mask.empty()) rle = rle & mask;
289
290             if (rle.empty()) continue;
291             if (matteType() == MatteType::AlphaInv) {
292                 rle = rle - matteRle;
293                 painter->drawRle(VPoint(), rle);
294             } else {
295                 // render with matteRle as clip.
296                 painter->drawRle(rle, matteRle);
297             }
298         }
299     }
300 }
301
302 LOTLayerMaskItem::LOTLayerMaskItem(LOTLayerData *layerData)
303 {
304     mMasks.reserve(layerData->mMasks.size());
305
306     for (auto &i : layerData->mMasks) {
307         mMasks.emplace_back(i.get());
308         mStatic &= i->isStatic();
309     }
310 }
311
312 void LOTLayerMaskItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
313 {
314     if (flag.testFlag(DirtyFlagBit::None) && isStatic()) return;
315
316     for (auto &i : mMasks) {
317         i.update(frameNo, parentMatrix, parentAlpha, flag);
318     }
319     mDirty = true;
320 }
321
322 VRle LOTLayerMaskItem::maskRle(const VRect &clipRect)
323 {
324     if (!mDirty) return mRle;
325
326     VRle rle;
327     for (auto &i : mMasks) {
328         switch (i.maskMode()) {
329         case LOTMaskData::Mode::Add: {
330             rle = rle + i.rle();
331             break;
332         }
333         case LOTMaskData::Mode::Substarct: {
334             if (rle.empty() && !clipRect.empty())
335                 rle = VRle::toRle(clipRect);
336             rle = rle - i.rle();
337             break;
338         }
339         case LOTMaskData::Mode::Intersect: {
340             rle = rle & i.rle();
341             break;
342         }
343         case LOTMaskData::Mode::Difference: {
344             rle = rle ^ i.rle();
345             break;
346         }
347         default:
348             break;
349         }
350     }
351
352     mRle = rle;
353     mDirty = false;
354     return mRle;
355 }
356
357
358 LOTLayerItem::LOTLayerItem(LOTLayerData *layerData): mLayerData(layerData)
359 {
360     if (mLayerData->mHasMask)
361         mLayerMask = std::make_unique<LOTLayerMaskItem>(mLayerData);
362 }
363
364 void LOTLayerItem::updateStaticProperty()
365 {
366     if (mParentLayer) mParentLayer->updateStaticProperty();
367
368     mStatic = mLayerData->isStatic();
369     mStatic = mParentLayer ? (mStatic & mParentLayer->isStatic()) : mStatic;
370     mStatic = mPrecompLayer ? (mStatic & mPrecompLayer->isStatic()) : mStatic;
371 }
372
373 void LOTLayerItem::update(int frameNumber, const VMatrix &parentMatrix,
374                           float parentAlpha)
375 {
376     mFrameNo = frameNumber;
377     // 1. check if the layer is part of the current frame
378     if (!visible()) return;
379
380     // 2. calculate the parent matrix and alpha
381     VMatrix m = matrix(frameNo());
382     m *= parentMatrix;
383     float alpha = parentAlpha * opacity(frameNo());
384
385     // 3. update the dirty flag based on the change
386     if (!mCombinedMatrix.fuzzyCompare(m)) {
387         mDirtyFlag |= DirtyFlagBit::Matrix;
388     }
389     if (!vCompare(mCombinedAlpha, alpha)) {
390         mDirtyFlag |= DirtyFlagBit::Alpha;
391     }
392     mCombinedMatrix = m;
393     mCombinedAlpha = alpha;
394
395     // 4. update the mask
396     if (mLayerMask) {
397         mLayerMask->update(frameNo(), m, alpha, mDirtyFlag);
398     }
399
400     // 5. if no parent property change and layer is static then nothing to do.
401     if (flag().testFlag(DirtyFlagBit::None) && isStatic()) return;
402
403     // 6. update the content of the layer
404     updateContent();
405
406     // 7. reset the dirty flag
407     mDirtyFlag = DirtyFlagBit::None;
408 }
409
410 float LOTLayerItem::opacity(int frameNo) const
411 {
412     return mLayerData->mTransform->opacity(frameNo);
413 }
414
415 VMatrix LOTLayerItem::matrix(int frameNo) const
416 {
417     if (mParentLayer)
418         return mLayerData->mTransform->matrix(frameNo, mLayerData->autoOrient()) *
419                mParentLayer->matrix(frameNo);
420     else
421         return mLayerData->mTransform->matrix(frameNo, mLayerData->autoOrient());
422 }
423
424 bool LOTLayerItem::visible() const
425 {
426     if (frameNo() >= mLayerData->inFrame() &&
427         frameNo() < mLayerData->outFrame())
428         return true;
429     else
430         return false;
431 }
432
433 LOTCompLayerItem::LOTCompLayerItem(LOTLayerData *layerModel)
434     : LOTLayerItem(layerModel)
435 {
436     // 1. create layer item
437     for (auto &i : mLayerData->mChildren) {
438         LOTLayerData *layerModel = static_cast<LOTLayerData *>(i.get());
439         auto layerItem = LOTCompItem::createLayerItem(layerModel);
440         if (layerItem) mLayers.push_back(std::move(layerItem));
441     }
442
443     // 2. update parent layer
444     for (const auto &layer : mLayers) {
445         int id = layer->parentId();
446         if (id >= 0) {
447             auto search = std::find_if(mLayers.begin(), mLayers.end(),
448                             [id](const auto& val){ return val->id() == id;});
449             if (search != mLayers.end()) layer->setParentLayer((*search).get());
450         }
451         // update the precomp layer if its not the root layer.
452         if (!layerModel->root()) layer->setPrecompLayer(this);
453     }
454
455     // 3. keep the layer in back-to-front order.
456     // as lottie model keeps the data in front-toback-order.
457     std::reverse(mLayers.begin(), mLayers.end());
458
459     // 4. check if its a nested composition
460     if (!layerModel->layerSize().empty()) {
461         mClipper = std::make_unique<LOTClipperItem>(layerModel->layerSize());
462     }
463 }
464
465 void LOTCompLayerItem::updateStaticProperty()
466 {
467     LOTLayerItem::updateStaticProperty();
468
469     for (const auto &layer : mLayers) {
470         layer->updateStaticProperty();
471     }
472 }
473
474 void LOTCompLayerItem::buildLayerNode()
475 {
476     LOTLayerItem::buildLayerNode();
477     if (mClipper) {
478         const std::vector<VPath::Element> &elm = mClipper->mPath.elements();
479         const std::vector<VPointF> &       pts = mClipper->mPath.points();
480         const float *ptPtr = reinterpret_cast<const float *>(pts.data());
481         const char * elmPtr = reinterpret_cast<const char *>(elm.data());
482         layerNode()->mClipPath.ptPtr = ptPtr;
483         layerNode()->mClipPath.elmPtr = elmPtr;
484         layerNode()->mClipPath.ptCount = 2 * pts.size();
485         layerNode()->mClipPath.elmCount = elm.size();
486     }
487     if (mLayers.size() != mLayersCNode.size()) {
488         for (const auto &layer : mLayers) {
489             layer->buildLayerNode();
490             mLayersCNode.push_back(layer->layerNode());
491         }
492         layerNode()->mLayerList.ptr = mLayersCNode.data();
493         layerNode()->mLayerList.size = mLayersCNode.size();
494     } else {
495         for (const auto &layer : mLayers) {
496             layer->buildLayerNode();
497         }
498     }
499 }
500
501 void LOTCompLayerItem::render(VPainter *painter, const VRle &inheritMask, const VRle &matteRle)
502 {
503     VRle mask;
504     if (mLayerMask) {
505         mask = mLayerMask->maskRle(painter->clipBoundingRect());
506         if (!inheritMask.empty())
507             mask = mask & inheritMask;
508         // if resulting mask is empty then return.
509         if (mask.empty())
510             return;
511     } else {
512         mask = inheritMask;
513     }
514
515     if (mClipper) {
516         if (mask.empty()) {
517             mask = mClipper->rle();
518         } else {
519             mask = mClipper->rle() & mask;
520         }
521     }
522
523     LOTLayerItem *matteLayer = nullptr;
524     for (const auto &layer : mLayers) {
525         if (layer->hasMatte()) {
526             if (matteLayer) {
527                 vWarning << "two consecutive layer has matter : not supported";
528             }
529             matteLayer = layer.get();
530             continue;
531         }
532
533         if (layer->visible()) {
534             if (matteLayer) {
535                 if (matteLayer->visible())
536                     renderMatteLayer(painter, mask, matteRle, matteLayer, layer.get());
537             } else {
538                 layer->render(painter, mask, matteRle);
539             }
540         }
541
542         matteLayer = nullptr;
543     }
544 }
545
546 void LOTCompLayerItem::renderMatteLayer(VPainter *painter,
547                                         const VRle &mask,
548                                         const VRle &matteRle,
549                                         LOTLayerItem *layer,
550                                         LOTLayerItem *src)
551 {
552     VSize size = painter->clipBoundingRect().size();
553     // Decide if we can use fast matte.
554     // 1. draw src layer to matte buffer
555     VPainter srcPainter;
556     VBitmap srcBitmap(size.width(), size.height(), VBitmap::Format::ARGB32_Premultiplied);
557     srcPainter.begin(&srcBitmap);
558     src->render(&srcPainter, mask, matteRle);
559     srcPainter.end();
560
561     // 2. draw layer to layer buffer
562     VPainter layerPainter;
563     VBitmap layerBitmap(size.width(), size.height(), VBitmap::Format::ARGB32_Premultiplied);
564     layerPainter.begin(&layerBitmap);
565     layer->render(&layerPainter, mask, matteRle);
566
567     // 2.1update composition mode
568     switch (layer->matteType()) {
569     case MatteType::Alpha:
570     case MatteType::Luma: {
571         layerPainter.setCompositionMode(VPainter::CompositionMode::CompModeDestIn);
572         break;
573     }
574     case MatteType::AlphaInv:
575     case MatteType::LumaInv: {
576         layerPainter.setCompositionMode(VPainter::CompositionMode::CompModeDestOut);
577         break;
578     }
579     default:
580         break;
581     }
582
583     //2.2 update srcBuffer if the matte is luma type
584     if (layer->matteType() == MatteType::Luma ||
585         layer->matteType() == MatteType::LumaInv) {
586         srcBitmap.updateLuma();
587     }
588
589     // 2.3 draw src buffer as mask
590     layerPainter.drawBitmap(VPoint(), srcBitmap);
591     layerPainter.end();
592     // 3. draw the result buffer into painter
593     painter->drawBitmap(VPoint(), layerBitmap);
594 }
595
596 void LOTClipperItem::update(const VMatrix &matrix)
597 {
598     mPath.reset();
599     mPath.addRect(VRectF(0,0, mSize.width(), mSize.height()));
600     mPath.transform(matrix);
601
602     VPath tmp = mPath;
603
604     if (!mRleFuture) mRleFuture = std::make_shared<VSharedState<VRle>>();
605
606     mRleFuture->reuse();
607     VRaster::generateFillInfo(mRleFuture, std::move(tmp), std::move(mRle));
608     mRle = VRle();
609 }
610
611 VRle LOTClipperItem::rle()
612 {
613     if (mRleFuture && mRleFuture->valid()) {
614         mRle = mRleFuture->get();
615     }
616     return mRle;
617 }
618
619 void LOTCompLayerItem::updateContent()
620 {
621     if (mClipper && flag().testFlag(DirtyFlagBit::Matrix)) {
622         mClipper->update(combinedMatrix());
623     }
624     int mappedFrame = mLayerData->timeRemap(frameNo());
625     for (const auto &layer : mLayers) {
626         layer->update( mappedFrame, combinedMatrix(), combinedAlpha());
627     }
628 }
629
630 void LOTCompLayerItem::renderList(std::vector<VDrawable *> &list)
631 {
632     if (!visible()) return;
633
634     for (const auto &layer : mLayers) {
635         layer->renderList(list);
636     }
637 }
638
639 LOTSolidLayerItem::LOTSolidLayerItem(LOTLayerData *layerData)
640     : LOTLayerItem(layerData)
641 {
642 }
643
644 void LOTSolidLayerItem::updateContent()
645 {
646     if (!mRenderNode) {
647         mRenderNode = std::make_unique<LOTDrawable>();
648         mRenderNode->mType = VDrawable::Type::Fill;
649         mRenderNode->mFlag |= VDrawable::DirtyState::All;
650     }
651
652     if (flag() & DirtyFlagBit::Matrix) {
653         VPath path;
654         path.addRect(
655             VRectF(0, 0, mLayerData->solidWidth(), mLayerData->solidHeight()));
656         path.transform(combinedMatrix());
657         mRenderNode->mFlag |= VDrawable::DirtyState::Path;
658         mRenderNode->mPath = path;
659     }
660     if (flag() & DirtyFlagBit::Alpha) {
661         LottieColor color = mLayerData->solidColor();
662         VBrush      brush(color.toColor(combinedAlpha()));
663         mRenderNode->setBrush(brush);
664         mRenderNode->mFlag |= VDrawable::DirtyState::Brush;
665     }
666 }
667
668 void LOTSolidLayerItem::buildLayerNode()
669 {
670     LOTLayerItem::buildLayerNode();
671
672     mDrawableList.clear();
673     renderList(mDrawableList);
674
675     mCNodeList.clear();
676     for (auto &i : mDrawableList) {
677         LOTDrawable *lotDrawable = static_cast<LOTDrawable *>(i);
678         lotDrawable->sync();
679         mCNodeList.push_back(lotDrawable->mCNode.get());
680     }
681     layerNode()->mNodeList.ptr = mCNodeList.data();
682     layerNode()->mNodeList.size = mCNodeList.size();
683 }
684
685 void LOTSolidLayerItem::renderList(std::vector<VDrawable *> &list)
686 {
687     if (!visible()) return;
688
689     list.push_back(mRenderNode.get());
690 }
691
692 LOTImageLayerItem::LOTImageLayerItem(LOTLayerData *layerData)
693     : LOTLayerItem(layerData)
694 {
695 }
696
697 void LOTImageLayerItem::updateContent()
698 {
699     if (!mRenderNode) {
700         mRenderNode = std::make_unique<LOTDrawable>();
701         mRenderNode->mType = VDrawable::Type::Fill;
702         mRenderNode->mFlag |= VDrawable::DirtyState::All;
703         // load image
704         //@TODO find a better way to load
705         // so that can be shared by multiple layers
706         if (!mLayerData->mAsset->mImagePath.empty()) {
707             VBitmap img = VImageLoader::instance().load(mLayerData->mAsset->mImagePath.c_str());
708             VBrush brush(img);
709             mRenderNode->setBrush(brush);
710         }
711     }
712
713     if (flag() & DirtyFlagBit::Matrix) {
714         VPath path;
715         path.addRect(
716             VRectF(0, 0, mLayerData->mAsset->mWidth, mLayerData->mAsset->mHeight));
717         path.transform(combinedMatrix());
718         mRenderNode->mFlag |= VDrawable::DirtyState::Path;
719         mRenderNode->mPath = path;
720         mRenderNode->mBrush.setMatrix(combinedMatrix());
721     }
722
723     if (flag() & DirtyFlagBit::Alpha) {
724         //@TODO handle alpha with the image.
725     }
726 }
727
728 void LOTImageLayerItem::renderList(std::vector<VDrawable *> &list)
729 {
730     if (!visible()) return;
731
732     list.push_back(mRenderNode.get());
733 }
734
735 void LOTImageLayerItem::buildLayerNode()
736 {
737     LOTLayerItem::buildLayerNode();
738
739     mDrawableList.clear();
740     renderList(mDrawableList);
741
742     mCNodeList.clear();
743     for (auto &i : mDrawableList) {
744         LOTDrawable *lotDrawable = static_cast<LOTDrawable *>(i);
745         lotDrawable->sync();
746         mCNodeList.push_back(lotDrawable->mCNode.get());
747     }
748     layerNode()->mNodeList.ptr = mCNodeList.data();
749     layerNode()->mNodeList.size = mCNodeList.size();
750 }
751
752 LOTNullLayerItem::LOTNullLayerItem(LOTLayerData *layerData)
753     : LOTLayerItem(layerData)
754 {
755 }
756 void LOTNullLayerItem::updateContent() {}
757
758 LOTShapeLayerItem::LOTShapeLayerItem(LOTLayerData *layerData)
759     : LOTLayerItem(layerData)
760 {
761     mRoot = std::make_unique<LOTContentGroupItem>(nullptr);
762     mRoot->addChildren(layerData);
763
764     std::vector<LOTPathDataItem *> list;
765     mRoot->processPaintItems(list);
766
767     if (layerData->hasPathOperator()) {
768         list.clear();
769         mRoot->processTrimItems(list);
770     }
771 }
772
773 std::unique_ptr<LOTContentItem>
774 LOTShapeLayerItem::createContentItem(LOTData *contentData)
775 {
776     switch (contentData->type()) {
777     case LOTData::Type::ShapeGroup: {
778         return std::make_unique<LOTContentGroupItem>(
779             static_cast<LOTGroupData *>(contentData));
780         break;
781     }
782     case LOTData::Type::Rect: {
783         return std::make_unique<LOTRectItem>(static_cast<LOTRectData *>(contentData));
784         break;
785     }
786     case LOTData::Type::Ellipse: {
787         return std::make_unique<LOTEllipseItem>(static_cast<LOTEllipseData *>(contentData));
788         break;
789     }
790     case LOTData::Type::Shape: {
791         return std::make_unique<LOTShapeItem>(static_cast<LOTShapeData *>(contentData));
792         break;
793     }
794     case LOTData::Type::Polystar: {
795         return std::make_unique<LOTPolystarItem>(static_cast<LOTPolystarData *>(contentData));
796         break;
797     }
798     case LOTData::Type::Fill: {
799         return std::make_unique<LOTFillItem>(static_cast<LOTFillData *>(contentData));
800         break;
801     }
802     case LOTData::Type::GFill: {
803         return std::make_unique<LOTGFillItem>(static_cast<LOTGFillData *>(contentData));
804         break;
805     }
806     case LOTData::Type::Stroke: {
807         return std::make_unique<LOTStrokeItem>(static_cast<LOTStrokeData *>(contentData));
808         break;
809     }
810     case LOTData::Type::GStroke: {
811         return std::make_unique<LOTGStrokeItem>(static_cast<LOTGStrokeData *>(contentData));
812         break;
813     }
814     case LOTData::Type::Repeater: {
815         return std::make_unique<LOTRepeaterItem>(static_cast<LOTRepeaterData *>(contentData));
816         break;
817     }
818     case LOTData::Type::Trim: {
819         return std::make_unique<LOTTrimItem>(static_cast<LOTTrimData *>(contentData));
820         break;
821     }
822     default:
823         return nullptr;
824         break;
825     }
826 }
827
828 void LOTShapeLayerItem::updateContent()
829 {
830     mRoot->update(frameNo(), combinedMatrix(), combinedAlpha(), flag());
831
832     if (mLayerData->hasPathOperator()) {
833         mRoot->applyTrim();
834     }
835 }
836
837 void LOTShapeLayerItem::buildLayerNode()
838 {
839     LOTLayerItem::buildLayerNode();
840
841     mDrawableList.clear();
842     renderList(mDrawableList);
843
844     mCNodeList.clear();
845     for (auto &i : mDrawableList) {
846         LOTDrawable *lotDrawable = static_cast<LOTDrawable *>(i);
847         lotDrawable->sync();
848         mCNodeList.push_back(lotDrawable->mCNode.get());
849     }
850     layerNode()->mNodeList.ptr = mCNodeList.data();
851     layerNode()->mNodeList.size = mCNodeList.size();
852 }
853
854 void LOTShapeLayerItem::renderList(std::vector<VDrawable *> &list)
855 {
856     if (!visible()) return;
857     mRoot->renderList(list);
858 }
859
860 LOTContentGroupItem::LOTContentGroupItem(LOTGroupData *data) : mData(data)
861 {
862     addChildren(mData);
863 }
864
865 void LOTContentGroupItem::addChildren(LOTGroupData *data)
866 {
867     if (!data) return;
868
869     for (auto &i : data->mChildren) {
870         auto content = LOTShapeLayerItem::createContentItem(i.get());
871         if (content) {
872             content->setParent(this);
873             mContents.push_back(std::move(content));
874         }
875     }
876
877     // keep the content in back-to-front order.
878     std::reverse(mContents.begin(), mContents.end());
879 }
880
881 void LOTContentGroupItem::update(int frameNo, const VMatrix &parentMatrix,
882                                  float parentAlpha, const DirtyFlag &flag)
883 {
884     VMatrix   m = parentMatrix;
885     float     alpha = parentAlpha;
886     DirtyFlag newFlag = flag;
887
888     if (mData && mData->mTransform) {
889         // update the matrix and the flag
890         if ((flag & DirtyFlagBit::Matrix) ||
891             !mData->mTransform->staticMatrix()) {
892             newFlag |= DirtyFlagBit::Matrix;
893         }
894         m = mData->mTransform->matrix(frameNo);
895         m *= parentMatrix;
896         alpha *= mData->mTransform->opacity(frameNo);
897
898         if (!vCompare(alpha, parentAlpha)) {
899             newFlag |= DirtyFlagBit::Alpha;
900         }
901     }
902
903     mMatrix = m;
904
905     for (const auto &content : mContents) {
906         content->update(frameNo, m, alpha, newFlag);
907     }
908 }
909
910 void LOTContentGroupItem::applyTrim()
911 {
912     for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
913         auto content = (*i).get();
914         if (auto trim = dynamic_cast<LOTTrimItem *>(content)) {
915             trim->update();
916         } else if (auto group = dynamic_cast<LOTContentGroupItem *>(content)) {
917             group->applyTrim();
918         }
919     }
920 }
921
922 void LOTContentGroupItem::renderList(std::vector<VDrawable *> &list)
923 {
924     for (const auto &content : mContents) {
925         content->renderList(list);
926     }
927 }
928
929 void LOTContentGroupItem::processPaintItems(
930     std::vector<LOTPathDataItem *> &list)
931 {
932     int curOpCount = list.size();
933     for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
934         auto content = (*i).get();
935         if (auto pathNode = dynamic_cast<LOTPathDataItem *>(content)) {
936             // add it to the list
937             list.push_back(pathNode);
938         } else if (auto paintNode = dynamic_cast<LOTPaintDataItem *>(content)) {
939             // the node is a paint data node update the path list of the paint item.
940             paintNode->addPathItems(list, curOpCount);
941         } else if (auto groupNode =
942                        dynamic_cast<LOTContentGroupItem *>(content)) {
943             // update the groups node with current list
944             groupNode->processPaintItems(list);
945         }
946     }
947 }
948
949 void LOTContentGroupItem::processTrimItems(
950     std::vector<LOTPathDataItem *> &list)
951 {
952     int curOpCount = list.size();
953     for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
954         auto content = (*i).get();
955         if (auto pathNode = dynamic_cast<LOTPathDataItem *>(content)) {
956             // add it to the list
957             list.push_back(pathNode);
958         } else if (auto trimNode = dynamic_cast<LOTTrimItem *>(content)) {
959             // the node is a paint data node update the path list of the paint item.
960             trimNode->addPathItems(list, curOpCount);
961         } else if (auto groupNode =
962                        dynamic_cast<LOTContentGroupItem *>(content)) {
963             // update the groups node with current list
964             groupNode->processTrimItems(list);
965         }
966     }
967 }
968
969 /*
970  * LOTPathDataItem uses 3 path objects for path object reuse.
971  * mLocalPath -  keeps track of the local path of the item before
972  * applying path operation and transformation.
973  * mTemp - keeps a referece to the mLocalPath and can be updated by the
974  *          path operation objects(trim, merge path),
975  *  mFinalPath - it takes a deep copy of the intermediate path(mTemp) each time
976  *  when the path is dirty(so if path changes every frame we don't realloc just copy to the
977  *  final path).
978  * NOTE: As path objects are COW objects we have to be carefull about the refcount so that
979  * we don't generate deep copy while modifying the path objects.
980  */
981 void LOTPathDataItem::update(int frameNo, const VMatrix &,
982                              float, const DirtyFlag &flag)
983 {
984     mPathChanged = false;
985
986     // 1. update the local path if needed
987     if (hasChanged(frameNo)) {
988         // loose the reference to mLocalPath if any
989         // from the last frame update.
990         mTemp = VPath();
991
992         updatePath(mLocalPath, frameNo);
993         mPathChanged = true;
994         mNeedUpdate = true;
995     }
996     // 2. keep a reference path in temp in case there is some
997     // path operation like trim which will update the path.
998     // we don't want to update the local path.
999     mTemp = mLocalPath;
1000
1001     // 3. compute the final path with parentMatrix
1002     if ((flag & DirtyFlagBit::Matrix) || mPathChanged) {
1003         mPathChanged = true;
1004     }
1005 }
1006
1007 const VPath & LOTPathDataItem::finalPath()
1008 {
1009     if (mPathChanged || mNeedUpdate) {
1010         mFinalPath.clone(mTemp);
1011         mFinalPath.transform(static_cast<LOTContentGroupItem *>(parent())->matrix());
1012         mNeedUpdate = false;
1013     }
1014     return mFinalPath;
1015 }
1016 LOTRectItem::LOTRectItem(LOTRectData *data)
1017     : LOTPathDataItem(data->isStatic()), mData(data)
1018 {
1019 }
1020
1021 void LOTRectItem::updatePath(VPath& path, int frameNo)
1022 {
1023     VPointF pos = mData->mPos.value(frameNo);
1024     VPointF size = mData->mSize.value(frameNo);
1025     float   roundness = mData->mRound.value(frameNo);
1026     VRectF  r(pos.x() - size.x() / 2, pos.y() - size.y() / 2, size.x(),
1027              size.y());
1028
1029     path.reset();
1030     path.addRoundRect(r, roundness, mData->direction());
1031 }
1032
1033 LOTEllipseItem::LOTEllipseItem(LOTEllipseData *data)
1034     : LOTPathDataItem(data->isStatic()), mData(data)
1035 {
1036 }
1037
1038 void LOTEllipseItem::updatePath(VPath& path, int frameNo)
1039 {
1040     VPointF pos = mData->mPos.value(frameNo);
1041     VPointF size = mData->mSize.value(frameNo);
1042     VRectF  r(pos.x() - size.x() / 2, pos.y() - size.y() / 2, size.x(),
1043              size.y());
1044
1045     path.reset();
1046     path.addOval(r, mData->direction());
1047 }
1048
1049 LOTShapeItem::LOTShapeItem(LOTShapeData *data)
1050     : LOTPathDataItem(data->isStatic()), mData(data)
1051 {
1052 }
1053
1054 void LOTShapeItem::updatePath(VPath& path, int frameNo)
1055 {
1056     mData->mShape.value(frameNo).toPath(path);
1057 }
1058
1059 LOTPolystarItem::LOTPolystarItem(LOTPolystarData *data)
1060     : LOTPathDataItem(data->isStatic()), mData(data)
1061 {
1062 }
1063
1064 void LOTPolystarItem::updatePath(VPath& path, int frameNo)
1065 {
1066     VPointF pos = mData->mPos.value(frameNo);
1067     float   points = mData->mPointCount.value(frameNo);
1068     float   innerRadius = mData->mInnerRadius.value(frameNo);
1069     float   outerRadius = mData->mOuterRadius.value(frameNo);
1070     float   innerRoundness = mData->mInnerRoundness.value(frameNo);
1071     float   outerRoundness = mData->mOuterRoundness.value(frameNo);
1072     float   rotation = mData->mRotation.value(frameNo);
1073
1074     path.reset();
1075     VMatrix m;
1076
1077     if (mData->mType == LOTPolystarData::PolyType::Star) {
1078         path.addPolystar(points, innerRadius, outerRadius, innerRoundness,
1079                          outerRoundness, 0.0, 0.0, 0.0, mData->direction());
1080     } else {
1081         path.addPolygon(points, outerRadius, outerRoundness, 0.0, 0.0, 0.0,
1082                         mData->direction());
1083     }
1084
1085     m.translate(pos.x(), pos.y()).rotate(rotation);
1086     m.rotate(rotation);
1087     path.transform(m);
1088 }
1089
1090 /*
1091  * PaintData Node handling
1092  *
1093  */
1094 LOTPaintDataItem::LOTPaintDataItem(bool staticContent):mDrawable(std::make_unique<LOTDrawable>()),
1095                                                        mStaticContent(staticContent){}
1096
1097 void LOTPaintDataItem::update(int frameNo, const VMatrix &parentMatrix,
1098                               float parentAlpha, const DirtyFlag &flag)
1099 {
1100     mRenderNodeUpdate = true;
1101     mParentAlpha = parentAlpha;
1102     mFlag = flag;
1103     mFrameNo = frameNo;
1104
1105     updateContent(frameNo);
1106 }
1107
1108 void LOTPaintDataItem::updateRenderNode()
1109 {
1110     bool dirty = false;
1111     for (auto &i : mPathItems) {
1112         if (i->dirty()) {
1113             dirty = true;
1114             break;
1115         }
1116     }
1117
1118     if (dirty) {
1119         mPath.reset();
1120
1121         for (auto &i : mPathItems) {
1122             mPath.addPath(i->finalPath());
1123         }
1124         mDrawable->setPath(mPath);
1125     } else {
1126         if (mDrawable->mFlag & VDrawable::DirtyState::Path)
1127             mDrawable->mPath = mPath;
1128     }
1129 }
1130
1131 void LOTPaintDataItem::renderList(std::vector<VDrawable *> &list)
1132 {
1133     if (mRenderNodeUpdate) {
1134         updateRenderNode();
1135         LOTPaintDataItem::updateRenderNode();
1136         mRenderNodeUpdate = false;
1137     }
1138     list.push_back(mDrawable.get());
1139 }
1140
1141
1142 void LOTPaintDataItem::addPathItems(std::vector<LOTPathDataItem *> &list, int startOffset)
1143 {
1144     std::copy(list.begin() + startOffset, list.end(), back_inserter(mPathItems));
1145 }
1146
1147
1148 LOTFillItem::LOTFillItem(LOTFillData *data)
1149     : LOTPaintDataItem(data->isStatic()), mData(data)
1150 {
1151 }
1152
1153 void LOTFillItem::updateContent(int frameNo)
1154 {
1155     LottieColor c = mData->mColor.value(frameNo);
1156     float       opacity = mData->opacity(frameNo);
1157     mColor = c.toColor(opacity);
1158     mFillRule = mData->fillRule();
1159 }
1160
1161 void LOTFillItem::updateRenderNode()
1162 {
1163     VColor color = mColor;
1164
1165     color.setAlpha(color.a * parentAlpha());
1166     VBrush brush(color);
1167     mDrawable->setBrush(brush);
1168     mDrawable->setFillRule(mFillRule);
1169 }
1170
1171 LOTGFillItem::LOTGFillItem(LOTGFillData *data)
1172     : LOTPaintDataItem(data->isStatic()), mData(data)
1173 {
1174 }
1175
1176 void LOTGFillItem::updateContent(int frameNo)
1177 {
1178     mData->update(mGradient, frameNo);
1179     mGradient->mMatrix = static_cast<LOTContentGroupItem *>(parent())->matrix();
1180     mFillRule = mData->fillRule();
1181 }
1182
1183 void LOTGFillItem::updateRenderNode()
1184 {
1185     mGradient->setAlpha(parentAlpha());
1186     mDrawable->setBrush(VBrush(mGradient.get()));
1187     mDrawable->setFillRule(mFillRule);
1188 }
1189
1190 LOTStrokeItem::LOTStrokeItem(LOTStrokeData *data)
1191     : LOTPaintDataItem(data->isStatic()), mData(data)
1192 {
1193     mDashArraySize = 0;
1194 }
1195
1196 void LOTStrokeItem::updateContent(int frameNo)
1197 {
1198     LottieColor c = mData->mColor.value(frameNo);
1199     float       opacity = mData->opacity(frameNo);
1200     mColor = c.toColor(opacity);
1201     mCap = mData->capStyle();
1202     mJoin = mData->joinStyle();
1203     mMiterLimit = mData->meterLimit();
1204     mWidth = mData->width(frameNo);
1205     if (mData->hasDashInfo()) {
1206         mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
1207     }
1208 }
1209
1210 static float getScale(const VMatrix &matrix)
1211 {
1212     constexpr float SQRT_2 = 1.41421;
1213     VPointF         p1(0, 0);
1214     VPointF         p2(SQRT_2, SQRT_2);
1215     p1 = matrix.map(p1);
1216     p2 = matrix.map(p2);
1217     VPointF final = p2 - p1;
1218
1219     return std::sqrt(final.x() * final.x() + final.y() * final.y()) / 2.0;
1220 }
1221
1222 void LOTStrokeItem::updateRenderNode()
1223 {
1224     VColor color = mColor;
1225
1226     color.setAlpha(color.a * parentAlpha());
1227     VBrush brush(color);
1228     mDrawable->setBrush(brush);
1229     float scale = getScale(static_cast<LOTContentGroupItem *>(parent())->matrix());
1230     mDrawable->setStrokeInfo(mCap, mJoin, mMiterLimit,
1231                             mWidth * scale);
1232     if (mDashArraySize) {
1233         for (int i = 0 ; i < mDashArraySize ; i++)
1234             mDashArray[i] *= scale;
1235
1236         /* AE draw the dash even if dash value is 0 */
1237         if (vCompare(mDashArray[0], 0.0f)) mDashArray[0]= 0.1;
1238
1239         mDrawable->setDashInfo(mDashArray, mDashArraySize);
1240     }
1241 }
1242
1243 LOTGStrokeItem::LOTGStrokeItem(LOTGStrokeData *data)
1244     : LOTPaintDataItem(data->isStatic()), mData(data)
1245 {
1246     mDashArraySize = 0;
1247 }
1248
1249 void LOTGStrokeItem::updateContent(int frameNo)
1250 {
1251     mData->update(mGradient, frameNo);
1252     mGradient->mMatrix = static_cast<LOTContentGroupItem *>(parent())->matrix();
1253     mCap = mData->capStyle();
1254     mJoin = mData->joinStyle();
1255     mMiterLimit = mData->meterLimit();
1256     mWidth = mData->width(frameNo);
1257     if (mData->hasDashInfo()) {
1258         mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
1259     }
1260 }
1261
1262 void LOTGStrokeItem::updateRenderNode()
1263 {
1264     float scale = getScale(mGradient->mMatrix);
1265     mGradient->setAlpha(parentAlpha());
1266     mDrawable->setBrush(VBrush(mGradient.get()));
1267     mDrawable->setStrokeInfo(mCap, mJoin, mMiterLimit,
1268                             mWidth * scale);
1269     if (mDashArraySize) {
1270         for (int i = 0 ; i < mDashArraySize ; i++)
1271             mDashArray[i] *= scale;
1272         mDrawable->setDashInfo(mDashArray, mDashArraySize);
1273     }
1274 }
1275
1276 LOTTrimItem::LOTTrimItem(LOTTrimData *data) : mData(data) {}
1277
1278 void LOTTrimItem::update(int frameNo, const VMatrix &/*parentMatrix*/,
1279                          float /*parentAlpha*/, const DirtyFlag &/*flag*/)
1280 {
1281     mDirty = false;
1282
1283     if (mCache.mFrameNo == frameNo) return;
1284
1285     LOTTrimData::Segment segment = mData->segment(frameNo);
1286
1287     if (!(vCompare(mCache.mSegment.start, segment.start) &&
1288           vCompare(mCache.mSegment.end, segment.end))) {
1289         mDirty = true;
1290         mCache.mSegment = segment;
1291     }
1292     mCache.mFrameNo = frameNo;
1293 }
1294
1295 void LOTTrimItem::update()
1296 {
1297     // when both path and trim are not dirty
1298     if (!(mDirty || pathDirty())) return;
1299
1300     if (vCompare(mCache.mSegment.start, mCache.mSegment.end)) {
1301         for (auto &i : mPathItems) {
1302             i->updatePath(VPath());
1303         }
1304         return;
1305     }
1306
1307     if (vCompare(std::fabs(mCache.mSegment.start - mCache.mSegment.end) , 1)) {
1308         for (auto &i : mPathItems) {
1309             i->updatePath(i->localPath());
1310         }
1311         return;
1312     }
1313
1314     if (mData->type() == LOTTrimData::TrimType::Simultaneously) {
1315         for (auto &i : mPathItems) {
1316             VPathMesure pm;
1317             pm.setStart(mCache.mSegment.start);
1318             pm.setEnd(mCache.mSegment.end);
1319             i->updatePath(pm.trim(i->localPath()));
1320         }
1321     } else { // LOTTrimData::TrimType::Individually
1322         float totalLength = 0.0;
1323         for (auto &i : mPathItems) {
1324             totalLength += i->localPath().length();
1325         }
1326         float start = totalLength * mCache.mSegment.start;
1327         float end  = totalLength * mCache.mSegment.end;
1328
1329         if (start < end ) {
1330             float curLen = 0.0;
1331             for (auto &i : mPathItems) {
1332                 if (curLen > end) {
1333                     // update with empty path.
1334                     i->updatePath(VPath());
1335                     continue;
1336                 }
1337                 float len = i->localPath().length();
1338
1339                 if (curLen < start  && curLen + len < start) {
1340                     curLen += len;
1341                     // update with empty path.
1342                     i->updatePath(VPath());
1343                     continue;
1344                 } else if (start <= curLen && end >= curLen + len) {
1345                     // inside segment
1346                     curLen += len;
1347                     continue;
1348                 } else {
1349                     float local_start = start > curLen ? start - curLen : 0;
1350                     local_start /= len;
1351                     float local_end = curLen + len < end ? len : end - curLen;
1352                     local_end /= len;
1353                     VPathMesure pm;
1354                     pm.setStart(local_start);
1355                     pm.setEnd(local_end);
1356                     VPath p = pm.trim(i->localPath());
1357                     i->updatePath(p);
1358                     curLen += len;
1359                 }
1360             }
1361         }
1362     }
1363
1364 }
1365
1366
1367 void LOTTrimItem::addPathItems(std::vector<LOTPathDataItem *> &list, int startOffset)
1368 {
1369     std::copy(list.begin() + startOffset, list.end(), back_inserter(mPathItems));
1370 }
1371
1372
1373 LOTRepeaterItem::LOTRepeaterItem(LOTRepeaterData *data) : mData(data)
1374 {
1375     assert(mData->mChildren.size() == 1);
1376     LOTGroupData *root = reinterpret_cast<LOTGroupData *>(mData->mChildren[0].get());
1377     assert(root);
1378
1379     for (int i= 0; i < mData->copies(0); i++) {
1380         auto content = std::make_unique<LOTContentGroupItem>(static_cast<LOTGroupData *>(root));
1381         content->setParent(this);
1382         mContents.push_back(std::move(content));
1383     }
1384 }
1385
1386 void LOTRepeaterItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
1387 {
1388
1389     DirtyFlag newFlag = flag;
1390
1391     if (mData->hasMtrixChange(frameNo)) {
1392         newFlag |= DirtyFlagBit::Matrix;
1393     }
1394
1395     float multiplier = mData->offset(frameNo);
1396     float startOpacity = mData->mTransform->startOpacity(frameNo);
1397     float endOpacity = mData->mTransform->endOpacity(frameNo);
1398     float index = 0;
1399     float copies = mData->copies(frameNo);
1400     if (!vCompare(copies, 1)) copies -=1;
1401
1402     newFlag |= DirtyFlagBit::Alpha;
1403     for (const auto &content : mContents) {
1404         float newAlpha = parentAlpha * lerp(startOpacity, endOpacity, index / copies);
1405         VMatrix result = mData->mTransform->matrixForRepeater(frameNo, multiplier) * parentMatrix;
1406         content->update(frameNo, result, newAlpha, newFlag);
1407         multiplier += 1;
1408         index +=1;
1409     }
1410 }
1411
1412 static void updateGStops(LOTNode *n, const VGradient *grad)
1413 {
1414     if (grad->mStops.size() != n->mGradient.stopCount) {
1415         if (n->mGradient.stopCount)
1416             free(n->mGradient.stopPtr);
1417         n->mGradient.stopCount = grad->mStops.size();
1418         n->mGradient.stopPtr = (LOTGradientStop *) malloc(n->mGradient.stopCount * sizeof(LOTGradientStop));
1419     }
1420
1421     LOTGradientStop *ptr = n->mGradient.stopPtr;
1422     for (const auto &i : grad->mStops) {
1423         ptr->pos = i.first;
1424         ptr->a = i.second.alpha() * grad->alpha();
1425         ptr->r = i.second.red();
1426         ptr->g = i.second.green();
1427         ptr->b = i.second.blue();
1428         ptr++;
1429     }
1430
1431 }
1432
1433 void LOTDrawable::sync()
1434 {
1435     if (!mCNode) {
1436         mCNode = std::make_unique<LOTNode>();
1437         mCNode->mGradient.stopPtr = nullptr;
1438         mCNode->mGradient.stopCount = 0;
1439     }
1440
1441     mCNode->mFlag = ChangeFlagNone;
1442     if (mFlag & DirtyState::None) return;
1443
1444     if (mFlag & DirtyState::Path) {
1445         if (mStroke.mDash.size()) {
1446             VDasher dasher(mStroke.mDash.data(), mStroke.mDash.size());
1447             mPath = dasher.dashed(mPath);
1448         }
1449         const std::vector<VPath::Element> &elm = mPath.elements();
1450         const std::vector<VPointF> &       pts = mPath.points();
1451         const float *ptPtr = reinterpret_cast<const float *>(pts.data());
1452         const char * elmPtr = reinterpret_cast<const char *>(elm.data());
1453         mCNode->mPath.elmPtr = elmPtr;
1454         mCNode->mPath.elmCount = elm.size();
1455         mCNode->mPath.ptPtr = ptPtr;
1456         mCNode->mPath.ptCount = 2 * pts.size();
1457         mCNode->mFlag |= ChangeFlagPath;
1458     }
1459
1460     if (mStroke.enable) {
1461         mCNode->mStroke.width = mStroke.width;
1462         mCNode->mStroke.meterLimit = mStroke.meterLimit;
1463         mCNode->mStroke.enable = 1;
1464
1465         switch (mStroke.cap) {
1466         case CapStyle::Flat:
1467             mCNode->mStroke.cap = LOTCapStyle::CapFlat;
1468             break;
1469         case CapStyle::Square:
1470             mCNode->mStroke.cap = LOTCapStyle::CapSquare;
1471             break;
1472         case CapStyle::Round:
1473             mCNode->mStroke.cap = LOTCapStyle::CapRound;
1474             break;
1475         default:
1476             mCNode->mStroke.cap = LOTCapStyle::CapFlat;
1477             break;
1478         }
1479
1480         switch (mStroke.join) {
1481         case JoinStyle::Miter:
1482             mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
1483             break;
1484         case JoinStyle::Bevel:
1485             mCNode->mStroke.join = LOTJoinStyle::JoinBevel;
1486             break;
1487         case JoinStyle::Round:
1488             mCNode->mStroke.join = LOTJoinStyle::JoinRound;
1489             break;
1490         default:
1491             mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
1492             break;
1493         }
1494     } else {
1495         mCNode->mStroke.enable = 0;
1496     }
1497
1498     switch (mFillRule) {
1499     case FillRule::EvenOdd:
1500         mCNode->mFillRule = LOTFillRule::FillEvenOdd;
1501         break;
1502     default:
1503         mCNode->mFillRule = LOTFillRule::FillWinding;
1504         break;
1505     }
1506
1507     switch (mBrush.type()) {
1508     case VBrush::Type::Solid:
1509         mCNode->mBrushType = LOTBrushType::BrushSolid;
1510         mCNode->mColor.r = mBrush.mColor.r;
1511         mCNode->mColor.g = mBrush.mColor.g;
1512         mCNode->mColor.b = mBrush.mColor.b;
1513         mCNode->mColor.a = mBrush.mColor.a;
1514         break;
1515     case VBrush::Type::LinearGradient: {
1516         mCNode->mBrushType = LOTBrushType::BrushGradient;
1517         mCNode->mGradient.type = LOTGradientType::GradientLinear;
1518         VPointF s = mBrush.mGradient->mMatrix.map({mBrush.mGradient->linear.x1,
1519                                                    mBrush.mGradient->linear.y1});
1520         VPointF e = mBrush.mGradient->mMatrix.map({mBrush.mGradient->linear.x2,
1521                                                    mBrush.mGradient->linear.y2});
1522         mCNode->mGradient.start.x = s.x();
1523         mCNode->mGradient.start.y = s.y();
1524         mCNode->mGradient.end.x = e.x();
1525         mCNode->mGradient.end.y = e.y();
1526         updateGStops(mCNode.get(), mBrush.mGradient);
1527         break;
1528     }
1529     case VBrush::Type::RadialGradient: {
1530         mCNode->mBrushType = LOTBrushType::BrushGradient;
1531         mCNode->mGradient.type = LOTGradientType::GradientRadial;
1532         VPointF c = mBrush.mGradient->mMatrix.map({mBrush.mGradient->radial.cx,
1533                                                    mBrush.mGradient->radial.cy});
1534         VPointF f = mBrush.mGradient->mMatrix.map({mBrush.mGradient->radial.fx,
1535                                                    mBrush.mGradient->radial.fy});
1536         mCNode->mGradient.center.x = c.x();
1537         mCNode->mGradient.center.y = c.y();
1538         mCNode->mGradient.focal.x = f.x();
1539         mCNode->mGradient.focal.y = f.y();
1540
1541         float scale = getScale(mBrush.mGradient->mMatrix);
1542         mCNode->mGradient.cradius = mBrush.mGradient->radial.cradius * scale;
1543         mCNode->mGradient.fradius = mBrush.mGradient->radial.fradius * scale;
1544         updateGStops(mCNode.get(), mBrush.mGradient);
1545         break;
1546     }
1547     default:
1548         break;
1549     }
1550 }