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