1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
3 
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10 
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13 
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 
23 #ifndef LOTTIEVIEW_H
24 #define LOTTIEVIEW_H
25 
26 #ifndef EFL_BETA_API_SUPPORT
27 #define EFL_BETA_API_SUPPORT
28 #endif
29 
30 #ifndef EFL_EO_API_SUPPORT
31 #define EFL_EO_API_SUPPORT
32 #endif
33 
34 #include <Eo.h>
35 #include <Efl.h>
36 #include <Evas.h>
37 #include <Ecore.h>
38 #include <Ecore_Evas.h>
39 #include "rlottie.h"
40 #include "rlottie_capi.h"
41 #include<future>
42 #include <cmath>
43 #include <algorithm>
44 
45 class RenderStrategy {
46 public:
~RenderStrategy()47     virtual ~RenderStrategy() {
48         evas_object_del(renderObject());
49     }
_renderObject(obj)50     RenderStrategy(Evas_Object *obj, bool useImage = true):_renderObject(obj), _useImage(useImage){
51         addCallback();
52     }
player()53     virtual rlottie::Animation *player() {return nullptr;}
54     virtual void loadFromFile(const char *filePath) = 0;
55     virtual void loadFromData(const std::string &jsonData, const std::string &key, const std::string &resourcePath) = 0;
56     virtual size_t totalFrame() = 0;
57     virtual double frameRate() = 0;
58     virtual size_t frameAtPos(double pos) = 0;
59     virtual double duration() = 0;
render(int frame)60     void render(int frame) {
61         _renderCount++;
62         _redraw = renderRequest(frame);
63         if (_redraw && _useImage)
64             evas_object_image_pixels_dirty_set(renderObject(), EINA_TRUE);
65     }
dataCb()66     void dataCb() {
67         if (_redraw && _useImage) {
68             evas_object_image_data_set(renderObject(), buffer());
69         }
70         _redraw = false;
71     }
72     virtual void resize(int width, int height) = 0;
setPos(int x,int y)73     virtual void setPos(int x, int y) {evas_object_move(renderObject(), x, y);}
74     typedef std::tuple<size_t, size_t> MarkerFrame;
75     virtual MarkerFrame findFrameAtMarker(const std::string &markerName) = 0;
show()76     void show() {evas_object_show(_renderObject);}
hide()77     void hide() {evas_object_hide(_renderObject);}
78     void addCallback();
renderObject()79     Evas_Object* renderObject() const {return _renderObject;}
renderCount()80     size_t renderCount() const {return _renderCount;}
81 protected:
82     virtual bool renderRequest(int) = 0;
83     virtual uint32_t* buffer() = 0;
84 private:
85     size_t       _renderCount{0};
86     bool         _redraw{false};
87     Evas_Object *_renderObject;
88     bool         _useImage{true};
89 };
90 
91 class CppApiBase : public RenderStrategy {
92 public:
CppApiBase(Evas_Object * renderObject)93     CppApiBase(Evas_Object *renderObject): RenderStrategy(renderObject) {}
player()94     rlottie::Animation *player() {return mPlayer.get();}
loadFromFile(const char * filePath)95     void loadFromFile(const char *filePath)
96     {
97         mPlayer = rlottie::Animation::loadFromFile(filePath);
98 
99         if (!mPlayer) {
100             printf("load failed file %s\n", filePath);
101         }
102     }
103 
loadFromData(const std::string & jsonData,const std::string & key,const std::string & resourcePath)104     void loadFromData(const std::string &jsonData, const std::string &key, const std::string &resourcePath)
105     {
106         mPlayer = rlottie::Animation::loadFromData(jsonData, key, resourcePath);
107 
108         if (!mPlayer) {
109             printf("load failed from data\n");
110         }
111     }
112 
totalFrame()113     size_t totalFrame() {
114         return mPlayer->totalFrame();
115 
116     }
duration()117     double duration() {
118         return mPlayer->duration();
119     }
120 
frameRate()121     double frameRate() {
122         return mPlayer->frameRate();
123     }
124 
frameAtPos(double pos)125     size_t frameAtPos(double pos) {
126         return  mPlayer->frameAtPos(pos);
127     }
128 
findFrameAtMarker(const std::string & markerName)129     MarkerFrame findFrameAtMarker(const std::string &markerName)
130     {
131         auto markerList = mPlayer->markers();
132         auto marker = std::find_if(markerList.begin(), markerList.end()
133                                    , [&markerName](const auto& e) {
134                                         return std::get<0>(e) == markerName;
135                                      });
136         if (marker == markerList.end())
137            return std::make_tuple(0, 0);
138         return std::make_tuple(std::get<1>(*marker), std::get<2>(*marker));
139     }
140 
141 protected:
142    std::unique_ptr<rlottie::Animation>       mPlayer;
143 };
144 
145 class RlottieRenderStrategyCBase : public RenderStrategy {
146 public:
RlottieRenderStrategyCBase(Evas * evas)147     RlottieRenderStrategyCBase(Evas *evas):RenderStrategy(evas_object_image_filled_add(evas)) {
148         evas_object_image_colorspace_set(renderObject(), EVAS_COLORSPACE_ARGB8888);
149         evas_object_image_alpha_set(renderObject(), EINA_TRUE);
150     }
resize(int width,int height)151     void resize(int width, int height) {
152         evas_object_resize(renderObject(), width, height);
153         evas_object_image_size_set(renderObject(), width, height);
154     }
155 };
156 
157 class RlottieRenderStrategy : public CppApiBase {
158 public:
RlottieRenderStrategy(Evas * evas)159     RlottieRenderStrategy(Evas *evas):CppApiBase(evas_object_image_filled_add(evas)) {
160         evas_object_image_colorspace_set(renderObject(), EVAS_COLORSPACE_ARGB8888);
161         evas_object_image_alpha_set(renderObject(), EINA_TRUE);
162     }
resize(int width,int height)163     void resize(int width, int height) {
164         evas_object_resize(renderObject(), width, height);
165         evas_object_image_size_set(renderObject(), width, height);
166     }
167 };
168 
169 class RlottieRenderStrategy_CPP : public RlottieRenderStrategy {
170 public:
RlottieRenderStrategy_CPP(Evas * evas)171     RlottieRenderStrategy_CPP(Evas *evas):RlottieRenderStrategy(evas) {}
172 
renderRequest(int frame)173     bool renderRequest(int frame) {
174         int width , height;
175         Evas_Object *image = renderObject();
176         evas_object_image_size_get(image, &width, &height);
177         mBuffer = (uint32_t *)evas_object_image_data_get(image, EINA_TRUE);
178         size_t bytesperline =  evas_object_image_stride_get(image);
179         rlottie::Surface surface(mBuffer, width, height, bytesperline);
180         mPlayer->renderSync(frame, surface);
181         return true;
182     }
buffer()183     uint32_t* buffer() {
184         return mBuffer;
185     }
186 
187 private:
188     uint32_t *              mBuffer;
189 };
190 
191 class RlottieRenderStrategy_CPP_ASYNC : public RlottieRenderStrategy {
192 public:
RlottieRenderStrategy_CPP_ASYNC(Evas * evas)193     RlottieRenderStrategy_CPP_ASYNC(Evas *evas):RlottieRenderStrategy(evas) {}
~RlottieRenderStrategy_CPP_ASYNC()194     ~RlottieRenderStrategy_CPP_ASYNC() {
195         if (mRenderTask.valid())
196             mRenderTask.get();
197     }
renderRequest(int frame)198     bool renderRequest(int frame) {
199         //addCallback();
200         if (mRenderTask.valid()) return true;
201         int width , height;
202         Evas_Object *image = renderObject();
203         evas_object_image_size_get(image, &width, &height);
204         auto buffer = (uint32_t *)evas_object_image_data_get(image, EINA_TRUE);
205         size_t bytesperline =  evas_object_image_stride_get(image);
206         rlottie::Surface surface(buffer, width, height, bytesperline);
207         mRenderTask = mPlayer->render(frame, surface);
208         return true;
209     }
210 
buffer()211     uint32_t* buffer() {
212         auto surface = mRenderTask.get();
213         return surface.buffer();
214     }
215 private:
216    std::future<rlottie::Surface>        mRenderTask;
217 };
218 
219 
220 class RlottieRenderStrategy_C : public RlottieRenderStrategyCBase {
221 public:
RlottieRenderStrategy_C(Evas * evas)222     RlottieRenderStrategy_C(Evas *evas):RlottieRenderStrategyCBase(evas) {}
~RlottieRenderStrategy_C()223     ~RlottieRenderStrategy_C() {
224         if (mPlayer) lottie_animation_destroy(mPlayer);
225     }
loadFromFile(const char * filePath)226     void loadFromFile(const char *filePath)
227     {
228         mPlayer = lottie_animation_from_file(filePath);
229 
230         if (!mPlayer) {
231             printf("load failed file %s\n", filePath);
232         }
233     }
234 
loadFromData(const std::string & jsonData,const std::string & key,const std::string & resourcePath)235     void loadFromData(const std::string &jsonData, const std::string &key, const std::string &resourcePath)
236     {
237         mPlayer = lottie_animation_from_data(jsonData.c_str(), key.c_str(), resourcePath.c_str());
238         if (!mPlayer) {
239             printf("load failed from data\n");
240         }
241     }
242 
totalFrame()243     size_t totalFrame() {
244         return lottie_animation_get_totalframe(mPlayer);
245 
246     }
247 
frameRate()248     double frameRate() {
249         return lottie_animation_get_framerate(mPlayer);
250     }
251 
frameAtPos(double pos)252     size_t frameAtPos(double pos) {
253         return  lottie_animation_get_frame_at_pos(mPlayer, pos);
254     }
255 
duration()256     double duration() {
257         return lottie_animation_get_duration(mPlayer);
258     }
findFrameAtMarker(const std::string & markerName)259     MarkerFrame findFrameAtMarker(const std::string &markerName)
260     {
261         printf("Can't not [%s] marker, CAPI isn't implements yet\n", markerName.c_str());
262         return std::make_tuple(0, 0);
263     }
264 
renderRequest(int frame)265     bool renderRequest(int frame) {
266         int width , height;
267         Evas_Object *image = renderObject();
268         evas_object_image_size_get(image, &width, &height);
269         mBuffer = (uint32_t *)evas_object_image_data_get(image, EINA_TRUE);
270         size_t bytesperline =  evas_object_image_stride_get(image);
271         lottie_animation_render(mPlayer, frame, mBuffer, width, height, bytesperline);
272         return true;
273     }
274 
buffer()275     uint32_t* buffer() {
276         return mBuffer;
277     }
278 
279 private:
280     uint32_t *              mBuffer;
281 protected:
282    Lottie_Animation       *mPlayer;
283 };
284 
285 class RlottieRenderStrategy_C_ASYNC : public RlottieRenderStrategy_C {
286 public:
RlottieRenderStrategy_C_ASYNC(Evas * evas)287     RlottieRenderStrategy_C_ASYNC(Evas *evas):RlottieRenderStrategy_C(evas) {}
~RlottieRenderStrategy_C_ASYNC()288     ~RlottieRenderStrategy_C_ASYNC() {
289         if (mDirty) lottie_animation_render_flush(mPlayer);
290     }
renderRequest(int frame)291     bool renderRequest(int frame) {
292         if (mDirty) return true;
293         mDirty = true;
294         Evas_Object *image = renderObject();
295         evas_object_image_size_get(image, &mWidth, &mHeight);
296         mBuffer = (uint32_t *)evas_object_image_data_get(image, EINA_TRUE);
297         size_t bytesperline =  evas_object_image_stride_get(image);
298         lottie_animation_render_async(mPlayer, frame, mBuffer, mWidth, mHeight, bytesperline);
299         return true;
300     }
301 
buffer()302     uint32_t* buffer() {
303        lottie_animation_render_flush(mPlayer);
304        mDirty =false;
305        return mBuffer;
306     }
307 
308 private:
309    uint32_t *              mBuffer;
310    int                     mWidth;
311    int                     mHeight;
312    bool                    mDirty{false};
313 };
314 
315 enum class  Strategy {
316   renderCpp = 0,
317   renderCppAsync,
318   renderC,
319   renderCAsync,
320   eflVg
321 };
322 
323 class LottieView
324 {
325 public:
326     enum class RepeatMode {
327         Restart,
328         Reverse
329     };
330     LottieView(Evas *evas, Strategy s = Strategy::renderCppAsync);
331     ~LottieView();
player()332     rlottie::Animation *player(){return mRenderDelegate->player();}
333     Evas_Object *getImage();
334     void setSize(int w, int h);
335     void setPos(int x, int y);
336     void setFilePath(const char *filePath);
337     void loadFromData(const std::string &jsonData, const std::string &key, const std::string &resourcePath="");
338     void show();
339     void hide();
340     void loop(bool loop);
setSpeed(float speed)341     void setSpeed(float speed) { mSpeed = speed;}
342     void setRepeatCount(int count);
343     void setRepeatMode(LottieView::RepeatMode mode);
getFrameRate()344     float getFrameRate() const { return mRenderDelegate->frameRate(); }
getTotalFrame()345     long getTotalFrame() const { return mRenderDelegate->totalFrame(); }
346 public:
347     void seek(float pos);
348     float getPos();
349     void finished();
350     void play();
351     void play(const std::string &marker);
352     void play(const std::string &startmarker, const std::string endmarker);
353     void pause();
354     void stop();
355     void initializeBufferObject(Evas *evas);
setMinProgress(float progress)356     void setMinProgress(float progress)
357     {
358         //clamp it to [0,1]
359         mMinProgress = progress;
360     }
setMaxProgress(float progress)361     void setMaxProgress(float progress)
362     {
363         //clamp it to [0,1]
364         mMaxprogress = progress;
365     }
366 
renderCount()367     size_t renderCount() const {
368         return mRenderDelegate ? mRenderDelegate->renderCount() : 0;
369     }
370 private:
mapProgress(float progress)371     float mapProgress(float progress) {
372         //clamp it to the segment
373         progress = (mMinProgress + (mMaxprogress - mMinProgress) * progress);
374 
375         // currently playing and in reverse mode
376         if (mPalying && mReverse)
377             progress = mMaxprogress > mMinProgress ?
378                         mMaxprogress - progress : mMinProgress - progress;
379 
380 
381         return progress;
382     }
duration()383     float duration() const {
384         // usually we run the animation for mPlayer->duration()
385         // but now run animation for segmented duration.
386         return  mRenderDelegate->duration() * fabs(mMaxprogress - mMinProgress);
387     }
388     void createVgNode(LOTNode *node, Efl_VG *root);
389     void update(const std::vector<LOTNode *> &);
390     void updateTree(const LOTLayerNode *);
391     void update(const LOTLayerNode *, Efl_VG *parent);
392     void restart();
393 public:
394     int                      mRepeatCount;
395     LottieView::RepeatMode   mRepeatMode;
396     size_t                   mCurFrame{UINT_MAX};
397     Ecore_Animator          *mAnimator{nullptr};
398     bool                     mLoop;
399     int                      mCurCount;
400     bool                     mReverse;
401     bool                     mPalying;
402     float                    mSpeed;
403     float                    mPos;
404     //keep a segment of the animation default is [0, 1]
405     float                   mMinProgress{0};
406     float                   mMaxprogress{1};
407     std::unique_ptr<RenderStrategy>  mRenderDelegate;
408 };
409 
410 #include<assert.h>
411 class EflVgRenderStrategy : public RenderStrategy {
412 public:
EflVgRenderStrategy(Evas * evas)413     EflVgRenderStrategy(Evas *evas):RenderStrategy(evas_object_vg_add(evas), false) {}
414 
loadFromFile(const char * filePath)415     void loadFromFile(const char *filePath) {
416         evas_object_vg_file_set(renderObject(), filePath, NULL);
417     }
418 
loadFromData(const std::string &,const std::string &,const std::string &)419     void loadFromData(const std::string&, const std::string&, const std::string&) {
420         assert(false);
421     }
422 
totalFrame()423     size_t totalFrame() {
424         return evas_object_vg_animated_frame_count_get(renderObject()) - 1;
425     }
426 
frameRate()427     double frameRate() {
428         return (double)totalFrame() / evas_object_vg_animated_frame_duration_get(renderObject(), 0, 0);
429     }
430 
frameAtPos(double pos)431     size_t frameAtPos(double pos) {
432         return totalFrame() * pos;
433     }
434 
duration()435     double duration() {
436         return evas_object_vg_animated_frame_duration_get(renderObject(), 0, 0);
437     }
438 
resize(int width,int height)439     void resize(int width, int height) {
440         evas_object_resize(renderObject(), width, height);
441     }
442 
buffer()443     uint32_t *buffer() {
444         assert(false);
445         return nullptr;
446     }
447 
renderRequest(int frame)448     bool renderRequest(int frame) {
449         evas_object_vg_animated_frame_set(renderObject(), frame);
450         return false;
451     }
452 
findFrameAtMarker(const std::string &)453     MarkerFrame findFrameAtMarker(const std::string&) {
454         return std::make_tuple(0, 0);
455     }
456 };
457 
458 #endif //LOTTIEVIEW_H
459