lottie/trim: fix regression, always modify the path in trim::update() call.
[platform/core/uifw/lottie-player.git] / src / lottie / lottieitem.cpp
index 56a4532..a882a0a 100644 (file)
@@ -80,10 +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);
 
-    buildRenderList();
     mCurFrameNo = frameNo;
     mUpdateViewBox = false;
     return true;
@@ -98,7 +97,7 @@ void LOTCompItem::buildRenderList()
     for (auto &i : mDrawableList) {
         LOTDrawable *lotDrawable = static_cast<LOTDrawable *>(i);
         lotDrawable->sync();
-        mRenderList.push_back(&lotDrawable->mCNode);
+        mRenderList.push_back(lotDrawable->mCNode.get());
     }
 }
 
@@ -107,16 +106,19 @@ 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);
@@ -129,7 +131,7 @@ void LOTMaskItem::update(int frameNo, const VMatrix &parentMatrix,
                          float parentAlpha, const DirtyFlag &/*flag*/)
 {
     if (mData->mShape.isStatic()) {
-        if (mLocalPath.isEmpty()) {
+        if (mLocalPath.empty()) {
             mData->mShape.value(frameNo).toPath(mLocalPath);
         }
     } else {
@@ -167,7 +169,7 @@ void LOTLayerItem::render(VPainter *painter, const VRle &inheritMask, const VRle
             matteRle = matteRle + i->rle();
         }
 
-        if (!inheritMatte.isEmpty())
+        if (!inheritMatte.empty())
             matteRle = matteRle & inheritMatte;
     } else {
         matteRle = inheritMatte;
@@ -178,10 +180,10 @@ void LOTLayerItem::render(VPainter *painter, const VRle &inheritMask, const VRle
     VRle mask;
     if (hasMask()) {
         mask = maskRle(painter->clipBoundingRect());
-        if (!inheritMask.isEmpty())
+        if (!inheritMask.empty())
             mask = mask & inheritMask;
         // if resulting mask is empty then return.
-        if (mask.isEmpty())
+        if (mask.empty())
             return;
     } else {
         mask = inheritMask;
@@ -190,11 +192,11 @@ void LOTLayerItem::render(VPainter *painter, const VRle &inheritMask, const VRle
     for (auto &i : mDrawableList) {
         painter->setBrush(i->mBrush);
         VRle rle = i->rle();
-        if (!mask.isEmpty()) rle = rle & mask;
+        if (!mask.empty()) rle = rle & mask;
 
-        if (rle.isEmpty()) continue;
+        if (rle.empty()) continue;
 
-        if (!matteRle.isEmpty()) {
+        if (!matteRle.empty()) {
             if (mLayerData->mMatteType == MatteType::AlphaInv) {
                 rle = rle - matteRle;
             } else {
@@ -215,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;
@@ -253,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
@@ -298,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
@@ -318,11 +320,9 @@ LOTCompLayerItem::LOTCompLayerItem(LOTLayerData *layerModel)
 {
     // 1. create layer item
     for (auto &i : mLayerData->mChildren) {
-        LOTLayerData *layerModel = dynamic_cast<LOTLayerData *>(i.get());
-        if (layerModel) {
-            auto layerItem = LOTCompItem::createLayerItem(layerModel);
-            if (layerItem) mLayers.push_back(std::move(layerItem));
-        }
+        LOTLayerData *layerModel = static_cast<LOTLayerData *>(i.get());
+        auto layerItem = LOTCompItem::createLayerItem(layerModel);
+        if (layerItem) mLayers.push_back(std::move(layerItem));
     }
 
     // 2. update parent layer
@@ -361,7 +361,7 @@ void LOTCompLayerItem::render(VPainter *painter, const VRle &inheritMask, const
             matteRle = matteRle + i->rle();
         }
 
-        if (!inheritMatte.isEmpty())
+        if (!inheritMatte.empty())
             matteRle = matteRle & inheritMatte;
     } else {
         matteRle = inheritMatte;
@@ -370,10 +370,10 @@ void LOTCompLayerItem::render(VPainter *painter, const VRle &inheritMask, const
     VRle mask;
     if (hasMask()) {
         mask = maskRle(painter->clipBoundingRect());
-        if (!inheritMask.isEmpty())
+        if (!inheritMask.empty())
             mask = mask & inheritMask;
         // if resulting mask is empty then return.
-        if (mask.isEmpty())
+        if (mask.empty())
             return;
     } else {
         mask = inheritMask;
@@ -400,7 +400,8 @@ void LOTCompLayerItem::render(VPainter *painter, const VRle &inheritMask, const
 void LOTCompLayerItem::updateContent()
 {
     for (const auto &layer : mLayers) {
-        layer->update(frameNo(), combinedMatrix(), combinedAlpha());
+        layer->update( mLayerData->timeRemap(frameNo()) - mLayerData->startFrame(),
+                       combinedMatrix(), combinedAlpha());
     }
 }
 
@@ -692,7 +693,7 @@ void LOTRectItem::updatePath(VPath& path, int frameNo)
              size.y());
 
     path.reset();
-    path.addRoundRect(r, roundness, roundness, mData->direction());
+    path.addRoundRect(r, roundness, mData->direction());
     updateCache(frameNo, pos, size, roundness);
 }
 
@@ -851,6 +852,7 @@ void LOTGFillItem::updateContent(int frameNo)
 
 void LOTGFillItem::updateRenderNode()
 {
+    mGradient->setAlpha(parentAlpha());
     mDrawable->setBrush(VBrush(mGradient.get()));
     mDrawable->setFillRule(mFillRule);
 }
@@ -926,6 +928,7 @@ void LOTGStrokeItem::updateContent(int frameNo)
 void LOTGStrokeItem::updateRenderNode()
 {
     float scale = getScale(mGradient->mMatrix);
+    mGradient->setAlpha(parentAlpha());
     mDrawable->setBrush(VBrush(mGradient.get()));
     mDrawable->setStrokeInfo(mCap, mJoin, mMiterLimit,
                             mWidth * scale);
@@ -964,14 +967,85 @@ void LOTTrimItem::update()
     // when both path and trim are not dirty
     if (!(mDirty || pathDirty())) return;
 
-    //@TODO take the offset and trim type into account.
-    for (auto &i : mPathItems) {
-        VPathMesure pm;
-        pm.setStart(mCache.mStart);
-        pm.setEnd(mCache.mEnd);
-        pm.setOffset(mCache.mOffset);
-        i->updatePath(pm.trim(i->localPath()));
+    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;
+                }
+            }
+        }
     }
+
 }
 
 
@@ -990,9 +1064,36 @@ void LOTRepeaterItem::update(int /*frameNo*/, const VMatrix &/*parentMatrix*/,
 
 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) {
@@ -1000,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 = LOTFillRule::FillEvenOdd;
+            mCNode->mFillRule = LOTFillRule::FillEvenOdd;
             break;
         default:
-            mCNode.mFillRule = LOTFillRule::FillWinding;
+            mCNode->mFillRule = LOTFillRule::FillWinding;
             break;
         }
 
         switch (mStroke.cap) {
         case CapStyle::Flat:
-            mCNode.mStroke.cap = LOTCapStyle::CapFlat;
+            mCNode->mStroke.cap = LOTCapStyle::CapFlat;
             break;
         case CapStyle::Square:
-            mCNode.mStroke.cap = LOTCapStyle::CapSquare;
+            mCNode->mStroke.cap = LOTCapStyle::CapSquare;
             break;
         case CapStyle::Round:
-            mCNode.mStroke.cap = LOTCapStyle::CapRound;
+            mCNode->mStroke.cap = LOTCapStyle::CapRound;
             break;
         default:
-            mCNode.mStroke.cap = LOTCapStyle::CapFlat;
+            mCNode->mStroke.cap = LOTCapStyle::CapFlat;
             break;
         }
 
         switch (mStroke.join) {
         case JoinStyle::Miter:
-            mCNode.mStroke.join = LOTJoinStyle::JoinMiter;
+            mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
             break;
         case JoinStyle::Bevel:
-            mCNode.mStroke.join = LOTJoinStyle::JoinBevel;
+            mCNode->mStroke.join = LOTJoinStyle::JoinBevel;
             break;
         case JoinStyle::Round:
-            mCNode.mStroke.join = LOTJoinStyle::JoinRound;
+            mCNode->mStroke.join = LOTJoinStyle::JoinRound;
             break;
         default:
-            mCNode.mStroke.join = LOTJoinStyle::JoinMiter;
+            mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
             break;
         }
 
-        mCNode.mStroke.dashArray = mStroke.mDash.data();
-        mCNode.mStroke.dashArraySize = mStroke.mDash.size();
+        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 = 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;
+        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 = LOTBrushType::BrushGradient;
-        mCNode.mGradient.type = LOTGradientType::GradientLinear;
-        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 = LOTBrushType::BrushGradient;
-        mCNode.mGradient.type = LOTGradientType::GradientRadial;
-        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;
     }