diff --git a/example/demo.cpp b/example/demo.cpp index 9b14d5502e..368e29f773 100644 --- a/example/demo.cpp +++ b/example/demo.cpp @@ -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()); diff --git a/example/lottieview.cpp b/example/lottieview.cpp index 383aa6dd98..755b2e0d8b 100644 --- a/example/lottieview.cpp +++ b/example/lottieview.cpp @@ -34,222 +34,7 @@ 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(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(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 LottieView::update(const std::vector &renderList) -{ - Efl_VG *root = evas_vg_container_add(mVg); - for(auto i : renderList) { - createVgNode(i, root); - } - 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); - } - } - - // 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); - } - } -} - -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); - } -} - -LottieView::LottieView(Evas *evas, bool renderMode, bool asyncRender):mVg(nullptr), mImage(nullptr) -{ - mPlayer = nullptr; +LottieView::LottieView(Evas *evas, Strategy s) { mPalying = false; mReverse = false; mRepeatCount = 0; @@ -257,86 +42,66 @@ LottieView::LottieView(Evas *evas, bool renderMode, bool asyncRender):mVg(nullpt mLoop = false; mSpeed = 1; - mEvas = evas; - mRenderMode = renderMode; - mAsyncRender = asyncRender; - - initializeBufferObject(evas); + switch (s) { + case Strategy::renderCpp: { + mRenderDelegate = std::make_unique(evas); + break; + } + case Strategy::renderCppAsync: { + mRenderDelegate = std::make_unique(evas); + break; + } + case Strategy::renderC: { + mRenderDelegate = std::make_unique(evas); + break; + } + case Strategy::renderCAsync: { + mRenderDelegate = std::make_unique(evas); + break; + } + case Strategy::eflVg: { + mRenderDelegate = std::make_unique(evas); + break; + } + default: + mRenderDelegate = std::make_unique(evas); + break; + } } 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; + if (mCurFrame == mRenderDelegate->frameAtPos(mPos)) return; - mCurFrame = mPlayer->frameAtPos(mPos); + mCurFrame = mRenderDelegate->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); - } + 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) diff --git a/example/lottieview.h b/example/lottieview.h index f979c56a9c..a0425236a2 100644 --- a/example/lottieview.h +++ b/example/lottieview.h @@ -36,6 +36,244 @@ #include "rlottie_capi.h" #include #include + +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 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 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 &); @@ -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 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 mRenderTask; - //keep a segment of the animation default is [0, 1] float mMinProgress{0}; float mMaxprogress{1}; + std::unique_ptr 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(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(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 &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 diff --git a/example/lottieviewer.cpp b/example/lottieviewer.cpp index 2d6bd4086a..858bc760cf 100644 --- a/example/lottieviewer.cpp +++ b/example/lottieviewer.cpp @@ -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); diff --git a/example/lottieviewtest.cpp b/example/lottieviewtest.cpp index 8eb4dbb057..843c6f7f1d 100644 --- a/example/lottieviewtest.cpp +++ b/example/lottieviewtest.cpp @@ -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 view(new LottieView(mApp->evas(), mRenderMode)); + std::unique_ptr 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> 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(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); diff --git a/example/uxsampletest.cpp b/example/uxsampletest.cpp index 73d8ce8653..aaba0ac416 100644 --- a/example/uxsampletest.cpp +++ b/example/uxsampletest.cpp @@ -57,7 +57,7 @@ public: private: void show() { - mView = std::make_unique(mApp->evas(), mRenderMode); + mView = std::make_unique(mApp->evas(), Strategy::renderCAsync); mView->setFilePath(mResourceList[mCurIndex].c_str()); mView->setPos(0, 0); mView->setSize(mApp->width(), mApp->height());