lottie/trim: fix regression, always modify the path in trim::update() call.
[platform/core/uifw/lottie-player.git] / src / lottie / lottieitem.cpp
index a48777b..a882a0a 100644 (file)
 #include "lottieitem.h"
-#include"vbitmap.h"
-#include"vpainter.h"
-#include"vraster.h"
-#include"vdasher.h"
 #include <cmath>
+#include <algorithm>
+#include "vbitmap.h"
+#include "vdasher.h"
+#include "vpainter.h"
+#include "vraster.h"
 
+/* Lottie Layer Rules
+ * 1. time stretch is pre calculated and applied to all the properties of the
+ * lottilayer model and all its children
+ * 2. The frame property could be reversed using,time-reverse layer property in
+ * AE. which means (start frame > endFrame) 3.
+ */
 
-VDrawable::VDrawable():mFlag(DirtyState::All),
-                       mType(Type::Fill),
-                       mFillRule(FillRule::Winding)
-{
-    mStroke.dashArraySize = 0;
-    mStroke.cap = CapStyle::Round;
-    mStroke.join= JoinStyle::Round;
-    mStroke.meterLimit = 10;
-    mStroke.enable = false;
-}
-
-void VDrawable::preprocess()
-{
-    if (mFlag & (DirtyState::Path)) {
-        if (mStroke.enable) {
-            VPath newPath = mPath;
-            if (mStroke.dashArraySize) {
-                VDasher dasher(mStroke.dashArray, mStroke.dashArraySize);
-                newPath = dasher.dashed(mPath);
-            }
-            FTOutline *outline = VRaster::toFTOutline(newPath);
-            mRleTask = VRaster::instance().generateStrokeInfo(outline, mStroke.cap, mStroke.join,
-                                                              mStroke.width, mStroke.meterLimit);
-        } else {
-            FTOutline *outline = VRaster::toFTOutline(mPath);
-            mRleTask = VRaster::instance().generateFillInfo(outline, mFillRule);
-        }
-        mFlag &= ~DirtyFlag(DirtyState::Path);
-    }
-}
-
-VRle VDrawable::rle()
-{
-    if (mRleTask.valid()) {
-        mRle = std::move(mRleTask.get());
-    }
-    return mRle;
-}
-
-void VDrawable::setStrokeInfo(CapStyle cap, JoinStyle join, float meterLimit, float strokeWidth)
+LOTCompItem::LOTCompItem(LOTModel *model)
+    : mRootModel(model), mUpdateViewBox(false), mCurFrameNo(-1)
 {
-    mStroke.enable = true;
-    mStroke.cap = cap;
-    mStroke.join = join;
-    mStroke.meterLimit = meterLimit;
-    mStroke.width = strokeWidth;
-    mFlag |= DirtyState::Path;
-}
-void VDrawable::setDashInfo(float *array, int size)
-{
-    mStroke.dashArray = array;
-    mStroke.dashArraySize = size;
-    mFlag |= DirtyState::Path;
+    mCompData = model->mRoot.get();
+    mRootLayer = createLayerItem(mCompData->mRootLayer.get());
+    mRootLayer->updateStaticProperty();
+    mViewSize = mCompData->size();
 }
 
-void VDrawable::sync()
+std::unique_ptr<LOTLayerItem>
+LOTCompItem::createLayerItem(LOTLayerData *layerData)
 {
-    mCNode.mFlag = ChangeFlagNone;
-    if (mFlag & DirtyState::None) return;
-
-    if (mFlag & DirtyState::Path) {
-        const std::vector<VPath::Element> &elm = mPath.elements();
-        const std::vector<VPointF> &pts  = mPath.points();
-        const float *ptPtr = reinterpret_cast<const float *>(pts.data());
-        const char *elmPtr = reinterpret_cast<const char *>(elm.data());
-        mCNode.mPath.elmPtr = elmPtr;
-        mCNode.mPath.elmCount = elm.size();
-        mCNode.mPath.ptPtr = ptPtr;
-        mCNode.mPath.ptCount = 2 * pts.size();
-        mCNode.mFlag |= ChangeFlagPath;
-    }
-
-    if (mStroke.enable) {
-        mCNode.mStroke.width = mStroke.width;
-        mCNode.mStroke.meterLimit = mStroke.meterLimit;
-        mCNode.mStroke.enable = 1;
-
-        switch (mFillRule) {
-        case FillRule::EvenOdd:
-            mCNode.mFillRule = LOTNode::EvenOdd;
-            break;
-        default:
-            mCNode.mFillRule = LOTNode::Winding;
-            break;
-        }
-
-        switch (mStroke.cap) {
-        case CapStyle::Flat:
-            mCNode.mStroke.cap = LOTNode::FlatCap;
-            break;
-        case CapStyle::Square:
-            mCNode.mStroke.cap = LOTNode::SquareCap;
-            break;
-        case CapStyle::Round:
-            mCNode.mStroke.cap = LOTNode::RoundCap;
-            break;
-        default:
-            mCNode.mStroke.cap = LOTNode::FlatCap;
-            break;
-        }
-
-        switch (mStroke.join) {
-        case JoinStyle::Miter:
-            mCNode.mStroke.join = LOTNode::MiterJoin;
-            break;
-        case JoinStyle::Bevel:
-            mCNode.mStroke.join = LOTNode::BevelJoin;
-            break;
-        case JoinStyle::Round:
-            mCNode.mStroke.join = LOTNode::RoundJoin;
-            break;
-        default:
-            mCNode.mStroke.join = LOTNode::MiterJoin;
-            break;
-        }
-
-        mCNode.mStroke.dashArray = mStroke.dashArray;
-        mCNode.mStroke.dashArraySize = mStroke.dashArraySize;
-
-    } else {
-        mCNode.mStroke.enable = 0;
+    switch (layerData->mLayerType) {
+    case LayerType::Precomp: {
+        return std::make_unique<LOTCompLayerItem>(layerData);
+        break;
     }
-
-    switch (mBrush.type()) {
-    case VBrush::Type::Solid:
-        mCNode.mType = LOTNode::BrushSolid;
-        mCNode.mColor.r = mBrush.mColor.r;
-        mCNode.mColor.g = mBrush.mColor.g;
-        mCNode.mColor.b = mBrush.mColor.b;
-        mCNode.mColor.a = mBrush.mColor.a;
+    case LayerType::Solid: {
+        return std::make_unique<LOTSolidLayerItem>(layerData);
         break;
-    case VBrush::Type::LinearGradient:
-        mCNode.mType = LOTNode::BrushGradient;
-        mCNode.mGradient.type = LOTNode::Gradient::Linear;
-        mCNode.mGradient.start.x = mBrush.mGradient->linear.x1;
-        mCNode.mGradient.start.y = mBrush.mGradient->linear.y1;
-        mCNode.mGradient.end.x = mBrush.mGradient->linear.x2;
-        mCNode.mGradient.end.y = mBrush.mGradient->linear.y2;
+    }
+    case LayerType::Shape: {
+        return std::make_unique<LOTShapeLayerItem>(layerData);
         break;
-    case VBrush::Type::RadialGradient:
-        mCNode.mType = LOTNode::BrushGradient;
-        mCNode.mGradient.type = LOTNode::Gradient::Radial;
-        mCNode.mGradient.center.x = mBrush.mGradient->radial.cx;
-        mCNode.mGradient.center.y = mBrush.mGradient->radial.cy;
-        mCNode.mGradient.focal.x = mBrush.mGradient->radial.fx;
-        mCNode.mGradient.focal.y = mBrush.mGradient->radial.fy;
-        mCNode.mGradient.cradius = mBrush.mGradient->radial.cradius;
-        mCNode.mGradient.fradius = mBrush.mGradient->radial.fradius;
+    }
+    case LayerType::Null: {
+        return std::make_unique<LOTNullLayerItem>(layerData);
         break;
+    }
     default:
+        return nullptr;
         break;
     }
 }
 
-/* Lottie Layer Rules
- * 1. time stretch is pre calculated and applied to all the properties of the lottilayer model and all its children
- * 2. The frame property could be reversed using,time-reverse layer property in AE. which means (start frame > endFrame)
- * 3.
- */
-
-LOTCompItem::LOTCompItem(LOTModel *model):mRootModel(model), mUpdateViewBox(false),mCurFrameNo(-1)
-{
-   // 1. build layer item list
-   mCompData = model->mRoot.get();
-   for(auto i : mCompData->mChildren) {
-      LOTLayerData *layerData = dynamic_cast<LOTLayerData *>(i.get());
-      if (layerData) {
-         LOTLayerItem *layerItem = LOTCompItem::createLayerItem(layerData);
-         if (layerItem) {
-            mLayers.push_back(layerItem);
-            mLayerMap[layerItem->id()] = layerItem;
-         }
-      }
-   }
-
-   //2. update parent layer
-   for(auto i : mLayers) {
-      int id = i->parentId();
-      if (id >=0) {
-         auto search = mLayerMap.find(id);
-         if (search != mLayerMap.end()) {
-           LOTLayerItem *parentLayer = search->second;
-           i->setParentLayer(parentLayer);
-         }
-      }
-   }
-   //3. update static property of each layer
-   for(auto i : mLayers) {
-      i->updateStaticProperty();
-   }
-
-   mViewSize = mCompData->size();
-}
-
-LOTCompItem::~LOTCompItem()
-{
-    for(auto i : mLayers) {
-       delete i;
-    }
-}
-
-LOTLayerItem *
-LOTCompItem::createLayerItem(LOTLayerData *layerData)
-{
-    switch(layerData->mLayerType) {
-        case LayerType::Precomp: {
-            return new LOTCompLayerItem(layerData);
-            break;
-        }
-        case LayerType::Solid: {
-            return new LOTSolidLayerItem(layerData);
-            break;
-        }
-        case LayerType::Shape: {
-            return new LOTShapeLayerItem(layerData);
-            break;
-        }
-        case LayerType::Null: {
-            return new LOTNullLayerItem(layerData);
-            break;
-        }
-        default:
-            return nullptr;
-            break;
-    }
-}
-
 void LOTCompItem::resize(const VSize &size)
 {
-   if (mViewSize == size) return;
-   mViewSize = size;
-   mUpdateViewBox = true;
+    if (mViewSize == size) return;
+    mViewSize = size;
+    mUpdateViewBox = true;
 }
 
 VSize LOTCompItem::size() const
 {
-   return mViewSize;
+    return mViewSize;
 }
 
 bool LOTCompItem::update(int frameNo)
 {
-   VMatrix m;
-   float sx, sy;
+    // check if cached frame is same as requested frame.
+    if (!mUpdateViewBox && (mCurFrameNo == frameNo)) return false;
+
+    /*
+     * if viewbox dosen't scale exactly to the viewport
+     * we scale the viewbox keeping AspectRatioPreserved and then align the
+     * viewbox to the viewport using AlignCenter rule.
+     */
+    VSize viewPort = mViewSize;
+    VSize viewBox = mCompData->size();
 
-   // check if cached frame is same as requested frame.
-   if (!mUpdateViewBox && (mCurFrameNo == frameNo)) return false;
+    float sx = float(viewPort.width()) / viewBox.width();
+    float sy = float(viewPort.height()) / viewBox.height();
+    float scale = fmin(sx, sy);
+    float tx = (viewPort.width() - viewBox.width() * scale) * 0.5;
+    float ty = (viewPort.height() - viewBox.height() * scale) * 0.5;
 
-   sx = mViewSize.width() / float(mCompData->size().width());
-   sy = mViewSize.height() / float(mCompData->size().height());
-   float scale = fmin(sx, sy);
-   m.scale(scale, scale);
+    VMatrix m;
+    m.translate(tx, ty).scale(scale, scale);
+    mRootLayer->update(frameNo, m, 1.0);
 
-   // update the layer from back to front
-   for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
-      LOTLayerItem *layer = *i;
-      layer->update(frameNo, m, 1.0);
-   }
-   buildRenderList();
-   mCurFrameNo = frameNo;
-   mUpdateViewBox = false;
-   return true;
+    mCurFrameNo = frameNo;
+    mUpdateViewBox = false;
+    return true;
 }
 
 void LOTCompItem::buildRenderList()
 {
     mDrawableList.clear();
-    for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
-       LOTLayerItem *layer = *i;
-       layer->renderList(mDrawableList);
-    }
+    mRootLayer->renderList(mDrawableList);
 
     mRenderList.clear();
-    for(auto i : mDrawableList) {
-        i->sync();
-        mRenderList.push_back(&i->mCNode);
+    for (auto &i : mDrawableList) {
+        LOTDrawable *lotDrawable = static_cast<LOTDrawable *>(i);
+        lotDrawable->sync();
+        mRenderList.push_back(lotDrawable->mCNode.get());
     }
 }
 
-const std::vector<LOTNode *>LOTCompItem::renderList() const
+const std::vector<LOTNode *> &LOTCompItem::renderList() const
 {
     return mRenderList;
 }
 
-bool LOTCompItem::render(const LOTBuffer &buffer)
+bool LOTCompItem::render(const lottie::Surface &surface)
 {
-    VBitmap bitmap((uchar *)buffer.buffer, buffer.width, buffer.height,
-                   buffer.bytesPerLine, VBitmap::Format::ARGB32_Premultiplied, nullptr, nullptr);
+    VBitmap bitmap((uchar *)surface.buffer(), surface.width(), surface.height(),
+                   surface.bytesPerLine(), VBitmap::Format::ARGB32_Premultiplied,
+                   nullptr, nullptr);
 
     /* schedule all preprocess task for this frame at once.
      */
+    mDrawableList.clear();
+    mRootLayer->renderList(mDrawableList);
+    VRect clip(0, 0, surface.width(), surface.height());
     for (auto &e : mDrawableList) {
-        e->preprocess();
+        e->preprocess(clip);
     }
 
     VPainter painter(&bitmap);
-    VRle mask;
-    for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
-       LOTLayerItem *layer = *i;
-       layer->render(&painter, mask);
-    }
+    mRootLayer->render(&painter, {}, {}, nullptr);
 
     return true;
 }
 
 void LOTMaskItem::update(int frameNo, const VMatrix &parentMatrix,
-                         float parentAlpha, const DirtyFlag &flag)
+                         float parentAlpha, const DirtyFlag &/*flag*/)
 {
     if (mData->mShape.isStatic()) {
-        if (mLocalPath.isEmpty()) {
-            mLocalPath = mData->mShape.value(frameNo).toPath();
+        if (mLocalPath.empty()) {
+            mData->mShape.value(frameNo).toPath(mLocalPath);
         }
     } else {
-        mLocalPath = mData->mShape.value(frameNo).toPath();
+        mData->mShape.value(frameNo).toPath(mLocalPath);
     }
     float opacity = mData->opacity(frameNo);
     opacity = opacity * parentAlpha;
@@ -327,223 +144,264 @@ void LOTMaskItem::update(int frameNo, const VMatrix &parentMatrix,
     VPath path = mLocalPath;
     path.transform(parentMatrix);
 
-    FTOutline *outline = VRaster::toFTOutline(path);
-    mRleTask = VRaster::instance().generateFillInfo(outline);
+    mRleTask = VRaster::generateFillInfo(std::move(path), std::move(mRle));
+    mRle = VRle();
 }
 
 VRle LOTMaskItem::rle()
 {
     if (mRleTask.valid()) {
-        mRle = std::move(mRleTask.get());
+        mRle = mRleTask.get();
         if (!vCompare(mCombinedAlpha, 1.0f))
-            mRle = mRle * (mCombinedAlpha * 255);
-        if (mData->mInv)
-            mRle = ~mRle;
+            mRle *= (mCombinedAlpha * 255);
+        if (mData->mInv) mRle.invert();
     }
     return mRle;
 }
 
-void LOTLayerItem::render(VPainter *painter, const VRle &inheritMask)
+void LOTLayerItem::render(VPainter *painter, const VRle &inheritMask, const VRle &inheritMatte, LOTLayerItem *matteSource)
 {
-    std::vector<VDrawable *> list;
-    renderList(list);
-    VRle mask = inheritMask;
+    VRle matteRle;
+    if (matteSource) {
+        mDrawableList.clear();
+        matteSource->renderList(mDrawableList);
+        for (auto &i : mDrawableList) {
+            matteRle = matteRle + i->rle();
+        }
+
+        if (!inheritMatte.empty())
+            matteRle = matteRle & inheritMatte;
+    } else {
+        matteRle = inheritMatte;
+    }
+    mDrawableList.clear();
+    renderList(mDrawableList);
+
+    VRle mask;
     if (hasMask()) {
-        if (mask.isEmpty())
-            mask = maskRle(painter->clipBoundingRect());
-        else
+        mask = maskRle(painter->clipBoundingRect());
+        if (!inheritMask.empty())
             mask = mask & inheritMask;
+        // if resulting mask is empty then return.
+        if (mask.empty())
+            return;
+    } else {
+        mask = inheritMask;
     }
 
-    for(auto i : list) {
+    for (auto &i : mDrawableList) {
         painter->setBrush(i->mBrush);
-        if (!mask.isEmpty()) {
-            VRle rle = i->rle() & mask;
-            painter->drawRle(VPoint(), rle);
-        } else {
-            painter->drawRle(VPoint(), i->rle());
+        VRle rle = i->rle();
+        if (!mask.empty()) rle = rle & mask;
+
+        if (rle.empty()) continue;
+
+        if (!matteRle.empty()) {
+            if (mLayerData->mMatteType == MatteType::AlphaInv) {
+                rle = rle - matteRle;
+            } else {
+                rle = rle & matteRle;
+            }
         }
+        painter->drawRle(VPoint(), rle);
     }
 }
 
 VRle LOTLayerItem::maskRle(const VRect &clipRect)
 {
-
     VRle rle;
     for (auto &i : mMasks) {
         switch (i->maskMode()) {
-            case LOTMaskData::Mode::Add: {
-                rle = rle + i->rle();
-                break;
-            }
-            case LOTMaskData::Mode::Substarct: {
-                if (rle.isEmpty() && !clipRect.isEmpty())
-                    rle = VRle::toRle(clipRect);
-                rle = rle - i->rle();
-                break;
-            }
-            case LOTMaskData::Mode::Intersect: {
-                rle = rle & i->rle();
-                break;
-            }
-            default:
-                break;
+        case LOTMaskData::Mode::Add: {
+            rle = rle + i->rle();
+            break;
+        }
+        case LOTMaskData::Mode::Substarct: {
+            if (rle.empty() && !clipRect.empty())
+                rle = VRle::toRle(clipRect);
+            rle = rle - i->rle();
+            break;
+        }
+        case LOTMaskData::Mode::Intersect: {
+            rle = rle & i->rle();
+            break;
+        }
+        case LOTMaskData::Mode::Difference: {
+            rle = rle ^ i->rle();
+            break;
+        }
+        default:
+            break;
         }
     }
     return rle;
 }
 
-LOTLayerItem::LOTLayerItem(LOTLayerData *layerData):mLayerData(layerData),
-                                                    mParentLayer(nullptr),
-                                                    mPrecompLayer(nullptr),
-                                                    mCombinedAlpha(0.0f),
-                                                    mFrameNo(-1),
-                                                    mDirtyFlag(DirtyFlagBit::All)
+LOTLayerItem::LOTLayerItem(LOTLayerData *layerData): mLayerData(layerData)
 {
     if (mLayerData->mHasMask) {
-        for (auto i : mLayerData->mMasks) {
-            mMasks.push_back(std::unique_ptr<LOTMaskItem>(new LOTMaskItem(i.get())));
+        for (auto &i : mLayerData->mMasks) {
+            mMasks.push_back(std::make_unique<LOTMaskItem>(i.get()));
         }
     }
 }
 
 void LOTLayerItem::updateStaticProperty()
 {
-   if (mParentLayer)
-     mParentLayer->updateStaticProperty();
+    if (mParentLayer) mParentLayer->updateStaticProperty();
 
-   mStatic = mLayerData->isStatic();
-   mStatic = mParentLayer ? (mStatic & mParentLayer->isStatic()) : mStatic;
-   mStatic = mPrecompLayer ? (mStatic & mPrecompLayer->isStatic()) : mStatic;
+    mStatic = mLayerData->isStatic();
+    mStatic = mParentLayer ? (mStatic & mParentLayer->isStatic()) : mStatic;
+    mStatic = mPrecompLayer ? (mStatic & mPrecompLayer->isStatic()) : mStatic;
 }
 
-void LOTLayerItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha)
+void LOTLayerItem::update(int frameNumber, const VMatrix &parentMatrix,
+                          float parentAlpha)
 {
-   mFrameNo = frameNo;
-   // 1. check if the layer is part of the current frame
-   if (!visible()) return;
+    mFrameNo = frameNumber;
+    // 1. check if the layer is part of the current frame
+    if (!visible()) return;
 
-   // 2. calculate the parent matrix and alpha
-   VMatrix m = matrix(frameNo) * parentMatrix;
-   float alpha = parentAlpha * opacity(frameNo);
+    // 2. calculate the parent matrix and alpha
+    VMatrix m = matrix(frameNo());
+    m *= parentMatrix;
+    float alpha = parentAlpha * opacity(frameNo());
 
-   //6. update the mask
-   if (hasMask()) {
-       for (auto &i : mMasks)
-           i->update(frameNo, m, alpha, mDirtyFlag);
-   }
+    // 6. update the mask
+    if (hasMask()) {
+        for (auto &i : mMasks) i->update(frameNo(), m, alpha, mDirtyFlag);
+    }
 
-   // 3. update the dirty flag based on the change
-   if (!mCombinedMatrix.fuzzyCompare(m)) {
-       mDirtyFlag |= DirtyFlagBit::Matrix;
-   }
-   if (!vCompare(mCombinedAlpha, alpha)) {
-       mDirtyFlag |= DirtyFlagBit::Alpha;
-   }
-   mCombinedMatrix = m;
-   mCombinedAlpha = alpha;
+    // 3. update the dirty flag based on the change
+    if (!mCombinedMatrix.fuzzyCompare(m)) {
+        mDirtyFlag |= DirtyFlagBit::Matrix;
+    }
+    if (!vCompare(mCombinedAlpha, alpha)) {
+        mDirtyFlag |= DirtyFlagBit::Alpha;
+    }
+    mCombinedMatrix = m;
+    mCombinedAlpha = alpha;
 
-   // 4. if no parent property change and layer is static then nothing to do.
-   if ((flag() & DirtyFlagBit::None) && isStatic())
-      return;
+    // 4. if no parent property change and layer is static then nothing to do.
+    if ((flag() & DirtyFlagBit::None) && isStatic()) return;
 
-   //5. update the content of the layer
-   updateContent();
+    // 5. update the content of the layer
+    updateContent();
 
-   //6. reset the dirty flag
-   mDirtyFlag = DirtyFlagBit::None;
+    // 6. reset the dirty flag
+    mDirtyFlag = DirtyFlagBit::None;
 }
 
-float
-LOTLayerItem::opacity(int frameNo) const
+float LOTLayerItem::opacity(int frameNo) const
 {
-   return mLayerData->mTransform->opacity(frameNo);
+    return mLayerData->mTransform->opacity(frameNo);
 }
 
-VMatrix
-LOTLayerItem::matrix(int frameNo) const
+VMatrix LOTLayerItem::matrix(int frameNo) const
 {
     if (mParentLayer)
-        return mLayerData->mTransform->matrix(frameNo) * mParentLayer->matrix(frameNo);
+        return mLayerData->mTransform->matrix(frameNo, mLayerData->autoOrient()) *
+               mParentLayer->matrix(frameNo);
     else
-        return mLayerData->mTransform->matrix(frameNo);
+        return mLayerData->mTransform->matrix(frameNo, mLayerData->autoOrient());
 }
 
 bool LOTLayerItem::visible() const
 {
-   if (frameNo() >= mLayerData->inFrame() && frameNo() < mLayerData->outFrame())
-      return true;
-   else
-      return false;
+    if (frameNo() >= mLayerData->inFrame() &&
+        frameNo() < mLayerData->outFrame())
+        return true;
+    else
+        return false;
 }
 
-
-
-LOTCompLayerItem::LOTCompLayerItem(LOTLayerData *layerModel):LOTLayerItem(layerModel)
+LOTCompLayerItem::LOTCompLayerItem(LOTLayerData *layerModel)
+    : LOTLayerItem(layerModel)
 {
-   for(auto i : mLayerData->mChildren) {
-      LOTLayerData *layerModel = dynamic_cast<LOTLayerData *>(i.get());
-      if (layerModel) {
-         LOTLayerItem *layerItem = LOTCompItem::createLayerItem(layerModel);
-         if (layerItem) {
-            mLayers.push_back(layerItem);
-            mLayerMap[layerItem->id()] = layerItem;
-         }
-      }
-   }
+    // 1. create layer item
+    for (auto &i : mLayerData->mChildren) {
+        LOTLayerData *layerModel = static_cast<LOTLayerData *>(i.get());
+        auto layerItem = LOTCompItem::createLayerItem(layerModel);
+        if (layerItem) mLayers.push_back(std::move(layerItem));
+    }
+
+    // 2. update parent layer
+    for (const auto &layer : mLayers) {
+        int id = layer->parentId();
+        if (id >= 0) {
+            auto search = std::find_if(mLayers.begin(), mLayers.end(),
+                            [id](const auto& val){ return val->id() == id;});
+            if (search != mLayers.end()) layer->setParentLayer((*search).get());
+        }
+        // update the precomp layer if its not the root layer.
+        if (!layerModel->root()) layer->setPrecompLayer(this);
+    }
 
-   //2. update parent layer
-   for(auto i : mLayers) {
-      int id = i->parentId();
-      if (id >=0) {
-         auto search = mLayerMap.find(id);
-         if (search != mLayerMap.end()) {
-           LOTLayerItem *parentLayer = search->second;
-           i->setParentLayer(parentLayer);
-         }
-      }
-      i->setPrecompLayer(this);
-   }
+    // 3. keep the layer in back-to-front order.
+    // as lottie model keeps the data in front-toback-order.
+    std::reverse(mLayers.begin(), mLayers.end());
 }
 
 void LOTCompLayerItem::updateStaticProperty()
 {
     LOTLayerItem::updateStaticProperty();
 
-    for(auto i : mLayers) {
-       i->updateStaticProperty();
+    for (const auto &layer : mLayers) {
+        layer->updateStaticProperty();
     }
 }
 
-void LOTCompLayerItem::render(VPainter *painter, const VRle &inheritMask)
+void LOTCompLayerItem::render(VPainter *painter, const VRle &inheritMask, const VRle &inheritMatte, LOTLayerItem *matteSource)
 {
-    VRle mask = inheritMask;
+    VRle matteRle;
+    if (matteSource) {
+        mDrawableList.clear();
+        matteSource->renderList(mDrawableList);
+        for (auto &i : mDrawableList) {
+            matteRle = matteRle + i->rle();
+        }
 
+        if (!inheritMatte.empty())
+            matteRle = matteRle & inheritMatte;
+    } else {
+        matteRle = inheritMatte;
+    }
+
+    VRle mask;
     if (hasMask()) {
-        if (mask.isEmpty())
-            mask = maskRle(painter->clipBoundingRect());
-        else
+        mask = maskRle(painter->clipBoundingRect());
+        if (!inheritMask.empty())
             mask = mask & inheritMask;
+        // if resulting mask is empty then return.
+        if (mask.empty())
+            return;
+    } else {
+        mask = inheritMask;
     }
 
-    for(auto i : mLayers) {
-       i->render(painter, mask);
-    }
-}
+    LOTLayerItem *matteLayer = nullptr;
+    for (const auto &layer : mLayers) {
+        if (!matteLayer && layer->hasMatte()) {
+            matteLayer = layer.get();
+            continue;
+        }
 
-LOTCompLayerItem::~LOTCompLayerItem()
-{
-    for(auto i : mLayers) {
-       delete i;
+        if (matteLayer) {
+            if (matteLayer->visible() && layer->visible())
+                matteLayer->render(painter, mask, matteRle, layer.get());
+            matteLayer = nullptr;
+        } else {
+            if (layer->visible())
+                layer->render(painter, mask, matteRle, nullptr);
+        }
     }
 }
 
 void LOTCompLayerItem::updateContent()
 {
-    // update the layer from back to front
-    for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
-       LOTLayerItem *layer = *i;
-       layer->update(frameNo(), combinedMatrix(), combinedAlpha());
+    for (const auto &layer : mLayers) {
+        layer->update( mLayerData->timeRemap(frameNo()) - mLayerData->startFrame(),
+                       combinedMatrix(), combinedAlpha());
     }
 }
 
@@ -551,39 +409,38 @@ void LOTCompLayerItem::renderList(std::vector<VDrawable *> &list)
 {
     if (!visible()) return;
 
-    // update the layer from back to front
-    for (auto i = mLayers.rbegin(); i != mLayers.rend(); ++i) {
-       LOTLayerItem *layer = *i;
-       layer->renderList(list);
+    for (const auto &layer : mLayers) {
+        layer->renderList(list);
     }
 }
 
-LOTSolidLayerItem::LOTSolidLayerItem(LOTLayerData *layerData):LOTLayerItem(layerData)
+LOTSolidLayerItem::LOTSolidLayerItem(LOTLayerData *layerData)
+    : LOTLayerItem(layerData)
 {
-
 }
 
 void LOTSolidLayerItem::updateContent()
 {
-   if (!mRenderNode) {
-      mRenderNode = std::unique_ptr<VDrawable>(new VDrawable());
-      mRenderNode->mType = VDrawable::Type::Fill;
-      mRenderNode->mFlag |= VDrawable::DirtyState::All;
-   }
-
-   if (flag() & DirtyFlagBit::Matrix) {
-       VPath path;
-       path.addRect(VRectF(0, 0, mLayerData->solidWidth(), mLayerData->solidHeight()));
-       path.transform(combinedMatrix());
-       mRenderNode->mFlag |= VDrawable::DirtyState::Path;
-       mRenderNode->mPath = path;
-   }
-   if (flag() & DirtyFlagBit::Alpha) {
-       LottieColor color = mLayerData->solidColor();
-       VBrush brush(color.toColor(combinedAlpha()));
-       mRenderNode->setBrush(brush);
-       mRenderNode->mFlag |= VDrawable::DirtyState::Brush;
-   }
+    if (!mRenderNode) {
+        mRenderNode = std::make_unique<LOTDrawable>();
+        mRenderNode->mType = VDrawable::Type::Fill;
+        mRenderNode->mFlag |= VDrawable::DirtyState::All;
+    }
+
+    if (flag() & DirtyFlagBit::Matrix) {
+        VPath path;
+        path.addRect(
+            VRectF(0, 0, mLayerData->solidWidth(), mLayerData->solidHeight()));
+        path.transform(combinedMatrix());
+        mRenderNode->mFlag |= VDrawable::DirtyState::Path;
+        mRenderNode->mPath = path;
+    }
+    if (flag() & DirtyFlagBit::Alpha) {
+        LottieColor color = mLayerData->solidColor();
+        VBrush      brush(color.toColor(combinedAlpha()));
+        mRenderNode->setBrush(brush);
+        mRenderNode->mFlag |= VDrawable::DirtyState::Brush;
+    }
 }
 
 void LOTSolidLayerItem::renderList(std::vector<VDrawable *> &list)
@@ -593,80 +450,89 @@ void LOTSolidLayerItem::renderList(std::vector<VDrawable *> &list)
     list.push_back(mRenderNode.get());
 }
 
-LOTNullLayerItem::LOTNullLayerItem(LOTLayerData *layerData):LOTLayerItem(layerData)
+LOTNullLayerItem::LOTNullLayerItem(LOTLayerData *layerData)
+    : LOTLayerItem(layerData)
 {
-
-}
-void LOTNullLayerItem::updateContent()
-{
-
 }
+void LOTNullLayerItem::updateContent() {}
 
-
-LOTShapeLayerItem::LOTShapeLayerItem(LOTLayerData *layerData):LOTLayerItem(layerData)
+LOTShapeLayerItem::LOTShapeLayerItem(LOTLayerData *layerData)
+    : LOTLayerItem(layerData)
 {
-    mRoot = new LOTContentGroupItem(nullptr);
+    mRoot = std::make_unique<LOTContentGroupItem>(nullptr);
     mRoot->addChildren(layerData);
-    mRoot->processPaintOperation();
-}
 
-LOTShapeLayerItem::~LOTShapeLayerItem()
-{
-    delete mRoot;
+    std::vector<LOTPathDataItem *> list;
+    mRoot->processPaintItems(list);
+
+    if (layerData->hasPathOperator()) {
+        list.clear();
+        mRoot->processTrimItems(list);
+    }
 }
 
-LOTContentItem * LOTShapeLayerItem::createContentItem(LOTData *contentData)
+std::unique_ptr<LOTContentItem>
+LOTShapeLayerItem::createContentItem(LOTData *contentData)
 {
-    switch(contentData->type()) {
-        case LOTData::Type::ShapeGroup: {
-            return new LOTContentGroupItem(static_cast<LOTShapeGroupData *>(contentData));
-            break;
-        }
-        case LOTData::Type::Rect: {
-            return new LOTRectItem(static_cast<LOTRectData *>(contentData));
-            break;
-        }
-        case LOTData::Type::Ellipse: {
-            return new LOTEllipseItem(static_cast<LOTEllipseData *>(contentData));
-            break;
-        }
-        case LOTData::Type::Shape: {
-            return new LOTShapeItem(static_cast<LOTShapeData *>(contentData));
-            break;
-        }
-        case LOTData::Type::Polystar: {
-            return new LOTPolystarItem(static_cast<LOTPolystarData *>(contentData));
-            break;
-        }
-        case LOTData::Type::Fill: {
-            return new LOTFillItem(static_cast<LOTFillData *>(contentData));
-            break;
-        }
-        case LOTData::Type::GFill: {
-            return new LOTGFillItem(static_cast<LOTGFillData *>(contentData));
-            break;
-        }
-        case LOTData::Type::Stroke: {
-            return new LOTStrokeItem(static_cast<LOTStrokeData *>(contentData));
-            break;
-        }
-        case LOTData::Type::GStroke: {
-            return new LOTGStrokeItem(static_cast<LOTGStrokeData *>(contentData));
-            break;
-        }
-        case LOTData::Type::Repeater: {
-                return new LOTRepeaterItem(static_cast<LOTRepeaterData *>(contentData));
-                break;
-            }
-        default:
-            return nullptr;
-            break;
+    switch (contentData->type()) {
+    case LOTData::Type::ShapeGroup: {
+        return std::make_unique<LOTContentGroupItem>(
+            static_cast<LOTShapeGroupData *>(contentData));
+        break;
+    }
+    case LOTData::Type::Rect: {
+        return std::make_unique<LOTRectItem>(static_cast<LOTRectData *>(contentData));
+        break;
+    }
+    case LOTData::Type::Ellipse: {
+        return std::make_unique<LOTEllipseItem>(static_cast<LOTEllipseData *>(contentData));
+        break;
+    }
+    case LOTData::Type::Shape: {
+        return std::make_unique<LOTShapeItem>(static_cast<LOTShapeData *>(contentData));
+        break;
+    }
+    case LOTData::Type::Polystar: {
+        return std::make_unique<LOTPolystarItem>(static_cast<LOTPolystarData *>(contentData));
+        break;
+    }
+    case LOTData::Type::Fill: {
+        return std::make_unique<LOTFillItem>(static_cast<LOTFillData *>(contentData));
+        break;
+    }
+    case LOTData::Type::GFill: {
+        return std::make_unique<LOTGFillItem>(static_cast<LOTGFillData *>(contentData));
+        break;
+    }
+    case LOTData::Type::Stroke: {
+        return std::make_unique<LOTStrokeItem>(static_cast<LOTStrokeData *>(contentData));
+        break;
+    }
+    case LOTData::Type::GStroke: {
+        return std::make_unique<LOTGStrokeItem>(static_cast<LOTGStrokeData *>(contentData));
+        break;
+    }
+    case LOTData::Type::Repeater: {
+        return std::make_unique<LOTRepeaterItem>(static_cast<LOTRepeaterData *>(contentData));
+        break;
+    }
+    case LOTData::Type::Trim: {
+        return std::make_unique<LOTTrimItem>(static_cast<LOTTrimData *>(contentData));
+        break;
+    }
+    default:
+        return nullptr;
+        break;
     }
 }
 
 void LOTShapeLayerItem::updateContent()
 {
-   mRoot->update(frameNo(), combinedMatrix(), combinedAlpha(), flag());
+    mRoot->update(frameNo(), combinedMatrix(), combinedAlpha(), flag());
+
+    if (mLayerData->hasPathOperator()) {
+        mRoot->applyTrim();
+    }
 }
 
 void LOTShapeLayerItem::renderList(std::vector<VDrawable *> &list)
@@ -675,306 +541,324 @@ void LOTShapeLayerItem::renderList(std::vector<VDrawable *> &list)
     mRoot->renderList(list);
 }
 
-LOTContentGroupItem::LOTContentGroupItem(LOTShapeGroupData *data):mData(data)
+LOTContentGroupItem::LOTContentGroupItem(LOTShapeGroupData *data) : mData(data)
 {
-   addChildren(mData);
+    addChildren(mData);
 }
 
 void LOTContentGroupItem::addChildren(LOTGroupData *data)
 {
-   if (!data) return;
-
-   for(auto i : data->mChildren) {
-      LOTData *data = i.get();
-      LOTContentItem *content = LOTShapeLayerItem::createContentItem(data);
-      if (content)
-         mContents.push_back(content);
-   }
-}
+    if (!data) return;
 
-LOTContentGroupItem::~LOTContentGroupItem()
-{
-    for(auto i : mContents) {
-        delete i;
+    for (auto &i : data->mChildren) {
+        auto content = LOTShapeLayerItem::createContentItem(i.get());
+        if (content) {
+            content->setParent(this);
+            mContents.push_back(std::move(content));
+        }
     }
-}
 
+    // keep the content in back-to-front order.
+    std::reverse(mContents.begin(), mContents.end());
+}
 
-void LOTContentGroupItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
+void LOTContentGroupItem::update(int frameNo, const VMatrix &parentMatrix,
+                                 float parentAlpha, const DirtyFlag &flag)
 {
-   VMatrix m = parentMatrix;
-   float alpha = parentAlpha;
-   DirtyFlag newFlag = flag;
+    VMatrix   m = parentMatrix;
+    float     alpha = parentAlpha;
+    DirtyFlag newFlag = flag;
 
-   if (mData) {
-      // update the matrix and the flag
-      if ((flag & DirtyFlagBit::Matrix) || !mData->mTransform->staticMatrix() ) {
-         newFlag |= DirtyFlagBit::Matrix;
-      }
-      m = mData->mTransform->matrix(frameNo) * parentMatrix;
-      alpha *= mData->mTransform->opacity(frameNo);
+    if (mData) {
+        // update the matrix and the flag
+        if ((flag & DirtyFlagBit::Matrix) ||
+            !mData->mTransform->staticMatrix()) {
+            newFlag |= DirtyFlagBit::Matrix;
+        }
+        m = mData->mTransform->matrix(frameNo);
+        m *= parentMatrix;
+        alpha *= mData->mTransform->opacity(frameNo);
 
-      if (!vCompare(alpha, parentAlpha)) {
-         newFlag |= DirtyFlagBit::Alpha;
-      }
-   }
+        if (!vCompare(alpha, parentAlpha)) {
+            newFlag |= DirtyFlagBit::Alpha;
+        }
+    }
+
+    mMatrix = m;
 
-   for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
-      (*i)->update(frameNo, m, alpha, newFlag);
-   }
+    for (const auto &content : mContents) {
+        content->update(frameNo, m, alpha, newFlag);
+    }
 }
 
-void LOTContentGroupItem::renderList(std::vector<VDrawable *> &list)
+void LOTContentGroupItem::applyTrim()
 {
     for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
-       (*i)->renderList(list);
+        auto content = (*i).get();
+        if (auto trim = dynamic_cast<LOTTrimItem *>(content)) {
+            trim->update();
+        } else if (auto group = dynamic_cast<LOTContentGroupItem *>(content)) {
+            group->applyTrim();
+        }
     }
 }
 
-void LOTContentGroupItem::processPaintOperation()
+void LOTContentGroupItem::renderList(std::vector<VDrawable *> &list)
 {
-   std::vector<LOTPaintDataItem *> list;
-   paintOperationHelper(list);
+    for (const auto &content : mContents) {
+        content->renderList(list);
+    }
 }
 
-void LOTContentGroupItem::paintOperationHelper(std::vector<LOTPaintDataItem *> &list)
+void LOTContentGroupItem::processPaintItems(
+    std::vector<LOTPathDataItem *> &list)
 {
-   int curOpCount = list.size();
-   for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
-      auto child = *i;
-      if (auto pathNode = dynamic_cast<LOTPathDataItem *>(child)) {
-         // the node is a path data node add the paint operation list to it.
-         pathNode->addPaintOperation(list, curOpCount);
-      } else if (auto paintNode = dynamic_cast<LOTPaintDataItem *>(child)) {
-         // add it to the paint operation list
-         list.push_back(paintNode);
-      } else if (auto groupNode = dynamic_cast<LOTContentGroupItem *>(child)) {
-         // update the groups node with current list
-         groupNode->paintOperationHelper(list);
-      }
-   }
-   list.erase(list.begin() + curOpCount, list.end());
+    int curOpCount = list.size();
+    for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
+        auto content = (*i).get();
+        if (auto pathNode = dynamic_cast<LOTPathDataItem *>(content)) {
+            // add it to the list
+            list.push_back(pathNode);
+        } else if (auto paintNode = dynamic_cast<LOTPaintDataItem *>(content)) {
+            // the node is a paint data node update the path list of the paint item.
+            paintNode->addPathItems(list, curOpCount);
+        } else if (auto groupNode =
+                       dynamic_cast<LOTContentGroupItem *>(content)) {
+            // update the groups node with current list
+            groupNode->processPaintItems(list);
+        }
+    }
 }
 
-void LOTPathDataItem::addPaintOperation(std::vector<LOTPaintDataItem *> &list, int externalCount)
+void LOTContentGroupItem::processTrimItems(
+    std::vector<LOTPathDataItem *> &list)
 {
-    for(auto paintItem : list) {
-      bool sameGroup = (externalCount-- > 0) ? false : true;
-      mNodeList.push_back(std::unique_ptr<VDrawable>(new VDrawable()));
-      mRenderList.push_back(LOTRenderNode(this, paintItem, mNodeList.back().get(), sameGroup));
+    int curOpCount = list.size();
+    for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
+        auto content = (*i).get();
+        if (auto pathNode = dynamic_cast<LOTPathDataItem *>(content)) {
+            // add it to the list
+            list.push_back(pathNode);
+        } else if (auto trimNode = dynamic_cast<LOTTrimItem *>(content)) {
+            // the node is a paint data node update the path list of the paint item.
+            trimNode->addPathItems(list, curOpCount);
+        } else if (auto groupNode =
+                       dynamic_cast<LOTContentGroupItem *>(content)) {
+            // update the groups node with current list
+            groupNode->processTrimItems(list);
+        }
     }
 }
 
-
-void LOTPathDataItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
+void LOTPathDataItem::update(int frameNo, const VMatrix &,
+                             float, const DirtyFlag &flag)
 {
-   mPathChanged = false;
-   mCombinedAlpha = parentAlpha;
+    mPathChanged = false;
 
-   // 1. update the local path if needed
-   if (!(mInit && mStaticPath)) {
-      mLocalPath = getPath(frameNo);
-      mInit = true;
-      mPathChanged = true;
-   }
-
-   // 2. apply path operation if needed
-   // TODO
+    // 1. update the local path if needed
+    if (hasChanged(frameNo)) {
+        updatePath(mLocalPath, frameNo);
+        mPathChanged = true;
+        mNeedUpdate = true;
+    }
 
-   // 3. compute the final path with parentMatrix
-   if ((flag & DirtyFlagBit::Matrix) || mPathChanged) {
-      mFinalPath = mLocalPath;
-      mFinalPath.transform(parentMatrix);
-      mPathChanged = true;
-   }
+    mTemp = mLocalPath;
 
-   // 2. update the rendernode list
-   for (const auto &i : mRenderList) {
-      i.drawable->mFlag = VDrawable::DirtyState::None;
-      i.paintNodeRef->updateRenderNode(i.pathNodeRef, i.drawable, i.sameGroup);
-      if (mPathChanged) {
-          i.drawable->mPath = mFinalPath;
-          i.drawable->mFlag |= VDrawable::DirtyState::Path;
-      }
-   }
+    // 3. compute the final path with parentMatrix
+    if ((flag & DirtyFlagBit::Matrix) || mPathChanged) {
+        mPathChanged = true;
+    }
 }
 
-void LOTPathDataItem::renderList(std::vector<VDrawable *> &list)
+const VPath & LOTPathDataItem::finalPath()
 {
-   for (const auto &i : mRenderList) {
-       list.push_back(i.drawable);
-   }
+    if (mPathChanged || mNeedUpdate) {
+        mFinalPath.clone(mTemp);
+        mFinalPath.transform(static_cast<LOTContentGroupItem *>(parent())->matrix());
+        mNeedUpdate = false;
+    }
+    return mFinalPath;
 }
-
-VPath LOTPathDataItem::path() const
+LOTRectItem::LOTRectItem(LOTRectData *data)
+    : LOTPathDataItem(data->isStatic()), mData(data)
 {
-   return mFinalPath;
 }
 
-
-LOTRectItem::LOTRectItem(LOTRectData *data):LOTPathDataItem(data->isStatic()),mData(data)
+void LOTRectItem::updatePath(VPath& path, int frameNo)
 {
+    VPointF pos = mData->mPos.value(frameNo);
+    VPointF size = mData->mSize.value(frameNo);
+    float   roundness = mData->mRound.value(frameNo);
+    VRectF  r(pos.x() - size.x() / 2, pos.y() - size.y() / 2, size.x(),
+             size.y());
+
+    path.reset();
+    path.addRoundRect(r, roundness, mData->direction());
+    updateCache(frameNo, pos, size, roundness);
 }
 
-VPath LOTRectItem::getPath(int frameNo)
+LOTEllipseItem::LOTEllipseItem(LOTEllipseData *data)
+    : LOTPathDataItem(data->isStatic()), mData(data)
 {
-   VPointF pos = mData->mPos.value(frameNo);
-   VPointF size = mData->mSize.value(frameNo);
-   float radius = mData->mRound.value(frameNo);
-   VRectF r(pos.x() - size.x()/2, pos.y() - size.y()/2, size.x(), size.y());
-
-   VPath path;
-   path.addRoundRect(r, radius, radius, mData->direction());
-
-   return path;
 }
 
-LOTEllipseItem::LOTEllipseItem(LOTEllipseData *data):LOTPathDataItem(data->isStatic()),mData(data)
+void LOTEllipseItem::updatePath(VPath& path, int frameNo)
 {
+    VPointF pos = mData->mPos.value(frameNo);
+    VPointF size = mData->mSize.value(frameNo);
+    VRectF  r(pos.x() - size.x() / 2, pos.y() - size.y() / 2, size.x(),
+             size.y());
 
+    path.reset();
+    path.addOval(r, mData->direction());
+    updateCache(frameNo, pos, size);
 }
 
-VPath LOTEllipseItem::getPath(int frameNo)
+LOTShapeItem::LOTShapeItem(LOTShapeData *data)
+    : LOTPathDataItem(data->isStatic()), mData(data)
 {
-   VPointF pos = mData->mPos.value(frameNo);
-   VPointF size = mData->mSize.value(frameNo);
-   VRectF r(pos.x() - size.x()/2, pos.y() - size.y()/2, size.x(), size.y());
-
-   VPath path;
-   path.addOval(r, mData->direction());
-
-   return path;
 }
 
-LOTShapeItem::LOTShapeItem(LOTShapeData *data):LOTPathDataItem(data->isStatic()),mData(data)
+void LOTShapeItem::updatePath(VPath& path, int frameNo)
 {
-
+    mData->mShape.value(frameNo).toPath(path);
 }
 
-VPath LOTShapeItem::getPath(int frameNo)
+LOTPolystarItem::LOTPolystarItem(LOTPolystarData *data)
+    : LOTPathDataItem(data->isStatic()), mData(data)
 {
-    LottieShapeData shapeData = mData->mShape.value(frameNo);
+}
 
-    if (shapeData.mPoints.empty())
-     return VPath();
+void LOTPolystarItem::updatePath(VPath& path, int frameNo)
+{
+    VPointF pos = mData->mPos.value(frameNo);
+    float   points = mData->mPointCount.value(frameNo);
+    float   innerRadius = mData->mInnerRadius.value(frameNo);
+    float   outerRadius = mData->mOuterRadius.value(frameNo);
+    float   innerRoundness = mData->mInnerRoundness.value(frameNo);
+    float   outerRoundness = mData->mOuterRoundness.value(frameNo);
+    float   rotation = mData->mRotation.value(frameNo);
 
-    VPath path;
+    path.reset();
+    VMatrix m;
 
-    int size = shapeData.mPoints.size();
-    const VPointF *points = shapeData.mPoints.data();
-    path.moveTo(points[0]);
-    for (int i = 1 ; i < size; i+=3) {
-       path.cubicTo(points[i], points[i+1], points[i+2]);
+    if (mData->mType == LOTPolystarData::PolyType::Star) {
+        path.addPolystar(points, innerRadius, outerRadius, innerRoundness,
+                         outerRoundness, 0.0, 0.0, 0.0, mData->direction());
+    } else {
+        path.addPolygon(points, outerRadius, outerRoundness, 0.0, 0.0, 0.0,
+                        mData->direction());
     }
-    if (shapeData.mClosed)
-      path.close();
 
-   return path;
+    m.translate(pos.x(), pos.y()).rotate(rotation);
+    m.rotate(rotation);
+    path.transform(m);
+    updateCache(frameNo, pos, points, innerRadius, outerRadius,
+                innerRoundness, outerRoundness, rotation);
 }
 
+/*
+ * PaintData Node handling
+ *
+ */
+LOTPaintDataItem::LOTPaintDataItem(bool staticContent):mDrawable(std::make_unique<LOTDrawable>()),
+                                                       mStaticContent(staticContent){}
 
-LOTPolystarItem::LOTPolystarItem(LOTPolystarData *data):LOTPathDataItem(data->isStatic()),mData(data)
+void LOTPaintDataItem::update(int frameNo, const VMatrix &parentMatrix,
+                              float parentAlpha, const DirtyFlag &flag)
 {
+    mRenderNodeUpdate = true;
+    mParentAlpha = parentAlpha;
+    mFlag = flag;
+    mFrameNo = frameNo;
 
+    updateContent(frameNo);
 }
 
-VPath LOTPolystarItem::getPath(int frameNo)
+void LOTPaintDataItem::updateRenderNode()
 {
-   VPointF pos = mData->mPos.value(frameNo);
-   float points = mData->mPointCount.value(frameNo);
-   float innerRadius = mData->mInnerRadius.value(frameNo);
-   float outerRadius = mData->mOuterRadius.value(frameNo);
-   float innerRoundness = mData->mInnerRoundness.value(frameNo);
-   float outerRoundness = mData->mOuterRoundness.value(frameNo);
-   float rotation = mData->mRotation.value(frameNo);
-
-   VPath path;
-   VMatrix m;
-
-   if (mData->mType == LOTPolystarData::PolyType::Star) {
-        path.addPolystarStar(0.0, 0.0, 0.0, points,
-                             innerRadius, outerRadius,
-                             innerRoundness, outerRoundness,
-                             mData->direction());
-   } else {
-        path.addPolystarPolygon(0.0, 0.0, 0.0, points,
-                                outerRadius, outerRoundness,
-                                mData->direction());
-   }
+    bool dirty = false;
+    for (auto &i : mPathItems) {
+        if (i->dirty()) {
+            dirty = true;
+            break;
+        }
+    }
 
-   m.translate(pos.x(), pos.y()).rotate(rotation);
-   m.rotate(rotation);
-   path.transform(m);
+    if (dirty) {
+        mPath.reset();
 
-   return path;
+        for (auto &i : mPathItems) {
+            mPath.addPath(i->finalPath());
+        }
+        mDrawable->setPath(mPath);
+    } else {
+        if (mDrawable->mFlag & VDrawable::DirtyState::Path)
+            mDrawable->mPath = mPath;
+    }
 }
 
+void LOTPaintDataItem::renderList(std::vector<VDrawable *> &list)
+{
+    if (mRenderNodeUpdate) {
+        updateRenderNode();
+        LOTPaintDataItem::updateRenderNode();
+        mRenderNodeUpdate = false;
+    }
+    list.push_back(mDrawable.get());
+}
 
 
-/*
- * PaintData Node handling
- *
- */
-
-void LOTPaintDataItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
+void LOTPaintDataItem::addPathItems(std::vector<LOTPathDataItem *> &list, int startOffset)
 {
-   mContentChanged = false;
-   mParentAlpha = parentAlpha;
-   mParentMatrix = parentMatrix;
-   mFlag = flag;
-   mFrameNo = frameNo;
-   // 1. update the local content if needed
-  // if (!(mInit && mStaticContent)) {
-      mInit = true;
-      updateContent(frameNo);
-      mContentChanged = true;
-  // }
+    std::copy(list.begin() + startOffset, list.end(), back_inserter(mPathItems));
 }
 
 
-LOTFillItem::LOTFillItem(LOTFillData *data):LOTPaintDataItem(data->isStatic()),mData(data)
+LOTFillItem::LOTFillItem(LOTFillData *data)
+    : LOTPaintDataItem(data->isStatic()), mData(data)
 {
 }
 
 void LOTFillItem::updateContent(int frameNo)
 {
-   LottieColor c = mData->mColor.value(frameNo);
-   float opacity = mData->opacity(frameNo);
-   mColor = c.toColor(opacity);
-   mFillRule = mData->fillRule();
+    LottieColor c = mData->mColor.value(frameNo);
+    float       opacity = mData->opacity(frameNo);
+    mColor = c.toColor(opacity);
+    mFillRule = mData->fillRule();
 }
 
-void LOTFillItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
+void LOTFillItem::updateRenderNode()
 {
     VColor color = mColor;
-    if (sameParent)
-      color.setAlpha(color.a * pathNode->combinedAlpha());
-    else
-      color.setAlpha(color.a  * parentAlpha() * pathNode->combinedAlpha());
+
+    color.setAlpha(color.a * parentAlpha());
     VBrush brush(color);
-    drawable->setBrush(brush);
-    drawable->setFillRule(mFillRule);
+    mDrawable->setBrush(brush);
+    mDrawable->setFillRule(mFillRule);
 }
 
-
-LOTGFillItem::LOTGFillItem(LOTGFillData *data):LOTPaintDataItem(data->isStatic()),mData(data)
+LOTGFillItem::LOTGFillItem(LOTGFillData *data)
+    : LOTPaintDataItem(data->isStatic()), mData(data)
 {
 }
 
 void LOTGFillItem::updateContent(int frameNo)
 {
     mData->update(mGradient, frameNo);
-    mGradient->mMatrix = mParentMatrix;
+    mGradient->mMatrix = static_cast<LOTContentGroupItem *>(parent())->matrix();
     mFillRule = mData->fillRule();
 }
 
-void LOTGFillItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
+void LOTGFillItem::updateRenderNode()
 {
-    drawable->setBrush(VBrush(mGradient.get()));
-    drawable->setFillRule(mFillRule);
+    mGradient->setAlpha(parentAlpha());
+    mDrawable->setBrush(VBrush(mGradient.get()));
+    mDrawable->setFillRule(mFillRule);
 }
 
-LOTStrokeItem::LOTStrokeItem(LOTStrokeData *data):LOTPaintDataItem(data->isStatic()),mData(data)
+LOTStrokeItem::LOTStrokeItem(LOTStrokeData *data)
+    : LOTPaintDataItem(data->isStatic()), mData(data)
 {
     mDashArraySize = 0;
 }
@@ -982,7 +866,7 @@ LOTStrokeItem::LOTStrokeItem(LOTStrokeData *data):LOTPaintDataItem(data->isStati
 void LOTStrokeItem::updateContent(int frameNo)
 {
     LottieColor c = mData->mColor.value(frameNo);
-    float opacity = mData->opacity(frameNo);
+    float       opacity = mData->opacity(frameNo);
     mColor = c.toColor(opacity);
     mCap = mData->capStyle();
     mJoin = mData->joinStyle();
@@ -996,33 +880,34 @@ void LOTStrokeItem::updateContent(int frameNo)
 static float getScale(const VMatrix &matrix)
 {
     constexpr float SQRT_2 = 1.41421;
-    VPointF p1(0,0);
-    VPointF p2(SQRT_2,SQRT_2);
+    VPointF         p1(0, 0);
+    VPointF         p2(SQRT_2, SQRT_2);
     p1 = matrix.map(p1);
     p2 = matrix.map(p2);
     VPointF final = p2 - p1;
 
-    return std::sqrt( final.x() * final.x() + final.y() * final.y());
+    return std::sqrt(final.x() * final.x() + final.y() * final.y()) / 2.0;
 }
 
-void LOTStrokeItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
+void LOTStrokeItem::updateRenderNode()
 {
     VColor color = mColor;
-    if (sameParent)
-      color.setAlpha(color.a * pathNode->combinedAlpha());
-    else
-      color.setAlpha(color.a  * parentAlpha() * pathNode->combinedAlpha());
 
+    color.setAlpha(color.a * parentAlpha());
     VBrush brush(color);
-    drawable->setBrush(brush);
-
-    drawable->setStrokeInfo(mCap, mJoin, mMiterLimit,  mWidth * getScale(mParentMatrix));
+    mDrawable->setBrush(brush);
+    float scale = getScale(static_cast<LOTContentGroupItem *>(parent())->matrix());
+    mDrawable->setStrokeInfo(mCap, mJoin, mMiterLimit,
+                            mWidth * scale);
     if (mDashArraySize) {
-        drawable->setDashInfo(mDashArray, mDashArraySize);
+        for (int i = 0 ; i < mDashArraySize ; i++)
+            mDashArray[i] *= scale;
+        mDrawable->setDashInfo(mDashArray, mDashArraySize);
     }
 }
 
-LOTGStrokeItem::LOTGStrokeItem(LOTGStrokeData *data):LOTPaintDataItem(data->isStatic()),mData(data)
+LOTGStrokeItem::LOTGStrokeItem(LOTGStrokeData *data)
+    : LOTPaintDataItem(data->isStatic()), mData(data)
 {
     mDashArraySize = 0;
 }
@@ -1030,7 +915,7 @@ LOTGStrokeItem::LOTGStrokeItem(LOTGStrokeData *data):LOTPaintDataItem(data->isSt
 void LOTGStrokeItem::updateContent(int frameNo)
 {
     mData->update(mGradient, frameNo);
-    mGradient->mMatrix = mParentMatrix;
+    mGradient->mMatrix = static_cast<LOTContentGroupItem *>(parent())->matrix();
     mCap = mData->capStyle();
     mJoin = mData->joinStyle();
     mMiterLimit = mData->meterLimit();
@@ -1040,32 +925,281 @@ void LOTGStrokeItem::updateContent(int frameNo)
     }
 }
 
-void LOTGStrokeItem::updateRenderNode(LOTPathDataItem *pathNode, VDrawable *drawable, bool sameParent)
+void LOTGStrokeItem::updateRenderNode()
 {
-    drawable->setBrush(VBrush(mGradient.get()));
-    drawable->setStrokeInfo(mCap, mJoin, mMiterLimit,  mWidth * getScale(mParentMatrix));
+    float scale = getScale(mGradient->mMatrix);
+    mGradient->setAlpha(parentAlpha());
+    mDrawable->setBrush(VBrush(mGradient.get()));
+    mDrawable->setStrokeInfo(mCap, mJoin, mMiterLimit,
+                            mWidth * scale);
     if (mDashArraySize) {
-        drawable->setDashInfo(mDashArray, mDashArraySize);
+        for (int i = 0 ; i < mDashArraySize ; i++)
+            mDashArray[i] *= scale;
+        mDrawable->setDashInfo(mDashArray, mDashArraySize);
     }
 }
 
-LOTTrimItem::LOTTrimItem(LOTTrimData *data):mData(data)
+LOTTrimItem::LOTTrimItem(LOTTrimData *data) : mData(data) {}
+
+void LOTTrimItem::update(int frameNo, const VMatrix &/*parentMatrix*/,
+                         float /*parentAlpha*/, const DirtyFlag &/*flag*/)
 {
+    mDirty = false;
 
+    if (mCache.mFrameNo == frameNo) return;
+
+    float   start = mData->start(frameNo);
+    float   end = mData->end(frameNo);
+    float   offset = mData->offset(frameNo);
+
+    if (!(vCompare(mCache.mStart, start) && vCompare(mCache.mEnd, end) &&
+          vCompare(mCache.mOffset, offset))) {
+        mDirty = true;
+        mCache.mStart = start;
+        mCache.mEnd = end;
+        mCache.mOffset = offset;
+    }
+    mCache.mFrameNo = frameNo;
 }
 
-LOTRepeaterItem::LOTRepeaterItem(LOTRepeaterData *data):mData(data)
+void LOTTrimItem::update()
 {
+    // when both path and trim are not dirty
+    if (!(mDirty || pathDirty())) return;
+
+    if (vCompare(mCache.mStart, mCache.mEnd)) {
+        for (auto &i : mPathItems) {
+            i->updatePath(VPath());
+        }
+        return;
+    }
+
+
+    if (mData->type() == LOTTrimData::TrimType::Simultaneously) {
+        for (auto &i : mPathItems) {
+            VPathMesure pm;
+            pm.setStart(mCache.mStart);
+            pm.setEnd(mCache.mEnd);
+            pm.setOffset(mCache.mOffset);
+            i->updatePath(pm.trim(i->localPath()));
+        }
+    } else { // LOTTrimData::TrimType::Individually
+        float totalLength = 0.0;
+        for (auto &i : mPathItems) {
+            totalLength += i->localPath().length();
+        }
+        float offset = totalLength * mCache.mOffset;
+        float start = totalLength * mCache.mStart;
+        float end  = totalLength * mCache.mEnd;
+        start += offset;
+        end +=offset;
+        // normalize start, end value to 0 - totalLength
+        if (fabs(start) > totalLength) start = fmod(start, totalLength);
+        if (fabs(end) > totalLength) end = fmod(end, totalLength);
+
+        if (start >= 0 && end >= 0) {
+            if (start > end) std::swap(start, end);
+        } else if ( start < 0 && end < 0) {
+            start += totalLength;
+            end += totalLength;
+            if (start > end) std::swap(start, end);
+        } else {
+            // one is +ve and one is -ve so the
+            // segment will be wrapped from end segment to start segment.
+            // we need to split it in two segment.
+            //@TODO
+            return;
+        }
+
+        if (start < end ) {
+            float curLen = 0.0;
+            for (auto &i : mPathItems) {
+                if (curLen > end) {
+                    // update with empty path.
+                    i->updatePath(VPath());
+                    continue;
+                }
+                float len = i->localPath().length();
+
+                if (curLen < start  && curLen + len < start) {
+                    curLen += len;
+                    // update with empty path.
+                    i->updatePath(VPath());
+                    continue;
+                } else if (start <= curLen && end >= curLen + len) {
+                    // inside segment
+                    curLen += len;
+                    continue;
+                } else {
+                    float local_start = start > curLen ? start - curLen : 0;
+                    local_start /= len;
+                    float local_end = curLen + len < end ? len : end - curLen;
+                    local_end /= len;
+                    VPathMesure pm;
+                    pm.setStart(local_start);
+                    pm.setEnd(local_end);
+                    VPath p = pm.trim(i->localPath());
+                    i->updatePath(p);
+                    curLen += len;
+                }
+            }
+        }
+    }
 
 }
 
-void LOTRepeaterItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
+
+void LOTTrimItem::addPathItems(std::vector<LOTPathDataItem *> &list, int startOffset)
 {
+    std::copy(list.begin() + startOffset, list.end(), back_inserter(mPathItems));
+}
+
+
+LOTRepeaterItem::LOTRepeaterItem(LOTRepeaterData *data) : mData(data) {}
 
+void LOTRepeaterItem::update(int /*frameNo*/, const VMatrix &/*parentMatrix*/,
+                             float /*parentAlpha*/, const DirtyFlag &/*flag*/)
+{
 }
 
-void LOTRepeaterItem::renderList(std::vector<VDrawable *> &list)
+void LOTRepeaterItem::renderList(std::vector<VDrawable *> &/*list*/) {}
+
+static void updateGStops(LOTNode *n, const VGradient *grad)
 {
+    if (grad->mStops.size() != n->mGradient.stopCount) {
+        if (n->mGradient.stopCount)
+            free(n->mGradient.stopPtr);
+        n->mGradient.stopCount = grad->mStops.size();
+        n->mGradient.stopPtr = (LOTGradientStop *) malloc(n->mGradient.stopCount * sizeof(LOTGradientStop));
+    }
+
+    LOTGradientStop *ptr = n->mGradient.stopPtr;
+    for (const auto &i : grad->mStops) {
+        ptr->pos = i.first;
+        ptr->a = i.second.alpha() * grad->alpha();
+        ptr->r = i.second.red();
+        ptr->g = i.second.green();
+        ptr->b = i.second.blue();
+        ptr++;
+    }
 
 }
 
+void LOTDrawable::sync()
+{
+    if (!mCNode) {
+        mCNode = std::make_unique<LOTNode>();
+        mCNode->mGradient.stopPtr = nullptr;
+        mCNode->mGradient.stopCount = 0;
+    }
+
+    mCNode->mFlag = ChangeFlagNone;
+    if (mFlag & DirtyState::None) return;
+
+    if (mFlag & DirtyState::Path) {
+        const std::vector<VPath::Element> &elm = mPath.elements();
+        const std::vector<VPointF> &       pts = mPath.points();
+        const float *ptPtr = reinterpret_cast<const float *>(pts.data());
+        const char * elmPtr = reinterpret_cast<const char *>(elm.data());
+        mCNode->mPath.elmPtr = elmPtr;
+        mCNode->mPath.elmCount = elm.size();
+        mCNode->mPath.ptPtr = ptPtr;
+        mCNode->mPath.ptCount = 2 * pts.size();
+        mCNode->mFlag |= ChangeFlagPath;
+    }
+
+    if (mStroke.enable) {
+        mCNode->mStroke.width = mStroke.width;
+        mCNode->mStroke.meterLimit = mStroke.meterLimit;
+        mCNode->mStroke.enable = 1;
+
+        switch (mFillRule) {
+        case FillRule::EvenOdd:
+            mCNode->mFillRule = LOTFillRule::FillEvenOdd;
+            break;
+        default:
+            mCNode->mFillRule = LOTFillRule::FillWinding;
+            break;
+        }
+
+        switch (mStroke.cap) {
+        case CapStyle::Flat:
+            mCNode->mStroke.cap = LOTCapStyle::CapFlat;
+            break;
+        case CapStyle::Square:
+            mCNode->mStroke.cap = LOTCapStyle::CapSquare;
+            break;
+        case CapStyle::Round:
+            mCNode->mStroke.cap = LOTCapStyle::CapRound;
+            break;
+        default:
+            mCNode->mStroke.cap = LOTCapStyle::CapFlat;
+            break;
+        }
+
+        switch (mStroke.join) {
+        case JoinStyle::Miter:
+            mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
+            break;
+        case JoinStyle::Bevel:
+            mCNode->mStroke.join = LOTJoinStyle::JoinBevel;
+            break;
+        case JoinStyle::Round:
+            mCNode->mStroke.join = LOTJoinStyle::JoinRound;
+            break;
+        default:
+            mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
+            break;
+        }
+
+        mCNode->mStroke.dashArray = mStroke.mDash.data();
+        mCNode->mStroke.dashArraySize = mStroke.mDash.size();
+
+    } else {
+        mCNode->mStroke.enable = 0;
+    }
+
+    switch (mBrush.type()) {
+    case VBrush::Type::Solid:
+        mCNode->mBrushType = LOTBrushType::BrushSolid;
+        mCNode->mColor.r = mBrush.mColor.r;
+        mCNode->mColor.g = mBrush.mColor.g;
+        mCNode->mColor.b = mBrush.mColor.b;
+        mCNode->mColor.a = mBrush.mColor.a;
+        break;
+    case VBrush::Type::LinearGradient: {
+        mCNode->mBrushType = LOTBrushType::BrushGradient;
+        mCNode->mGradient.type = LOTGradientType::GradientLinear;
+        VPointF s = mBrush.mGradient->mMatrix.map({mBrush.mGradient->linear.x1,
+                                                   mBrush.mGradient->linear.y1});
+        VPointF e = mBrush.mGradient->mMatrix.map({mBrush.mGradient->linear.x2,
+                                                   mBrush.mGradient->linear.y2});
+        mCNode->mGradient.start.x = s.x();
+        mCNode->mGradient.start.y = s.y();
+        mCNode->mGradient.end.x = e.x();
+        mCNode->mGradient.end.y = e.y();
+        updateGStops(mCNode.get(), mBrush.mGradient);
+        break;
+    }
+    case VBrush::Type::RadialGradient: {
+        mCNode->mBrushType = LOTBrushType::BrushGradient;
+        mCNode->mGradient.type = LOTGradientType::GradientRadial;
+        VPointF c = mBrush.mGradient->mMatrix.map({mBrush.mGradient->radial.cx,
+                                                   mBrush.mGradient->radial.cy});
+        VPointF f = mBrush.mGradient->mMatrix.map({mBrush.mGradient->radial.fx,
+                                                   mBrush.mGradient->radial.fy});
+        mCNode->mGradient.center.x = c.x();
+        mCNode->mGradient.center.y = c.y();
+        mCNode->mGradient.focal.x = f.x();
+        mCNode->mGradient.focal.y = f.y();
+
+        float scale = getScale(mBrush.mGradient->mMatrix);
+        mCNode->mGradient.cradius = mBrush.mGradient->radial.cradius * scale;
+        mCNode->mGradient.fradius = mBrush.mGradient->radial.fradius * scale;
+        updateGStops(mCNode.get(), mBrush.mGradient);
+        break;
+    }
+    default:
+        break;
+    }
+}