lottie/trim: fix regression, always modify the path in trim::update() call.
[platform/core/uifw/lottie-player.git] / src / lottie / lottieitem.cpp
index c3ff6a7..a882a0a 100644 (file)
@@ -1,5 +1,6 @@
 #include "lottieitem.h"
 #include <cmath>
+#include <algorithm>
 #include "vbitmap.h"
 #include "vdasher.h"
 #include "vpainter.h"
 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();
-    }
-
+    mRootLayer = createLayerItem(mCompData->mRootLayer.get());
+    mRootLayer->updateStaticProperty();
     mViewSize = mCompData->size();
 }
 
-LOTCompItem::~LOTCompItem()
-{
-    for (auto i : mLayers) {
-        delete i;
-    }
-}
-
-LOTLayerItem *LOTCompItem::createLayerItem(LOTLayerData *layerData)
+std::unique_ptr<LOTLayerItem>
+LOTCompItem::createLayerItem(LOTLayerData *layerData)
 {
     switch (layerData->mLayerType) {
     case LayerType::Precomp: {
-        return new LOTCompLayerItem(layerData);
+        return std::make_unique<LOTCompLayerItem>(layerData);
         break;
     }
     case LayerType::Solid: {
-        return new LOTSolidLayerItem(layerData);
+        return std::make_unique<LOTSolidLayerItem>(layerData);
         break;
     }
     case LayerType::Shape: {
-        return new LOTShapeLayerItem(layerData);
+        return std::make_unique<LOTShapeLayerItem>(layerData);
         break;
     }
     case LayerType::Null: {
-        return new LOTNullLayerItem(layerData);
+        return std::make_unique<LOTNullLayerItem>(layerData);
         break;
     }
     default:
@@ -111,14 +80,9 @@ bool LOTCompItem::update(int frameNo)
     float ty = (viewPort.height() - viewBox.height() * scale) * 0.5;
 
     VMatrix m;
-    m.scale(scale, scale).translate(tx, ty);
+    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;
@@ -127,16 +91,13 @@ bool LOTCompItem::update(int frameNo)
 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) {
+    for (auto &i : mDrawableList) {
         LOTDrawable *lotDrawable = static_cast<LOTDrawable *>(i);
         lotDrawable->sync();
-        mRenderList.push_back(&lotDrawable->mCNode);
+        mRenderList.push_back(lotDrawable->mCNode.get());
     }
 }
 
@@ -145,33 +106,32 @@ 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,
+    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()) {
+        if (mLocalPath.empty()) {
             mData->mShape.value(frameNo).toPath(mLocalPath);
         }
     } else {
@@ -184,7 +144,8 @@ void LOTMaskItem::update(int frameNo, const VMatrix &parentMatrix,
     VPath path = mLocalPath;
     path.transform(parentMatrix);
 
-    mRleTask = VRaster::instance().generateFillInfo(path, std::move(mRle));
+    mRleTask = VRaster::generateFillInfo(std::move(path), std::move(mRle));
+    mRle = VRle();
 }
 
 VRle LOTMaskItem::rle()
@@ -192,32 +153,57 @@ VRle LOTMaskItem::rle()
     if (mRleTask.valid()) {
         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);
     }
 }
 
@@ -231,7 +217,7 @@ VRle LOTLayerItem::maskRle(const VRect &clipRect)
             break;
         }
         case LOTMaskData::Mode::Substarct: {
-            if (rle.isEmpty() && !clipRect.isEmpty())
+            if (rle.empty() && !clipRect.empty())
                 rle = VRle::toRle(clipRect);
             rle = rle - i->rle();
             break;
@@ -240,6 +226,10 @@ VRle LOTLayerItem::maskRle(const VRect &clipRect)
             rle = rle & i->rle();
             break;
         }
+        case LOTMaskData::Mode::Difference: {
+            rle = rle ^ i->rle();
+            break;
+        }
         default:
             break;
         }
@@ -247,16 +237,10 @@ VRle LOTLayerItem::maskRle(const VRect &clipRect)
     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) {
+        for (auto &i : mLayerData->mMasks) {
             mMasks.push_back(std::make_unique<LOTMaskItem>(i.get()));
         }
     }
@@ -271,21 +255,21 @@ void LOTLayerItem::updateStaticProperty()
     mStatic = mPrecompLayer ? (mStatic & mPrecompLayer->isStatic()) : mStatic;
 }
 
-void LOTLayerItem::update(int frameNo, const VMatrix &parentMatrix,
+void LOTLayerItem::update(int frameNumber, const VMatrix &parentMatrix,
                           float parentAlpha)
 {
-    mFrameNo = frameNo;
+    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);
+    VMatrix m = matrix(frameNo());
     m *= parentMatrix;
-    float alpha = parentAlpha * opacity(frameNo);
+    float alpha = parentAlpha * opacity(frameNo());
 
     // 6. update the mask
     if (hasMask()) {
-        for (auto &i : mMasks) i->update(frameNo, m, alpha, mDirtyFlag);
+        for (auto &i : mMasks) i->update(frameNo(), m, alpha, mDirtyFlag);
     }
 
     // 3. update the dirty flag based on the change
@@ -316,10 +300,10 @@ float LOTLayerItem::opacity(int frameNo) const
 VMatrix LOTLayerItem::matrix(int frameNo) const
 {
     if (mParentLayer)
-        return mLayerData->mTransform->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
@@ -334,69 +318,90 @@ bool LOTLayerItem::visible() const
 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 (auto i : mLayers) {
-        int id = i->parentId();
+    for (const auto &layer : mLayers) {
+        int id = layer->parentId();
         if (id >= 0) {
-            auto search = mLayerMap.find(id);
-            if (search != mLayerMap.end()) {
-                LOTLayerItem *parentLayer = search->second;
-                i->setParentLayer(parentLayer);
-            }
+            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());
         }
-        i->setPrecompLayer(this);
+        // update the precomp layer if its not the root layer.
+        if (!layerModel->root()) layer->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());
     }
 }
 
@@ -404,9 +409,7 @@ 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;
+    for (const auto &layer : mLayers) {
         layer->renderList(list);
     }
 }
@@ -456,63 +459,65 @@ void LOTNullLayerItem::updateContent() {}
 LOTShapeLayerItem::LOTShapeLayerItem(LOTLayerData *layerData)
     : LOTLayerItem(layerData)
 {
-    mRoot = new LOTContentGroupItem(nullptr);
+    mRoot = std::make_unique<LOTContentGroupItem>(nullptr);
     mRoot->addChildren(layerData);
-    mRoot->processPaintOperation();
-    if (layerData->hasPathOperator()) mRoot->processTrimOperation();
-}
 
-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(
+        return std::make_unique<LOTContentGroupItem>(
             static_cast<LOTShapeGroupData *>(contentData));
         break;
     }
     case LOTData::Type::Rect: {
-        return new LOTRectItem(static_cast<LOTRectData *>(contentData));
+        return std::make_unique<LOTRectItem>(static_cast<LOTRectData *>(contentData));
         break;
     }
     case LOTData::Type::Ellipse: {
-        return new LOTEllipseItem(static_cast<LOTEllipseData *>(contentData));
+        return std::make_unique<LOTEllipseItem>(static_cast<LOTEllipseData *>(contentData));
         break;
     }
     case LOTData::Type::Shape: {
-        return new LOTShapeItem(static_cast<LOTShapeData *>(contentData));
+        return std::make_unique<LOTShapeItem>(static_cast<LOTShapeData *>(contentData));
         break;
     }
     case LOTData::Type::Polystar: {
-        return new LOTPolystarItem(static_cast<LOTPolystarData *>(contentData));
+        return std::make_unique<LOTPolystarItem>(static_cast<LOTPolystarData *>(contentData));
         break;
     }
     case LOTData::Type::Fill: {
-        return new LOTFillItem(static_cast<LOTFillData *>(contentData));
+        return std::make_unique<LOTFillItem>(static_cast<LOTFillData *>(contentData));
         break;
     }
     case LOTData::Type::GFill: {
-        return new LOTGFillItem(static_cast<LOTGFillData *>(contentData));
+        return std::make_unique<LOTGFillItem>(static_cast<LOTGFillData *>(contentData));
         break;
     }
     case LOTData::Type::Stroke: {
-        return new LOTStrokeItem(static_cast<LOTStrokeData *>(contentData));
+        return std::make_unique<LOTStrokeItem>(static_cast<LOTStrokeData *>(contentData));
         break;
     }
     case LOTData::Type::GStroke: {
-        return new LOTGStrokeItem(static_cast<LOTGStrokeData *>(contentData));
+        return std::make_unique<LOTGStrokeItem>(static_cast<LOTGStrokeData *>(contentData));
         break;
     }
     case LOTData::Type::Repeater: {
-        return new LOTRepeaterItem(static_cast<LOTRepeaterData *>(contentData));
+        return std::make_unique<LOTRepeaterItem>(static_cast<LOTRepeaterData *>(contentData));
         break;
     }
     case LOTData::Type::Trim: {
-        return new LOTTrimItem(static_cast<LOTTrimData *>(contentData));
+        return std::make_unique<LOTTrimItem>(static_cast<LOTTrimData *>(contentData));
         break;
     }
     default:
@@ -524,6 +529,10 @@ LOTContentItem *LOTShapeLayerItem::createContentItem(LOTData *contentData)
 void LOTShapeLayerItem::updateContent()
 {
     mRoot->update(frameNo(), combinedMatrix(), combinedAlpha(), flag());
+
+    if (mLayerData->hasPathOperator()) {
+        mRoot->applyTrim();
+    }
 }
 
 void LOTShapeLayerItem::renderList(std::vector<VDrawable *> &list)
@@ -541,18 +550,16 @@ 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);
+    for (auto &i : data->mChildren) {
+        auto content = LOTShapeLayerItem::createContentItem(i.get());
+        if (content) {
+            content->setParent(this);
+            mContents.push_back(std::move(content));
+        }
     }
-}
 
-LOTContentGroupItem::~LOTContentGroupItem()
-{
-    for (auto i : mContents) {
-        delete i;
-    }
+    // keep the content in back-to-front order.
+    std::reverse(mContents.begin(), mContents.end());
 }
 
 void LOTContentGroupItem::update(int frameNo, const VMatrix &parentMatrix,
@@ -577,150 +584,101 @@ void LOTContentGroupItem::update(int frameNo, const VMatrix &parentMatrix,
         }
     }
 
-    for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
-        (*i)->update(frameNo, m, alpha, newFlag);
+    mMatrix = m;
+
+    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);
+        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 *>(child)) {
+                       dynamic_cast<LOTContentGroupItem *>(content)) {
             // update the groups node with current list
-            groupNode->paintOperationHelper(list);
+            groupNode->processPaintItems(list);
         }
     }
-    list.erase(list.begin() + curOpCount, list.end());
-}
-
-void LOTPathDataItem::addPaintOperation(std::vector<LOTPaintDataItem *> &list,
-                                        int externalCount)
-{
-    for (auto paintItem : list) {
-        bool sameGroup = (externalCount-- > 0) ? false : true;
-        mNodeList.push_back(std::make_unique<LOTDrawable>());
-        mRenderList.push_back(
-            LOTRenderNode(this, paintItem, mNodeList.back().get(), sameGroup));
-    }
-}
-
-void LOTContentGroupItem::processTrimOperation()
-{
-    std::vector<LOTTrimItem *> list;
-    trimOperationHelper(list);
 }
 
-void LOTContentGroupItem::trimOperationHelper(std::vector<LOTTrimItem *> &list)
+void LOTContentGroupItem::processTrimItems(
+    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 trim operation list to it.
-            pathNode->addTrimOperation(list);
-        } else if (auto trimNode = dynamic_cast<LOTTrimItem *>(child)) {
-            // add it to the trim operation list
-            list.push_back(trimNode);
+        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 *>(child)) {
+                       dynamic_cast<LOTContentGroupItem *>(content)) {
             // update the groups node with current list
-            groupNode->trimOperationHelper(list);
+            groupNode->processTrimItems(list);
         }
     }
-    list.erase(list.begin() + curOpCount, list.end());
-}
-
-void LOTPathDataItem::addTrimOperation(std::vector<LOTTrimItem *> &list)
-{
-    for (auto trimItem : list) {
-        mTrimNodeRefs.push_back(trimItem);
-    }
 }
 
-void LOTPathDataItem::update(int frameNo, const VMatrix &parentMatrix,
-                             float parentAlpha, const DirtyFlag &flag)
+void LOTPathDataItem::update(int frameNo, const VMatrix &,
+                             float, const DirtyFlag &flag)
 {
-    VPath tempPath;
-
     mPathChanged = false;
-    mCombinedAlpha = parentAlpha;
 
     // 1. update the local path if needed
-    if (!(mInit && mStaticPath)) {
+    if (hasChanged(frameNo)) {
         updatePath(mLocalPath, frameNo);
-        mInit = true;
         mPathChanged = true;
+        mNeedUpdate = true;
     }
 
-    tempPath = mLocalPath;
-
-    // 2. apply path operation if needed
-    if (mTrimNodeRefs.size() > 0) {
-        // TODO apply more than one trim path if necessary
-        VPathMesure pm;
-        float       s = mTrimNodeRefs.front()->getStart(frameNo) / 100.0f;
-        float       e = mTrimNodeRefs.front()->getEnd(frameNo) / 100.0f;
-
-        pm.setOffset(s, e);
-        tempPath = pm.trim(tempPath);
-        mPathChanged = true;
-    }
+    mTemp = mLocalPath;
 
     // 3. compute the final path with parentMatrix
-
     if ((flag & DirtyFlagBit::Matrix) || mPathChanged) {
-        mFinalPath.clone(tempPath);
-        mFinalPath.transform(parentMatrix);
         mPathChanged = true;
     }
-
-    // 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;
-        }
-    }
 }
 
-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;
     }
-}
-
-VPath LOTPathDataItem::path() const
-{
     return mFinalPath;
 }
-
 LOTRectItem::LOTRectItem(LOTRectData *data)
     : LOTPathDataItem(data->isStatic()), mData(data)
 {
@@ -730,12 +688,13 @@ void LOTRectItem::updatePath(VPath& path, int frameNo)
 {
     VPointF pos = mData->mPos.value(frameNo);
     VPointF size = mData->mSize.value(frameNo);
-    float   radius = mData->mRound.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, radius, radius, mData->direction());
+    path.addRoundRect(r, roundness, mData->direction());
+    updateCache(frameNo, pos, size, roundness);
 }
 
 LOTEllipseItem::LOTEllipseItem(LOTEllipseData *data)
@@ -752,6 +711,7 @@ void LOTEllipseItem::updatePath(VPath& path, int frameNo)
 
     path.reset();
     path.addOval(r, mData->direction());
+    updateCache(frameNo, pos, size);
 }
 
 LOTShapeItem::LOTShapeItem(LOTShapeData *data)
@@ -793,29 +753,68 @@ void LOTPolystarItem::updatePath(VPath& path, int frameNo)
     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){}
 
 void LOTPaintDataItem::update(int frameNo, const VMatrix &parentMatrix,
                               float parentAlpha, const DirtyFlag &flag)
 {
-    mContentChanged = false;
+    mRenderNodeUpdate = true;
     mParentAlpha = parentAlpha;
-    mParentMatrix = parentMatrix;
     mFlag = flag;
     mFrameNo = frameNo;
-    // 1. update the local content if needed
-    // if (!(mInit && mStaticContent)) {
-    mInit = true;
+
     updateContent(frameNo);
-    mContentChanged = true;
-    // }
 }
 
+void LOTPaintDataItem::updateRenderNode()
+{
+    bool dirty = false;
+    for (auto &i : mPathItems) {
+        if (i->dirty()) {
+            dirty = true;
+            break;
+        }
+    }
+
+    if (dirty) {
+        mPath.reset();
+
+        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());
+}
+
+
+void LOTPaintDataItem::addPathItems(std::vector<LOTPathDataItem *> &list, int startOffset)
+{
+    std::copy(list.begin() + startOffset, list.end(), back_inserter(mPathItems));
+}
+
+
 LOTFillItem::LOTFillItem(LOTFillData *data)
     : LOTPaintDataItem(data->isStatic()), mData(data)
 {
@@ -829,17 +828,14 @@ void LOTFillItem::updateContent(int frameNo)
     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)
@@ -850,15 +846,15 @@ LOTGFillItem::LOTGFillItem(LOTGFillData *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)
@@ -890,25 +886,23 @@ static float getScale(const VMatrix &matrix)
     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);
     }
 }
 
@@ -921,7 +915,7 @@ LOTGStrokeItem::LOTGStrokeItem(LOTGStrokeData *data)
 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();
@@ -931,36 +925,175 @@ 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) {}
 
-void LOTTrimItem::update(int frameNo, const VMatrix &parentMatrix,
-                         float parentAlpha, const DirtyFlag &flag)
+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;
 }
 
+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 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::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()
 {
-    mCNode.mFlag = ChangeFlagNone;
+    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) {
@@ -968,90 +1101,104 @@ void LOTDrawable::sync()
         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;
+        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;
+        mCNode->mStroke.width = mStroke.width;
+        mCNode->mStroke.meterLimit = mStroke.meterLimit;
+        mCNode->mStroke.enable = 1;
 
         switch (mFillRule) {
         case FillRule::EvenOdd:
-            mCNode.mFillRule = LOTNode::EvenOdd;
+            mCNode->mFillRule = LOTFillRule::FillEvenOdd;
             break;
         default:
-            mCNode.mFillRule = LOTNode::Winding;
+            mCNode->mFillRule = LOTFillRule::FillWinding;
             break;
         }
 
         switch (mStroke.cap) {
         case CapStyle::Flat:
-            mCNode.mStroke.cap = LOTNode::FlatCap;
+            mCNode->mStroke.cap = LOTCapStyle::CapFlat;
             break;
         case CapStyle::Square:
-            mCNode.mStroke.cap = LOTNode::SquareCap;
+            mCNode->mStroke.cap = LOTCapStyle::CapSquare;
             break;
         case CapStyle::Round:
-            mCNode.mStroke.cap = LOTNode::RoundCap;
+            mCNode->mStroke.cap = LOTCapStyle::CapRound;
             break;
         default:
-            mCNode.mStroke.cap = LOTNode::FlatCap;
+            mCNode->mStroke.cap = LOTCapStyle::CapFlat;
             break;
         }
 
         switch (mStroke.join) {
         case JoinStyle::Miter:
-            mCNode.mStroke.join = LOTNode::MiterJoin;
+            mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
             break;
         case JoinStyle::Bevel:
-            mCNode.mStroke.join = LOTNode::BevelJoin;
+            mCNode->mStroke.join = LOTJoinStyle::JoinBevel;
             break;
         case JoinStyle::Round:
-            mCNode.mStroke.join = LOTNode::RoundJoin;
+            mCNode->mStroke.join = LOTJoinStyle::JoinRound;
             break;
         default:
-            mCNode.mStroke.join = LOTNode::MiterJoin;
+            mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
             break;
         }
 
-        mCNode.mStroke.dashArray = mStroke.dashArray;
-        mCNode.mStroke.dashArraySize = mStroke.dashArraySize;
+        mCNode->mStroke.dashArray = mStroke.mDash.data();
+        mCNode->mStroke.dashArraySize = mStroke.mDash.size();
 
     } else {
-        mCNode.mStroke.enable = 0;
+        mCNode->mStroke.enable = 0;
     }
 
     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;
+        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.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 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.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 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;
     }