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()
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()
void LottieView::play()
{
- if (!mPlayer) return;
-
if (mAnimator) ecore_animator_del(mAnimator);
mAnimator = ecore_animator_timeline_add(duration()/mSpeed, animator, this);
mReverse = false;
void LottieView::restart()
{
- if (!mPlayer) return;
-
mCurCount--;
if (mLoop || mRepeatCount) {
if (mRepeatMode == LottieView::RepeatMode::Reverse)
#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:
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);
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();
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 *> &);
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