example: refactor lottieview to test both c and cpp api
authorsubhransu mohanty <sub.mohanty@samsung.com>
Wed, 17 Apr 2019 07:47:11 +0000 (16:47 +0900)
committerHermet Park <hermetpark@gmail.com>
Thu, 25 Apr 2019 04:13:38 +0000 (13:13 +0900)
Change-Id: Ie1c9e2f5a25b620cec0d14ed593095d8696a727c

example/demo.cpp
example/lottieview.cpp
example/lottieview.h
example/lottieviewer.cpp
example/lottieviewtest.cpp
example/uxsampletest.cpp

index 9b14d55..368e29f 100644 (file)
@@ -45,7 +45,7 @@ main(void)
    app->setup();
 
    std::string filePath = DEMO_DIR;
-   filePath +="image_test.json";
+   filePath +="3d.json";
 
    LottieView *view = new LottieView(app->evas());
    view->setFilePath(filePath.c_str());
index 383aa6d..755b2e0 100644 (file)
@@ -34,309 +34,74 @@ animator(void *data , double pos)
     return EINA_TRUE;
 }
 
-void LottieView::createVgNode(LOTNode *node, Efl_VG *root)
-{
-    Efl_VG *shape = evas_vg_shape_add(root);
-
-    //0: Path
-    const float *data = node->mPath.ptPtr;
-    if (!data) return;
-
-    for (int i = 0; i < node->mPath.elmCount; i++) {
-        switch (node->mPath.elmPtr[i]) {
-        case 0:
-            evas_vg_shape_append_move_to(shape, data[0], data[1]);
-            data += 2;
-            break;
-        case 1:
-            evas_vg_shape_append_line_to(shape, data[0], data[1]);
-            data += 2;
-            break;
-        case 2:
-            evas_vg_shape_append_cubic_to(shape, data[0], data[1], data[2], data[3], data[4], data[5]);
-            data += 6;
-            break;
-        case 3:
-            evas_vg_shape_append_close(shape);
-            break;
-        default:
-            break;
-        }
-    }
-
-    //1: Stroke
-    if (node->mStroke.enable) {
-        //Stroke Width
-        evas_vg_shape_stroke_width_set(shape, node->mStroke.width);
-
-        //Stroke Cap
-        Efl_Gfx_Cap cap;
-        switch (node->mStroke.cap) {
-        case CapFlat: cap = EFL_GFX_CAP_BUTT; break;
-        case CapSquare: cap = EFL_GFX_CAP_SQUARE; break;
-        case CapRound: cap = EFL_GFX_CAP_ROUND; break;
-        default: cap = EFL_GFX_CAP_BUTT; break;
-        }
-        evas_vg_shape_stroke_cap_set(shape, cap);
-
-        //Stroke Join
-        Efl_Gfx_Join join;
-        switch (node->mStroke.join) {
-        case JoinMiter: join = EFL_GFX_JOIN_MITER; break;
-        case JoinBevel: join = EFL_GFX_JOIN_BEVEL; break;
-        case JoinRound: join = EFL_GFX_JOIN_ROUND; break;
-        default: join = EFL_GFX_JOIN_MITER; break;
-        }
-        evas_vg_shape_stroke_join_set(shape, join);
-
-        //Stroke Dash
-        if (node->mStroke.dashArraySize > 0) {
-            int size = (node->mStroke.dashArraySize / 2);
-            Efl_Gfx_Dash *dash = static_cast<Efl_Gfx_Dash*>(malloc(sizeof(Efl_Gfx_Dash) * size));
-            if (dash) {
-                for (int i = 0; i <= size; i+=2) {
-                    dash[i].length = node->mStroke.dashArray[i];
-                    dash[i].gap = node->mStroke.dashArray[i + 1];
-                }
-                evas_vg_shape_stroke_dash_set(shape, dash, size);
-                free(dash);
-            }
-        }
-    }
+LottieView::LottieView(Evas *evas, Strategy s) {
+    mPalying = false;
+    mReverse = false;
+    mRepeatCount = 0;
+    mRepeatMode = LottieView::RepeatMode::Restart;
+    mLoop = false;
+    mSpeed = 1;
 
-    //2: Fill Method
-    switch (node->mBrushType) {
-    case BrushSolid: {
-        float pa = ((float)node->mColor.a) / 255;
-        int r = (int)(((float) node->mColor.r) * pa);
-        int g = (int)(((float) node->mColor.g) * pa);
-        int b = (int)(((float) node->mColor.b) * pa);
-        int a = node->mColor.a;
-        if (node->mStroke.enable)
-          evas_vg_shape_stroke_color_set(shape, r, g, b, a);
-        else
-          evas_vg_node_color_set(shape, r, g, b, a);
+    switch (s) {
+    case Strategy::renderCpp: {
+        mRenderDelegate = std::make_unique<RlottieRenderStrategy_CPP>(evas);
         break;
     }
-    case BrushGradient: {
-        Efl_VG* grad = NULL;
-        if (node->mGradient.type == GradientLinear) {
-            grad = evas_vg_gradient_linear_add(root);
-            evas_vg_gradient_linear_start_set(grad, node->mGradient.start.x, node->mGradient.start.y);
-            evas_vg_gradient_linear_end_set(grad, node->mGradient.end.x, node->mGradient.end.y);
-
-        }
-        else if (node->mGradient.type == GradientRadial) {
-            grad = evas_vg_gradient_radial_add(root);
-            evas_vg_gradient_radial_center_set(grad, node->mGradient.center.x, node->mGradient.center.y);
-            evas_vg_gradient_radial_focal_set(grad, node->mGradient.focal.x, node->mGradient.focal.y);
-            evas_vg_gradient_radial_radius_set(grad, node->mGradient.cradius);
-        }
-
-        if (grad) {
-            //Gradient Stop
-            Efl_Gfx_Gradient_Stop* stops = static_cast<Efl_Gfx_Gradient_Stop*>(malloc(sizeof(Efl_Gfx_Gradient_Stop) * node->mGradient.stopCount));
-            if (stops) {
-                for (unsigned int i = 0; i < node->mGradient.stopCount; i++) {
-                    stops[i].offset = node->mGradient.stopPtr[i].pos;
-                    float pa = ((float)node->mGradient.stopPtr[i].a) / 255;
-                    stops[i].r = (int)(((float)node->mGradient.stopPtr[i].r) * pa);
-                    stops[i].g = (int)(((float)node->mGradient.stopPtr[i].g) * pa);
-                    stops[i].b = (int)(((float)node->mGradient.stopPtr[i].b) * pa);
-                    stops[i].a = node->mGradient.stopPtr[i].a;
-                }
-                evas_vg_gradient_stop_set(grad, stops, node->mGradient.stopCount);
-                free(stops);
-            }
-            if (node->mStroke.enable)
-              evas_vg_shape_stroke_fill_set(shape, grad);
-            else
-              evas_vg_shape_fill_set(shape, grad);
-        }
+    case Strategy::renderCppAsync: {
+        mRenderDelegate = std::make_unique<RlottieRenderStrategy_CPP_ASYNC>(evas);
         break;
     }
-    default:
-      break;
-    }
-
-    //3: Fill Rule
-    if (node->mFillRule == FillEvenOdd)
-      efl_gfx_shape_fill_rule_set(shape, EFL_GFX_FILL_RULE_ODD_EVEN);
-    else if (node->mFillRule == FillWinding)
-      efl_gfx_shape_fill_rule_set(shape, EFL_GFX_FILL_RULE_WINDING);
-}
-
-void LottieView::update(const std::vector<LOTNode *> &renderList)
-{
-    Efl_VG *root = evas_vg_container_add(mVg);
-    for(auto i : renderList) {
-        createVgNode(i, root);
+    case Strategy::renderC: {
+        mRenderDelegate = std::make_unique<RlottieRenderStrategy_C>(evas);
+        break;
     }
-    evas_object_vg_root_node_set(mVg, root);
-}
-
-void LottieView::updateTree(const LOTLayerNode * node)
-{
-    Efl_VG *root = evas_vg_container_add(mVg);
-    update(node, root);
-    evas_object_vg_root_node_set(mVg, root);
-}
-
-void LottieView::update(const LOTLayerNode * node, Efl_VG *parent)
-{
-    // if the layer is invisible return
-    if (!node->mVisible) return;
-
-    // check if this layer is a container layer
-    bool hasMatte = false;
-    if (node->mLayerList.size) {
-        for (unsigned int i = 0; i < node->mLayerList.size; i++) {
-            if (hasMatte) {
-                hasMatte = false;
-                continue;
-            }
-            // if the layer has matte then
-            // the next layer has to be rendered using this layer
-            // as matte source
-            if (node->mLayerList.ptr[i]->mMatte != MatteNone) {
-                hasMatte = true;
-                printf("Matte is not supported Yet\n");
-                continue;
-            }
-            update(node->mLayerList.ptr[i], parent);
-        }
+    case Strategy::renderCAsync: {
+        mRenderDelegate = std::make_unique<RlottieRenderStrategy_C_ASYNC>(evas);
+        break;
     }
-
-    // check if this layer has drawable
-    if (node->mNodeList.size) {
-        for (unsigned int i = 0; i < node->mNodeList.size; i++) {
-            createVgNode(node->mNodeList.ptr[i], parent);
-        }
+    case Strategy::eflVg: {
+        mRenderDelegate = std::make_unique<EflVgRenderStrategy>(evas);
+        break;
     }
-}
-
-static void mImageDelCb(void *data, Evas *evas, Evas_Object *obj, void *)
-{
-    LottieView *lottie = (LottieView *)data;
-
-    if (lottie->mImage != obj) return;
-
-    lottie->mImage = NULL;
-    lottie->stop();
-}
-
-static void mVgDelCb(void *data, Evas *evas, Evas_Object *obj, void *)
-{
-    LottieView *lottie = (LottieView *)data;
-    if (lottie->mVg != obj) return;
-
-    lottie->mVg = NULL;
-    lottie->stop();
-}
-
-void LottieView::initializeBufferObject(Evas *evas)
-{
-    if (mRenderMode) {
-        mImage = evas_object_image_filled_add(evas);
-        evas_object_image_colorspace_set(mImage, EVAS_COLORSPACE_ARGB8888);
-        evas_object_image_alpha_set(mImage, EINA_TRUE);
-        evas_object_event_callback_add(mImage, EVAS_CALLBACK_DEL, mImageDelCb, this);
-    } else {
-        mVg = evas_object_vg_add(evas);
-        evas_object_event_callback_add(mVg, EVAS_CALLBACK_DEL, mVgDelCb, this);
+    default:
+        mRenderDelegate = std::make_unique<RlottieRenderStrategy_CPP>(evas);
+        break;
     }
 }
 
-LottieView::LottieView(Evas *evas, bool renderMode, bool asyncRender):mVg(nullptr), mImage(nullptr)
-{
-    mPlayer = nullptr;
-    mPalying = false;
-    mReverse = false;
-    mRepeatCount = 0;
-    mRepeatMode = LottieView::RepeatMode::Restart;
-    mLoop = false;
-    mSpeed = 1;
-
-    mEvas = evas;
-    mRenderMode = renderMode;
-    mAsyncRender = asyncRender;
-
-    initializeBufferObject(evas);
-}
-
 LottieView::~LottieView()
 {
-    if (mRenderTask.valid())
-        mRenderTask.get();
-
     if (mAnimator) ecore_animator_del(mAnimator);
-    if (mVg) evas_object_del(mVg);
-    if (mImage) evas_object_del(mImage);
 }
 
 Evas_Object *LottieView::getImage() {
-    if (mRenderMode) {
-        return mImage;
-    } else {
-        return mVg;
-    }
+    return mRenderDelegate->renderObject();
 }
 
 void LottieView::show()
 {
-    if (mRenderMode) {
-        evas_object_show(mImage);
-    } else {
-        evas_object_show(mVg);
-    }
+    mRenderDelegate->show();
     seek(0);
 }
 
 void LottieView::hide()
 {
-    if (mRenderMode) {
-        evas_object_hide(mImage);
-    } else {
-        evas_object_hide(mVg);
-    }
+    mRenderDelegate->hide();
 }
 
 void LottieView::seek(float pos)
 {
-    if (!mPlayer) return;
+    if (!mRenderDelegate) return;
 
 
     mPos = mapProgress(pos);
 
     // check if the pos maps to the current frame
-    if (mCurFrame == mPlayer->frameAtPos(mPos)) return;
-
-    mCurFrame = mPlayer->frameAtPos(mPos);
-
-    if (mRenderMode) {
-        int width , height;
-        evas_object_image_size_get(mImage, &width, &height);
-        if (mAsyncRender) {
-            if (mRenderTask.valid()) return;
-            mDirty = true;
-            auto buffer = (uint32_t *)evas_object_image_data_get(mImage, EINA_TRUE);
-            size_t bytesperline =  evas_object_image_stride_get(mImage);
-            rlottie::Surface surface(buffer, width, height, bytesperline);
-            mRenderTask = mPlayer->render(mCurFrame, surface);
-            // to force a redraw
-            evas_object_image_data_update_add(mImage, 0 , 0, surface.width(), surface.height());
-        } else {
-            auto buffer = (uint32_t *)evas_object_image_data_get(mImage, EINA_TRUE);
-            size_t bytesperline =  evas_object_image_stride_get(mImage);
-            rlottie::Surface surface(buffer, width, height, bytesperline);
-            mPlayer->renderSync(mCurFrame, surface);
-            evas_object_image_data_set(mImage, surface.buffer());
-            evas_object_image_data_update_add(mImage, 0 , 0, surface.width(), surface.height());
-        }
-    } else {
-        const LOTLayerNode *root = mPlayer->renderTree(mCurFrame, mw, mh);
-        updateTree(root);
-    }
+    if (mCurFrame == mRenderDelegate->frameAtPos(mPos)) return;
+
+    mCurFrame = mRenderDelegate->frameAtPos(mPos);
+
+    mRenderDelegate->renderRequest(mCurFrame);
 }
 
 float LottieView::getPos()
@@ -346,57 +111,27 @@ float LottieView::getPos()
 
 void LottieView::render()
 {
-    if (!mPlayer) return;
-
-    if (!mDirty) return;
-    mDirty = false;
-
-    if (mRenderMode) {
-        if (!mRenderTask.valid()) return;
-        auto surface = mRenderTask.get();
-        evas_object_image_data_set(mImage, surface.buffer());
-        evas_object_image_data_update_add(mImage, 0 , 0, surface.width(), surface.height());
-    }
+    mRenderDelegate->renderFlush();
 }
 
 void LottieView::setFilePath(const char *filePath)
 {
-    if (mPlayer = Animation::loadFromFile(filePath)) {
-        mFrameRate = mPlayer->frameRate();
-        mTotalFrame = mPlayer->totalFrame();
-    } else {
-        printf("load failed file %s\n", filePath);
-    }
+    mRenderDelegate->loadFromFile(filePath);
 }
 
 void LottieView::loadFromData(const std::string &jsonData, const std::string &key, const std::string &resourcePath)
 {
-    if (mPlayer = Animation::loadFromData(jsonData, key, resourcePath)) {
-        mFrameRate = mPlayer->frameRate();
-        mTotalFrame = mPlayer->totalFrame();
-    } else {
-        printf("load failed from data key : %s\n", key.c_str());
-    }
+    mRenderDelegate->loadFromData(jsonData, key, resourcePath);
 }
 
 void LottieView::setSize(int w, int h)
 {
-    mw = w; mh = h;
-
-    if (mRenderMode) {
-        evas_object_resize(mImage, w, h);
-        evas_object_image_size_set(mImage, w, h);
-    } else {
-        evas_object_resize(mVg, w, h);
-    }
+    mRenderDelegate->resize(w, h);
 }
+
 void LottieView::setPos(int x, int y)
 {
-    if (mRenderMode) {
-        evas_object_move(mImage, x, y);
-    } else {
-        evas_object_move(mVg, x, y);
-    }
+    mRenderDelegate->setPos(x, y);
 }
 
 void LottieView::finished()
@@ -421,8 +156,6 @@ void LottieView::setRepeatMode(LottieView::RepeatMode mode)
 
 void LottieView::play()
 {
-    if (!mPlayer) return;
-
     if (mAnimator) ecore_animator_del(mAnimator);
     mAnimator = ecore_animator_timeline_add(duration()/mSpeed, animator, this);
     mReverse = false;
@@ -446,8 +179,6 @@ void LottieView::stop()
 
 void LottieView::restart()
 {
-    if (!mPlayer) return;
-
     mCurCount--;
     if (mLoop || mRepeatCount) {
         if (mRepeatMode == LottieView::RepeatMode::Reverse)
index f979c56..a042523 100644 (file)
 #include "rlottie_capi.h"
 #include<future>
 #include <cmath>
+
+class RenderStrategy {
+public:
+    virtual ~RenderStrategy() {
+        evas_object_del(renderObject());
+    }
+    RenderStrategy(Evas_Object *obj):_renderObject(obj){}
+    virtual void loadFromFile(const char *filePath) = 0;
+    virtual void loadFromData(const std::string &jsonData, const std::string &key, const std::string &resourcePath) = 0;
+    virtual size_t totalFrame() = 0;
+    virtual double frameRate() = 0;
+    virtual size_t frameAtPos(double pos) = 0;
+    virtual double duration() = 0;
+    virtual void renderRequest(int frame) = 0;
+    virtual void renderFlush() {}
+    virtual void resize(int width, int height) = 0;
+    virtual void setPos(int x, int y) {evas_object_move(renderObject(), x, y);}
+    void show() {evas_object_show(_renderObject);}
+    void hide() {evas_object_hide(_renderObject);}
+    Evas_Object* renderObject() const {return _renderObject;}
+private:
+    Evas_Object *_renderObject;
+};
+
+class CppApiBase : public RenderStrategy {
+public:
+    CppApiBase(Evas_Object *renderObject): RenderStrategy(renderObject) {}
+
+    void loadFromFile(const char *filePath)
+    {
+        mPlayer = rlottie::Animation::loadFromFile(filePath);
+
+        if (!mPlayer) {
+            printf("load failed file %s\n", filePath);
+        }
+    }
+
+    void loadFromData(const std::string &jsonData, const std::string &key, const std::string &resourcePath)
+    {
+        mPlayer = rlottie::Animation::loadFromData(jsonData, key, resourcePath);
+        if (!mPlayer) {
+            printf("load failed from data\n");
+        }
+    }
+
+    size_t totalFrame() {
+        return mPlayer->totalFrame();
+
+    }
+    double duration() {
+        return mPlayer->duration();
+    }
+
+    double frameRate() {
+        return mPlayer->frameRate();
+    }
+
+    size_t frameAtPos(double pos) {
+        return  mPlayer->frameAtPos(pos);
+    }
+protected:
+   std::unique_ptr<rlottie::Animation>       mPlayer;
+};
+
+class RlottieRenderStrategyCBase : public RenderStrategy {
+public:
+    RlottieRenderStrategyCBase(Evas *evas):RenderStrategy(evas_object_image_filled_add(evas)) {
+        evas_object_image_colorspace_set(renderObject(), EVAS_COLORSPACE_ARGB8888);
+        evas_object_image_alpha_set(renderObject(), EINA_TRUE);
+    }
+    void resize(int width, int height) {
+        evas_object_resize(renderObject(), width, height);
+        evas_object_image_size_set(renderObject(), width, height);
+    }
+};
+
+class RlottieRenderStrategy : public CppApiBase {
+public:
+    RlottieRenderStrategy(Evas *evas):CppApiBase(evas_object_image_filled_add(evas)) {
+        evas_object_image_colorspace_set(renderObject(), EVAS_COLORSPACE_ARGB8888);
+        evas_object_image_alpha_set(renderObject(), EINA_TRUE);
+    }
+    void resize(int width, int height) {
+        evas_object_resize(renderObject(), width, height);
+        evas_object_image_size_set(renderObject(), width, height);
+    }
+};
+
+class RlottieRenderStrategy_CPP : public RlottieRenderStrategy {
+public:
+    RlottieRenderStrategy_CPP(Evas *evas):RlottieRenderStrategy(evas) {}
+
+    void renderRequest(int frame) {
+        int width , height;
+        Evas_Object *image = renderObject();
+        evas_object_image_size_get(image, &width, &height);
+        auto buffer = (uint32_t *)evas_object_image_data_get(image, EINA_TRUE);
+        size_t bytesperline =  evas_object_image_stride_get(image);
+        rlottie::Surface surface(buffer, width, height, bytesperline);
+        mPlayer->renderSync(frame, surface);
+        evas_object_image_data_set(image, surface.buffer());
+        evas_object_image_data_update_add(image, 0 , 0, surface.width(), surface.height());
+    }
+};
+
+class RlottieRenderStrategy_CPP_ASYNC : public RlottieRenderStrategy_CPP {
+public:
+    RlottieRenderStrategy_CPP_ASYNC(Evas *evas):RlottieRenderStrategy_CPP(evas) {}
+    ~RlottieRenderStrategy_CPP_ASYNC() {
+        if (mRenderTask.valid())
+            mRenderTask.get();
+    }
+    void renderRequest(int frame) {
+        if (mRenderTask.valid()) return;
+        mDirty = true;
+        int width , height;
+        Evas_Object *image = renderObject();
+        evas_object_image_size_get(image, &width, &height);
+        auto buffer = (uint32_t *)evas_object_image_data_get(image, EINA_TRUE);
+        size_t bytesperline =  evas_object_image_stride_get(image);
+        rlottie::Surface surface(buffer, width, height, bytesperline);
+        mRenderTask = mPlayer->render(frame, surface);
+        // to force a redraw
+        evas_object_image_data_update_add(renderObject(), 0 , 0, surface.width(), surface.height());
+    }
+
+    void renderFlush() {
+        if (!mDirty) return;
+
+        if (!mRenderTask.valid()) return;
+
+        auto surface = mRenderTask.get();
+        evas_object_image_data_set(renderObject(), surface.buffer());
+        evas_object_image_data_update_add(renderObject(), 0 , 0, surface.width(), surface.height());
+        mDirty = false;
+    }
+private:
+   std::future<rlottie::Surface>        mRenderTask;
+   bool                                 mDirty{true};
+};
+
+
+class RlottieRenderStrategy_C : public RlottieRenderStrategyCBase {
+public:
+    RlottieRenderStrategy_C(Evas *evas):RlottieRenderStrategyCBase(evas) {}
+    ~RlottieRenderStrategy_C() {
+        if (mPlayer) lottie_animation_destroy(mPlayer);
+    }
+    void loadFromFile(const char *filePath)
+    {
+        mPlayer = lottie_animation_from_file(filePath);
+
+        if (!mPlayer) {
+            printf("load failed file %s\n", filePath);
+        }
+    }
+
+    void loadFromData(const std::string &jsonData, const std::string &key, const std::string &resourcePath)
+    {
+        mPlayer = lottie_animation_from_data(jsonData.c_str(), key.c_str(), resourcePath.c_str());
+        if (!mPlayer) {
+            printf("load failed from data\n");
+        }
+    }
+
+    size_t totalFrame() {
+        return lottie_animation_get_totalframe(mPlayer);
+
+    }
+
+    double frameRate() {
+        return lottie_animation_get_framerate(mPlayer);
+    }
+
+    size_t frameAtPos(double pos) {
+        return  lottie_animation_get_frame_at_pos(mPlayer, pos);
+    }
+
+    double duration() {
+        return lottie_animation_get_duration(mPlayer);
+    }
+
+    void renderRequest(int frame) {
+        int width , height;
+        Evas_Object *image = renderObject();
+        evas_object_image_size_get(image, &width, &height);
+        auto buffer = (uint32_t *)evas_object_image_data_get(image, EINA_TRUE);
+        size_t bytesperline =  evas_object_image_stride_get(image);
+        lottie_animation_render_async(mPlayer, frame, buffer, width, height, bytesperline);
+        lottie_animation_render_flush(mPlayer);
+        evas_object_image_data_set(image, buffer);
+        evas_object_image_data_update_add(image, 0 , 0, width, height);
+    }
+
+protected:
+   Lottie_Animation       *mPlayer;
+};
+
+class RlottieRenderStrategy_C_ASYNC : public RlottieRenderStrategy_C {
+public:
+    RlottieRenderStrategy_C_ASYNC(Evas *evas):RlottieRenderStrategy_C(evas) {}
+    ~RlottieRenderStrategy_C_ASYNC() {
+        if (mDirty) lottie_animation_render_flush(mPlayer);
+    }
+    void renderRequest(int frame) {
+        if (mDirty) return;
+        mDirty = true;
+        Evas_Object *image = renderObject();
+        evas_object_image_size_get(image, &mWidth, &mHeight);
+        mBuffer = (uint32_t *)evas_object_image_data_get(image, EINA_TRUE);
+        size_t bytesperline =  evas_object_image_stride_get(image);
+        lottie_animation_render_async(mPlayer, frame, mBuffer, mWidth, mHeight, bytesperline);
+        // to force a redraw
+        evas_object_image_data_update_add(renderObject(), 0 , 0, mWidth, mWidth);
+    }
+
+    void renderFlush() {
+        if (!mDirty) return;
+        lottie_animation_render_flush(mPlayer);
+        evas_object_image_data_set(renderObject(), mBuffer);
+        evas_object_image_data_update_add(renderObject(), 0 , 0, mWidth, mHeight);
+        mDirty = false;
+    }
+private:
+   uint32_t *              mBuffer;
+   int                     mWidth;
+   int                     mHeight;
+   bool                    mDirty{false};
+};
+
+enum class  Strategy {
+  renderCpp = 0,
+  renderCppAsync,
+  renderC,
+  renderCAsync,
+  eflVg
+};
+
 class LottieView
 {
 public:
@@ -43,7 +281,7 @@ public:
         Restart,
         Reverse
     };
-    LottieView(Evas *evas, bool renderMode = true, bool asyncRender = true);
+    LottieView(Evas *evas, Strategy s = Strategy::renderCppAsync);
     ~LottieView();
     Evas_Object *getImage();
     void setSize(int w, int h);
@@ -56,8 +294,8 @@ public:
     void setSpeed(float speed) { mSpeed = speed;}
     void setRepeatCount(int count);
     void setRepeatMode(LottieView::RepeatMode mode);
-    float getFrameRate() const { return mFrameRate; }
-    long getTotalFrame() const { return mTotalFrame; }
+    float getFrameRate() const { return mRenderDelegate->frameRate(); }
+    long getTotalFrame() const { return mRenderDelegate->totalFrame(); }
 public:
     void seek(float pos);
     float getPos();
@@ -93,7 +331,7 @@ private:
     float duration() const {
         // usually we run the animation for mPlayer->duration()
         // but now run animation for segmented duration.
-        return  mPlayer->duration() * fabs(mMaxprogress - mMinProgress);
+        return  mRenderDelegate->duration() * fabs(mMaxprogress - mMinProgress);
     }
     void createVgNode(LOTNode *node, Efl_VG *root);
     void update(const std::vector<LOTNode *> &);
@@ -101,41 +339,220 @@ private:
     void update(const LOTLayerNode *, Efl_VG *parent);
     void restart();
 public:
-    int                      mw;
-    int                      mh;
-    Evas                    *mEvas;
-    Efl_VG                  *mRoot;
-    Evas_Object             *mVg;
     int                      mRepeatCount;
     LottieView::RepeatMode   mRepeatMode;
-    std::unique_ptr<rlottie::Animation>       mPlayer;
     size_t                   mCurFrame{UINT_MAX};
     Ecore_Animator          *mAnimator{nullptr};
     bool                     mLoop;
     int                      mCurCount;
     bool                     mReverse;
     bool                     mPalying;
-    Evas_Object             *mImage;
     float                    mSpeed;
-    bool                     mRenderMode;
-    bool                     mAsyncRender;
-    bool                     mDirty;
     float                    mPos;
-    float                    mFrameRate;
-    long                     mTotalFrame;
-    std::future<rlottie::Surface>        mRenderTask;
-
     //keep a segment of the animation default is [0, 1]
     float                   mMinProgress{0};
     float                   mMaxprogress{1};
+    std::unique_ptr<RenderStrategy>  mRenderDelegate;
 };
 
-class LottieViewCApi
-{
+
+class EflVgRenderStrategy : public CppApiBase {
+    int mW;
+    int mH;
 public:
-private:
-    Evas                    *mEvas;
-    Lottie_Animation        *mAnimation;
+    EflVgRenderStrategy(Evas *evas):CppApiBase(evas_object_vg_add(evas)) {}
+
+    void resize(int width, int height) {
+        mW = width;
+        mH = height;
+        evas_object_resize(renderObject(), width, height);
+    }
+
+    void renderRequest(int frame) {
+        const LOTLayerNode *root = mPlayer->renderTree(frame, mW, mH);
+        updateTree(root);
+    }
+
+    void updateTree(const LOTLayerNode * node)
+    {
+        Efl_VG *root = evas_vg_container_add(renderObject());
+        update(node, root);
+        evas_object_vg_root_node_set(renderObject(), root);
+    }
+
+    void createVgNode(LOTNode *node, Efl_VG *root)
+    {
+        Efl_VG *shape = evas_vg_shape_add(root);
+
+        //0: Path
+        const float *data = node->mPath.ptPtr;
+        if (!data) return;
+
+        for (int i = 0; i < node->mPath.elmCount; i++) {
+            switch (node->mPath.elmPtr[i]) {
+            case 0:
+                evas_vg_shape_append_move_to(shape, data[0], data[1]);
+                data += 2;
+                break;
+            case 1:
+                evas_vg_shape_append_line_to(shape, data[0], data[1]);
+                data += 2;
+                break;
+            case 2:
+                evas_vg_shape_append_cubic_to(shape, data[0], data[1], data[2], data[3], data[4], data[5]);
+                data += 6;
+                break;
+            case 3:
+                evas_vg_shape_append_close(shape);
+                break;
+            default:
+                break;
+            }
+        }
+
+        //1: Stroke
+        if (node->mStroke.enable) {
+            //Stroke Width
+            evas_vg_shape_stroke_width_set(shape, node->mStroke.width);
+
+            //Stroke Cap
+            Efl_Gfx_Cap cap;
+            switch (node->mStroke.cap) {
+            case CapFlat: cap = EFL_GFX_CAP_BUTT; break;
+            case CapSquare: cap = EFL_GFX_CAP_SQUARE; break;
+            case CapRound: cap = EFL_GFX_CAP_ROUND; break;
+            default: cap = EFL_GFX_CAP_BUTT; break;
+            }
+            evas_vg_shape_stroke_cap_set(shape, cap);
+
+            //Stroke Join
+            Efl_Gfx_Join join;
+            switch (node->mStroke.join) {
+            case JoinMiter: join = EFL_GFX_JOIN_MITER; break;
+            case JoinBevel: join = EFL_GFX_JOIN_BEVEL; break;
+            case JoinRound: join = EFL_GFX_JOIN_ROUND; break;
+            default: join = EFL_GFX_JOIN_MITER; break;
+            }
+            evas_vg_shape_stroke_join_set(shape, join);
+
+            //Stroke Dash
+            if (node->mStroke.dashArraySize > 0) {
+                int size = (node->mStroke.dashArraySize / 2);
+                Efl_Gfx_Dash *dash = static_cast<Efl_Gfx_Dash*>(malloc(sizeof(Efl_Gfx_Dash) * size));
+                if (dash) {
+                    for (int i = 0; i <= size; i+=2) {
+                        dash[i].length = node->mStroke.dashArray[i];
+                        dash[i].gap = node->mStroke.dashArray[i + 1];
+                    }
+                    evas_vg_shape_stroke_dash_set(shape, dash, size);
+                    free(dash);
+                }
+            }
+        }
+
+        //2: Fill Method
+        switch (node->mBrushType) {
+        case BrushSolid: {
+            float pa = ((float)node->mColor.a) / 255;
+            int r = (int)(((float) node->mColor.r) * pa);
+            int g = (int)(((float) node->mColor.g) * pa);
+            int b = (int)(((float) node->mColor.b) * pa);
+            int a = node->mColor.a;
+            if (node->mStroke.enable)
+              evas_vg_shape_stroke_color_set(shape, r, g, b, a);
+            else
+              evas_vg_node_color_set(shape, r, g, b, a);
+            break;
+        }
+        case BrushGradient: {
+            Efl_VG* grad = NULL;
+            if (node->mGradient.type == GradientLinear) {
+                grad = evas_vg_gradient_linear_add(root);
+                evas_vg_gradient_linear_start_set(grad, node->mGradient.start.x, node->mGradient.start.y);
+                evas_vg_gradient_linear_end_set(grad, node->mGradient.end.x, node->mGradient.end.y);
+
+            }
+            else if (node->mGradient.type == GradientRadial) {
+                grad = evas_vg_gradient_radial_add(root);
+                evas_vg_gradient_radial_center_set(grad, node->mGradient.center.x, node->mGradient.center.y);
+                evas_vg_gradient_radial_focal_set(grad, node->mGradient.focal.x, node->mGradient.focal.y);
+                evas_vg_gradient_radial_radius_set(grad, node->mGradient.cradius);
+            }
+
+            if (grad) {
+                //Gradient Stop
+                Efl_Gfx_Gradient_Stop* stops = static_cast<Efl_Gfx_Gradient_Stop*>(malloc(sizeof(Efl_Gfx_Gradient_Stop) * node->mGradient.stopCount));
+                if (stops) {
+                    for (unsigned int i = 0; i < node->mGradient.stopCount; i++) {
+                        stops[i].offset = node->mGradient.stopPtr[i].pos;
+                        float pa = ((float)node->mGradient.stopPtr[i].a) / 255;
+                        stops[i].r = (int)(((float)node->mGradient.stopPtr[i].r) * pa);
+                        stops[i].g = (int)(((float)node->mGradient.stopPtr[i].g) * pa);
+                        stops[i].b = (int)(((float)node->mGradient.stopPtr[i].b) * pa);
+                        stops[i].a = node->mGradient.stopPtr[i].a;
+                    }
+                    evas_vg_gradient_stop_set(grad, stops, node->mGradient.stopCount);
+                    free(stops);
+                }
+                if (node->mStroke.enable)
+                  evas_vg_shape_stroke_fill_set(shape, grad);
+                else
+                  evas_vg_shape_fill_set(shape, grad);
+            }
+            break;
+        }
+        default:
+          break;
+        }
+
+        //3: Fill Rule
+//        if (node->mFillRule == FillEvenOdd)
+//          efl_gfx_shape_fill_rule_set(shape, EFL_GFX_FILL_RULE_ODD_EVEN);
+//        else if (node->mFillRule == FillWinding)
+//          efl_gfx_shape_fill_rule_set(shape, EFL_GFX_FILL_RULE_WINDING);
+    }
+
+    void update(const std::vector<LOTNode *> &renderList)
+    {
+        Efl_VG *root = evas_vg_container_add(renderObject());
+        for(auto i : renderList) {
+            createVgNode(i, root);
+        }
+        evas_object_vg_root_node_set(renderObject(), root);
+    }
+
+    void update(const LOTLayerNode * node, Efl_VG *parent)
+    {
+        // if the layer is invisible return
+        if (!node->mVisible) return;
+
+        // check if this layer is a container layer
+        bool hasMatte = false;
+        if (node->mLayerList.size) {
+            for (unsigned int i = 0; i < node->mLayerList.size; i++) {
+                if (hasMatte) {
+                    hasMatte = false;
+                    continue;
+                }
+                // if the layer has matte then
+                // the next layer has to be rendered using this layer
+                // as matte source
+                if (node->mLayerList.ptr[i]->mMatte != MatteNone) {
+                    hasMatte = true;
+                    printf("Matte is not supported Yet\n");
+                    continue;
+                }
+                update(node->mLayerList.ptr[i], parent);
+            }
+        }
+
+        // check if this layer has drawable
+        if (node->mNodeList.size) {
+            for (unsigned int i = 0; i < node->mNodeList.size; i++) {
+                createVgNode(node->mNodeList.ptr[i], parent);
+            }
+        }
+    }
 };
 
 #endif //LOTTIEVIEW_H
index 2d6bd40..858bc76 100644 (file)
@@ -147,7 +147,7 @@ create_layout(Evas_Object *parent, const char *file)
    elm_layout_file_set(layout, edjPath.c_str(), "layout");
 
    //LOTTIEVIEW
-   LottieView *view = new LottieView(evas_object_evas_get(layout), renderMode);
+   LottieView *view = new LottieView(evas_object_evas_get(layout), Strategy::renderCppAsync);
    view->setFilePath(file);
    view->setSize(500, 500);
 
index 8eb4dbb..843c6f7 100644 (file)
@@ -35,10 +35,9 @@ using namespace std;
 class LottieViewTest
 {
 public:
-  LottieViewTest(EvasApp *app, bool renderMode) {
+  LottieViewTest(EvasApp *app, Strategy st) {
+      mStrategy = st;
       mApp = app;
-      mRenderMode = renderMode;
-      ecore_animator_frametime_set(1.0/120.0);
   }
 
   void show(int numberOfImage) {
@@ -56,7 +55,7 @@ public:
     int resourceSize = resource.size();
     for (int i = 0 ; i < numberOfImage; i++) {
         int index = i % resourceSize;
-        std::unique_ptr<LottieView> view(new LottieView(mApp->evas(), mRenderMode));
+        std::unique_ptr<LottieView> view(new LottieView(mApp->evas(), mStrategy));
         view->setFilePath(resource[index].c_str());
         view->setPos(posx, posy);
         view->setSize(vw, vh);
@@ -85,7 +84,7 @@ public:
 
 public:
   EvasApp     *mApp;
-  bool         mRenderMode = false;
+  Strategy     mStrategy;
   std::vector<std::unique_ptr<LottieView>>   mViews;
 };
 
@@ -106,16 +105,31 @@ onRenderPreCb(void *data, void *extra)
 int
 main(int argc, char **argv)
 {
+    if (argc > 1) {
+        if (!strcmp(argv[1],"--help") || !strcmp(argv[1],"-h")) {
+            printf("Usage ./lottieviewTest 1 \n");
+            printf("\t 0  - Test Lottie SYNC Renderer with CPP API\n");
+            printf("\t 1  - Test Lottie ASYNC Renderer with CPP API\n");
+            printf("\t 2  - Test Lottie SYNC Renderer with C API\n");
+            printf("\t 3  - Test Lottie ASYNC Renderer with C API\n");
+            printf("\t 4  - Test Lottie Tree Api using Efl VG Render\n");
+            printf("\t Default is ./lottieviewTest 1 \n");
+            return 0;
+        }
+    } else {
+        printf("Run ./lottieviewTest -h  for more option\n");
+    }
+
    EvasApp *app = new EvasApp(800, 800);
    app->setup();
 
-   bool renderMode = true;
+   Strategy st = Strategy::renderCppAsync;
    if (argc > 1) {
-       if (!strcmp(argv[1],"--disable-render"))
-           renderMode = false;
+       int option = atoi(argv[1]);
+       st = static_cast<Strategy>(option);
    }
-   LottieViewTest *view = new LottieViewTest(app, renderMode);
-   view->show(250);
+   LottieViewTest *view = new LottieViewTest(app, st);
+   view->show(150);
 
    app->addExitCb(onExitCb, view);
    app->addRenderPreCb(onRenderPreCb, view);
index 73d8ce8..aaba0ac 100644 (file)
@@ -57,7 +57,7 @@ public:
 
 private:
   void show() {
-      mView = std::make_unique<LottieView>(mApp->evas(), mRenderMode);
+      mView = std::make_unique<LottieView>(mApp->evas(), Strategy::renderCAsync);
       mView->setFilePath(mResourceList[mCurIndex].c_str());
       mView->setPos(0, 0);
       mView->setSize(mApp->width(), mApp->height());