added lottie2gif converter for both cmake and meson build
[platform/core/uifw/lottie-player.git] / example / lottieview.h
1 /* 
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
3  * 
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  * 
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  * 
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #ifndef LOTTIEVIEW_H
20 #define LOTTIEVIEW_H
21
22 #ifndef EFL_BETA_API_SUPPORT
23 #define EFL_BETA_API_SUPPORT
24 #endif
25
26 #ifndef EFL_EO_API_SUPPORT
27 #define EFL_EO_API_SUPPORT
28 #endif
29
30 #include <Eo.h>
31 #include <Efl.h>
32 #include <Evas.h>
33 #include <Ecore.h>
34 #include <Ecore_Evas.h>
35 #include "rlottie.h"
36 #include "rlottie_capi.h"
37 #include<future>
38 #include <cmath>
39
40 class RenderStrategy {
41 public:
42     virtual ~RenderStrategy() {
43         evas_object_del(renderObject());
44     }
45     RenderStrategy(Evas_Object *obj):_renderObject(obj){}
46     virtual rlottie::Animation *player() {return nullptr;}
47     virtual void loadFromFile(const char *filePath) = 0;
48     virtual void loadFromData(const std::string &jsonData, const std::string &key, const std::string &resourcePath) = 0;
49     virtual size_t totalFrame() = 0;
50     virtual double frameRate() = 0;
51     virtual size_t frameAtPos(double pos) = 0;
52     virtual double duration() = 0;
53     virtual void renderRequest(int frame) = 0;
54     virtual void renderFlush() {}
55     virtual void resize(int width, int height) = 0;
56     virtual void setPos(int x, int y) {evas_object_move(renderObject(), x, y);}
57     void show() {evas_object_show(_renderObject);}
58     void hide() {evas_object_hide(_renderObject);}
59     Evas_Object* renderObject() const {return _renderObject;}
60 private:
61     Evas_Object *_renderObject;
62 };
63
64 class CppApiBase : public RenderStrategy {
65 public:
66     CppApiBase(Evas_Object *renderObject): RenderStrategy(renderObject) {}
67     rlottie::Animation *player() {return mPlayer.get();}
68     void loadFromFile(const char *filePath)
69     {
70         mPlayer = rlottie::Animation::loadFromFile(filePath);
71
72         if (!mPlayer) {
73             printf("load failed file %s\n", filePath);
74         }
75     }
76
77     void loadFromData(const std::string &jsonData, const std::string &key, const std::string &resourcePath)
78     {
79         mPlayer = rlottie::Animation::loadFromData(jsonData, key, resourcePath);
80         if (!mPlayer) {
81             printf("load failed from data\n");
82         }
83     }
84
85     size_t totalFrame() {
86         return mPlayer->totalFrame();
87
88     }
89     double duration() {
90         return mPlayer->duration();
91     }
92
93     double frameRate() {
94         return mPlayer->frameRate();
95     }
96
97     size_t frameAtPos(double pos) {
98         return  mPlayer->frameAtPos(pos);
99     }
100 protected:
101    std::unique_ptr<rlottie::Animation>       mPlayer;
102 };
103
104 class RlottieRenderStrategyCBase : public RenderStrategy {
105 public:
106     RlottieRenderStrategyCBase(Evas *evas):RenderStrategy(evas_object_image_filled_add(evas)) {
107         evas_object_image_colorspace_set(renderObject(), EVAS_COLORSPACE_ARGB8888);
108         evas_object_image_alpha_set(renderObject(), EINA_TRUE);
109     }
110     void resize(int width, int height) {
111         evas_object_resize(renderObject(), width, height);
112         evas_object_image_size_set(renderObject(), width, height);
113     }
114 };
115
116 class RlottieRenderStrategy : public CppApiBase {
117 public:
118     RlottieRenderStrategy(Evas *evas):CppApiBase(evas_object_image_filled_add(evas)) {
119         evas_object_image_colorspace_set(renderObject(), EVAS_COLORSPACE_ARGB8888);
120         evas_object_image_alpha_set(renderObject(), EINA_TRUE);
121     }
122     void resize(int width, int height) {
123         evas_object_resize(renderObject(), width, height);
124         evas_object_image_size_set(renderObject(), width, height);
125     }
126 };
127
128 class RlottieRenderStrategy_CPP : public RlottieRenderStrategy {
129 public:
130     RlottieRenderStrategy_CPP(Evas *evas):RlottieRenderStrategy(evas) {}
131
132     void renderRequest(int frame) {
133         int width , height;
134         Evas_Object *image = renderObject();
135         evas_object_image_size_get(image, &width, &height);
136         auto buffer = (uint32_t *)evas_object_image_data_get(image, EINA_TRUE);
137         size_t bytesperline =  evas_object_image_stride_get(image);
138         rlottie::Surface surface(buffer, width, height, bytesperline);
139         mPlayer->renderSync(frame, surface);
140         evas_object_image_data_set(image, surface.buffer());
141         evas_object_image_data_update_add(image, 0 , 0, surface.width(), surface.height());
142     }
143 };
144
145 class RlottieRenderStrategy_CPP_ASYNC : public RlottieRenderStrategy_CPP {
146 public:
147     RlottieRenderStrategy_CPP_ASYNC(Evas *evas):RlottieRenderStrategy_CPP(evas) {}
148     ~RlottieRenderStrategy_CPP_ASYNC() {
149         if (mRenderTask.valid())
150             mRenderTask.get();
151     }
152     void renderRequest(int frame) {
153         if (mRenderTask.valid()) return;
154         mDirty = true;
155         int width , height;
156         Evas_Object *image = renderObject();
157         evas_object_image_size_get(image, &width, &height);
158         auto buffer = (uint32_t *)evas_object_image_data_get(image, EINA_TRUE);
159         size_t bytesperline =  evas_object_image_stride_get(image);
160         rlottie::Surface surface(buffer, width, height, bytesperline);
161         mRenderTask = mPlayer->render(frame, surface);
162         // to force a redraw
163         evas_object_image_data_update_add(renderObject(), 0 , 0, surface.width(), surface.height());
164     }
165
166     void renderFlush() {
167         if (!mDirty) return;
168
169         if (!mRenderTask.valid()) return;
170
171         auto surface = mRenderTask.get();
172         evas_object_image_data_set(renderObject(), surface.buffer());
173         evas_object_image_data_update_add(renderObject(), 0 , 0, surface.width(), surface.height());
174         mDirty = false;
175     }
176 private:
177    std::future<rlottie::Surface>        mRenderTask;
178    bool                                 mDirty{true};
179 };
180
181
182 class RlottieRenderStrategy_C : public RlottieRenderStrategyCBase {
183 public:
184     RlottieRenderStrategy_C(Evas *evas):RlottieRenderStrategyCBase(evas) {}
185     ~RlottieRenderStrategy_C() {
186         if (mPlayer) lottie_animation_destroy(mPlayer);
187     }
188     void loadFromFile(const char *filePath)
189     {
190         mPlayer = lottie_animation_from_file(filePath);
191
192         if (!mPlayer) {
193             printf("load failed file %s\n", filePath);
194         }
195     }
196
197     void loadFromData(const std::string &jsonData, const std::string &key, const std::string &resourcePath)
198     {
199         mPlayer = lottie_animation_from_data(jsonData.c_str(), key.c_str(), resourcePath.c_str());
200         if (!mPlayer) {
201             printf("load failed from data\n");
202         }
203     }
204
205     size_t totalFrame() {
206         return lottie_animation_get_totalframe(mPlayer);
207
208     }
209
210     double frameRate() {
211         return lottie_animation_get_framerate(mPlayer);
212     }
213
214     size_t frameAtPos(double pos) {
215         return  lottie_animation_get_frame_at_pos(mPlayer, pos);
216     }
217
218     double duration() {
219         return lottie_animation_get_duration(mPlayer);
220     }
221
222     void renderRequest(int frame) {
223         int width , height;
224         Evas_Object *image = renderObject();
225         evas_object_image_size_get(image, &width, &height);
226         auto buffer = (uint32_t *)evas_object_image_data_get(image, EINA_TRUE);
227         size_t bytesperline =  evas_object_image_stride_get(image);
228         lottie_animation_render_async(mPlayer, frame, buffer, width, height, bytesperline);
229         lottie_animation_render_flush(mPlayer);
230         evas_object_image_data_set(image, buffer);
231         evas_object_image_data_update_add(image, 0 , 0, width, height);
232     }
233
234 protected:
235    Lottie_Animation       *mPlayer;
236 };
237
238 class RlottieRenderStrategy_C_ASYNC : public RlottieRenderStrategy_C {
239 public:
240     RlottieRenderStrategy_C_ASYNC(Evas *evas):RlottieRenderStrategy_C(evas) {}
241     ~RlottieRenderStrategy_C_ASYNC() {
242         if (mDirty) lottie_animation_render_flush(mPlayer);
243     }
244     void renderRequest(int frame) {
245         if (mDirty) return;
246         mDirty = true;
247         Evas_Object *image = renderObject();
248         evas_object_image_size_get(image, &mWidth, &mHeight);
249         mBuffer = (uint32_t *)evas_object_image_data_get(image, EINA_TRUE);
250         size_t bytesperline =  evas_object_image_stride_get(image);
251         lottie_animation_render_async(mPlayer, frame, mBuffer, mWidth, mHeight, bytesperline);
252         // to force a redraw
253         evas_object_image_data_update_add(renderObject(), 0 , 0, mWidth, mWidth);
254     }
255
256     void renderFlush() {
257         if (!mDirty) return;
258         lottie_animation_render_flush(mPlayer);
259         evas_object_image_data_set(renderObject(), mBuffer);
260         evas_object_image_data_update_add(renderObject(), 0 , 0, mWidth, mHeight);
261         mDirty = false;
262     }
263 private:
264    uint32_t *              mBuffer;
265    int                     mWidth;
266    int                     mHeight;
267    bool                    mDirty{false};
268 };
269
270 enum class  Strategy {
271   renderCpp = 0,
272   renderCppAsync,
273   renderC,
274   renderCAsync,
275   eflVg
276 };
277
278 class LottieView
279 {
280 public:
281     enum class RepeatMode {
282         Restart,
283         Reverse
284     };
285     LottieView(Evas *evas, Strategy s = Strategy::renderCppAsync);
286     ~LottieView();
287     rlottie::Animation *player(){return mRenderDelegate->player();}
288     Evas_Object *getImage();
289     void setSize(int w, int h);
290     void setPos(int x, int y);
291     void setFilePath(const char *filePath);
292     void loadFromData(const std::string &jsonData, const std::string &key, const std::string &resourcePath="");
293     void show();
294     void hide();
295     void loop(bool loop);
296     void setSpeed(float speed) { mSpeed = speed;}
297     void setRepeatCount(int count);
298     void setRepeatMode(LottieView::RepeatMode mode);
299     float getFrameRate() const { return mRenderDelegate->frameRate(); }
300     long getTotalFrame() const { return mRenderDelegate->totalFrame(); }
301 public:
302     void seek(float pos);
303     float getPos();
304     void finished();
305     void play();
306     void pause();
307     void stop();
308     void render();
309     void initializeBufferObject(Evas *evas);
310     void setMinProgress(float progress)
311     {
312         //clamp it to [0,1]
313         mMinProgress = progress;
314     }
315     void setMaxProgress(float progress)
316     {
317         //clamp it to [0,1]
318         mMaxprogress = progress;
319     }
320 private:
321     float mapProgress(float progress) {
322         //clamp it to the segment
323         progress = (mMinProgress + (mMaxprogress - mMinProgress) * progress);
324
325         // currently playing and in reverse mode
326         if (mPalying && mReverse)
327             progress = mMaxprogress > mMinProgress ?
328                         mMaxprogress - progress : mMinProgress - progress;
329
330
331         return progress;
332     }
333     float duration() const {
334         // usually we run the animation for mPlayer->duration()
335         // but now run animation for segmented duration.
336         return  mRenderDelegate->duration() * fabs(mMaxprogress - mMinProgress);
337     }
338     void createVgNode(LOTNode *node, Efl_VG *root);
339     void update(const std::vector<LOTNode *> &);
340     void updateTree(const LOTLayerNode *);
341     void update(const LOTLayerNode *, Efl_VG *parent);
342     void restart();
343 public:
344     int                      mRepeatCount;
345     LottieView::RepeatMode   mRepeatMode;
346     size_t                   mCurFrame{UINT_MAX};
347     Ecore_Animator          *mAnimator{nullptr};
348     bool                     mLoop;
349     int                      mCurCount;
350     bool                     mReverse;
351     bool                     mPalying;
352     float                    mSpeed;
353     float                    mPos;
354     //keep a segment of the animation default is [0, 1]
355     float                   mMinProgress{0};
356     float                   mMaxprogress{1};
357     std::unique_ptr<RenderStrategy>  mRenderDelegate;
358 };
359
360
361 class EflVgRenderStrategy : public CppApiBase {
362     int mW;
363     int mH;
364 public:
365     EflVgRenderStrategy(Evas *evas):CppApiBase(evas_object_vg_add(evas)) {}
366
367     void resize(int width, int height) {
368         mW = width;
369         mH = height;
370         evas_object_resize(renderObject(), width, height);
371     }
372
373     void renderRequest(int frame) {
374         const LOTLayerNode *root = mPlayer->renderTree(frame, mW, mH);
375         updateTree(root);
376     }
377
378     void updateTree(const LOTLayerNode * node)
379     {
380         Efl_VG *root = evas_vg_container_add(renderObject());
381         update(node, root);
382         evas_object_vg_root_node_set(renderObject(), root);
383     }
384
385     void createVgNode(LOTNode *node, Efl_VG *root)
386     {
387         Efl_VG *shape = evas_vg_shape_add(root);
388
389         //0: Path
390         const float *data = node->mPath.ptPtr;
391         if (!data) return;
392
393         for (int i = 0; i < node->mPath.elmCount; i++) {
394             switch (node->mPath.elmPtr[i]) {
395             case 0:
396                 evas_vg_shape_append_move_to(shape, data[0], data[1]);
397                 data += 2;
398                 break;
399             case 1:
400                 evas_vg_shape_append_line_to(shape, data[0], data[1]);
401                 data += 2;
402                 break;
403             case 2:
404                 evas_vg_shape_append_cubic_to(shape, data[0], data[1], data[2], data[3], data[4], data[5]);
405                 data += 6;
406                 break;
407             case 3:
408                 evas_vg_shape_append_close(shape);
409                 break;
410             default:
411                 break;
412             }
413         }
414
415         //1: Stroke
416         if (node->mStroke.enable) {
417             //Stroke Width
418             evas_vg_shape_stroke_width_set(shape, node->mStroke.width);
419
420             //Stroke Cap
421             Efl_Gfx_Cap cap;
422             switch (node->mStroke.cap) {
423             case CapFlat: cap = EFL_GFX_CAP_BUTT; break;
424             case CapSquare: cap = EFL_GFX_CAP_SQUARE; break;
425             case CapRound: cap = EFL_GFX_CAP_ROUND; break;
426             default: cap = EFL_GFX_CAP_BUTT; break;
427             }
428             evas_vg_shape_stroke_cap_set(shape, cap);
429
430             //Stroke Join
431             Efl_Gfx_Join join;
432             switch (node->mStroke.join) {
433             case JoinMiter: join = EFL_GFX_JOIN_MITER; break;
434             case JoinBevel: join = EFL_GFX_JOIN_BEVEL; break;
435             case JoinRound: join = EFL_GFX_JOIN_ROUND; break;
436             default: join = EFL_GFX_JOIN_MITER; break;
437             }
438             evas_vg_shape_stroke_join_set(shape, join);
439
440             //Stroke Dash
441             if (node->mStroke.dashArraySize > 0) {
442                 int size = (node->mStroke.dashArraySize / 2);
443                 Efl_Gfx_Dash *dash = static_cast<Efl_Gfx_Dash*>(malloc(sizeof(Efl_Gfx_Dash) * size));
444                 if (dash) {
445                     for (int i = 0; i <= size; i+=2) {
446                         dash[i].length = node->mStroke.dashArray[i];
447                         dash[i].gap = node->mStroke.dashArray[i + 1];
448                     }
449                     evas_vg_shape_stroke_dash_set(shape, dash, size);
450                     free(dash);
451                 }
452             }
453         }
454
455         //2: Fill Method
456         switch (node->mBrushType) {
457         case BrushSolid: {
458             float pa = ((float)node->mColor.a) / 255;
459             int r = (int)(((float) node->mColor.r) * pa);
460             int g = (int)(((float) node->mColor.g) * pa);
461             int b = (int)(((float) node->mColor.b) * pa);
462             int a = node->mColor.a;
463             if (node->mStroke.enable)
464               evas_vg_shape_stroke_color_set(shape, r, g, b, a);
465             else
466               evas_vg_node_color_set(shape, r, g, b, a);
467             break;
468         }
469         case BrushGradient: {
470             Efl_VG* grad = NULL;
471             if (node->mGradient.type == GradientLinear) {
472                 grad = evas_vg_gradient_linear_add(root);
473                 evas_vg_gradient_linear_start_set(grad, node->mGradient.start.x, node->mGradient.start.y);
474                 evas_vg_gradient_linear_end_set(grad, node->mGradient.end.x, node->mGradient.end.y);
475
476             }
477             else if (node->mGradient.type == GradientRadial) {
478                 grad = evas_vg_gradient_radial_add(root);
479                 evas_vg_gradient_radial_center_set(grad, node->mGradient.center.x, node->mGradient.center.y);
480                 evas_vg_gradient_radial_focal_set(grad, node->mGradient.focal.x, node->mGradient.focal.y);
481                 evas_vg_gradient_radial_radius_set(grad, node->mGradient.cradius);
482             }
483
484             if (grad) {
485                 //Gradient Stop
486                 Efl_Gfx_Gradient_Stop* stops = static_cast<Efl_Gfx_Gradient_Stop*>(malloc(sizeof(Efl_Gfx_Gradient_Stop) * node->mGradient.stopCount));
487                 if (stops) {
488                     for (unsigned int i = 0; i < node->mGradient.stopCount; i++) {
489                         stops[i].offset = node->mGradient.stopPtr[i].pos;
490                         float pa = ((float)node->mGradient.stopPtr[i].a) / 255;
491                         stops[i].r = (int)(((float)node->mGradient.stopPtr[i].r) * pa);
492                         stops[i].g = (int)(((float)node->mGradient.stopPtr[i].g) * pa);
493                         stops[i].b = (int)(((float)node->mGradient.stopPtr[i].b) * pa);
494                         stops[i].a = node->mGradient.stopPtr[i].a;
495                     }
496                     evas_vg_gradient_stop_set(grad, stops, node->mGradient.stopCount);
497                     free(stops);
498                 }
499                 if (node->mStroke.enable)
500                   evas_vg_shape_stroke_fill_set(shape, grad);
501                 else
502                   evas_vg_shape_fill_set(shape, grad);
503             }
504             break;
505         }
506         default:
507           break;
508         }
509
510         //3: Fill Rule
511 //        if (node->mFillRule == FillEvenOdd)
512 //          efl_gfx_shape_fill_rule_set(shape, EFL_GFX_FILL_RULE_ODD_EVEN);
513 //        else if (node->mFillRule == FillWinding)
514 //          efl_gfx_shape_fill_rule_set(shape, EFL_GFX_FILL_RULE_WINDING);
515     }
516
517     void update(const std::vector<LOTNode *> &renderList)
518     {
519         Efl_VG *root = evas_vg_container_add(renderObject());
520         for(auto i : renderList) {
521             createVgNode(i, root);
522         }
523         evas_object_vg_root_node_set(renderObject(), root);
524     }
525
526     void update(const LOTLayerNode * node, Efl_VG *parent)
527     {
528         // if the layer is invisible return
529         if (!node->mVisible) return;
530
531         // check if this layer is a container layer
532         bool hasMatte = false;
533         if (node->mLayerList.size) {
534             for (unsigned int i = 0; i < node->mLayerList.size; i++) {
535                 if (hasMatte) {
536                     hasMatte = false;
537                     continue;
538                 }
539                 // if the layer has matte then
540                 // the next layer has to be rendered using this layer
541                 // as matte source
542                 if (node->mLayerList.ptr[i]->mMatte != MatteNone) {
543                     hasMatte = true;
544                     printf("Matte is not supported Yet\n");
545                     continue;
546                 }
547                 update(node->mLayerList.ptr[i], parent);
548             }
549         }
550
551         // check if this layer has drawable
552         if (node->mNodeList.size) {
553             for (unsigned int i = 0; i < node->mNodeList.size; i++) {
554                 createVgNode(node->mNodeList.ptr[i], parent);
555             }
556         }
557     }
558 };
559
560 #endif //LOTTIEVIEW_H