1 /**
2  * @file lv_rlottie.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "../../lvgl.h"
10 #if LV_USE_RLOTTIE
11 
12 #include "lv_rlottie_private.h"
13 #include "../../core/lv_obj_class_private.h"
14 #include <rlottie_capi.h>
15 #include <string.h>
16 
17 /*********************
18 *      DEFINES
19 *********************/
20 #define MY_CLASS (&lv_rlottie_class)
21 #define LV_ARGB32   32
22 
23 /**********************
24 *      TYPEDEFS
25 **********************/
26 #define LV_ARGB32   32
27 
28 /**********************
29  *  STATIC PROTOTYPES
30  **********************/
31 static void lv_rlottie_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
32 static void lv_rlottie_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
33 static void next_frame_task_cb(lv_timer_t * t);
34 
35 /**********************
36  *  STATIC VARIABLES
37  **********************/
38 
39 const lv_obj_class_t lv_rlottie_class = {
40     .constructor_cb = lv_rlottie_constructor,
41     .destructor_cb = lv_rlottie_destructor,
42     .instance_size = sizeof(lv_rlottie_t),
43     .base_class = &lv_image_class,
44     .name = "rlottie",
45 };
46 
47 typedef struct {
48     int32_t width;
49     int32_t height;
50     const char * rlottie_desc;
51     const char * path;
52 } lv_rlottie_create_info_t;
53 
54 /*Only used in lv_obj_class_create_obj, no affect multiple instances*/
55 static lv_rlottie_create_info_t create_info;
56 
57 /**********************
58  *      MACROS
59  **********************/
60 
61 /**********************
62  *   GLOBAL FUNCTIONS
63  **********************/
64 
lv_rlottie_create_from_file(lv_obj_t * parent,int32_t width,int32_t height,const char * path)65 lv_obj_t * lv_rlottie_create_from_file(lv_obj_t * parent, int32_t width, int32_t height, const char * path)
66 {
67     create_info.width = width;
68     create_info.height = height;
69     create_info.path = path;
70     create_info.rlottie_desc = NULL;
71 
72     LV_LOG_INFO("begin");
73     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
74     lv_obj_class_init_obj(obj);
75 
76     return obj;
77 }
78 
lv_rlottie_create_from_raw(lv_obj_t * parent,int32_t width,int32_t height,const char * rlottie_desc)79 lv_obj_t * lv_rlottie_create_from_raw(lv_obj_t * parent, int32_t width, int32_t height, const char * rlottie_desc)
80 {
81     create_info.width = width;
82     create_info.height = height;
83     create_info.rlottie_desc = rlottie_desc;
84     create_info.path = NULL;
85 
86     LV_LOG_INFO("begin");
87     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
88     lv_obj_class_init_obj(obj);
89 
90     return obj;
91 }
92 
lv_rlottie_set_play_mode(lv_obj_t * obj,const lv_rlottie_ctrl_t ctrl)93 void lv_rlottie_set_play_mode(lv_obj_t * obj, const lv_rlottie_ctrl_t ctrl)
94 {
95     lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
96     rlottie->play_ctrl = ctrl;
97 
98     if(rlottie->task && (rlottie->dest_frame != rlottie->current_frame ||
99                          (rlottie->play_ctrl & LV_RLOTTIE_CTRL_PAUSE) == LV_RLOTTIE_CTRL_PLAY)) {
100         lv_timer_resume(rlottie->task);
101     }
102 }
103 
lv_rlottie_set_current_frame(lv_obj_t * obj,const size_t goto_frame)104 void lv_rlottie_set_current_frame(lv_obj_t * obj, const size_t goto_frame)
105 {
106     lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
107     rlottie->current_frame = goto_frame < rlottie->total_frames ? goto_frame : rlottie->total_frames - 1;
108 }
109 
110 /**********************
111  *   STATIC FUNCTIONS
112  **********************/
113 
lv_rlottie_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)114 static void lv_rlottie_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
115 {
116     LV_UNUSED(class_p);
117     lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
118 
119     if(create_info.rlottie_desc) {
120         rlottie->animation = lottie_animation_from_data(create_info.rlottie_desc, create_info.rlottie_desc, "");
121     }
122     else if(create_info.path) {
123         rlottie->animation = lottie_animation_from_file(create_info.path);
124     }
125     if(rlottie->animation == NULL) {
126         LV_LOG_WARN("The animation can't be opened");
127         return;
128     }
129 
130     rlottie->total_frames = lottie_animation_get_totalframe(rlottie->animation);
131     rlottie->framerate = (size_t)lottie_animation_get_framerate(rlottie->animation);
132     rlottie->current_frame = 0;
133 
134     rlottie->scanline_width = create_info.width * LV_ARGB32 / 8;
135 
136     size_t allocated_buf_size = (create_info.width * create_info.height * LV_ARGB32 / 8);
137     rlottie->allocated_buf = lv_malloc(allocated_buf_size);
138     if(rlottie->allocated_buf != NULL) {
139         rlottie->allocated_buffer_size = allocated_buf_size;
140         memset(rlottie->allocated_buf, 0, allocated_buf_size);
141     }
142 
143     rlottie->imgdsc.header.cf = LV_COLOR_FORMAT_ARGB8888;
144     rlottie->imgdsc.header.h = create_info.height;
145     rlottie->imgdsc.header.w = create_info.width;
146     rlottie->imgdsc.data = (void *)rlottie->allocated_buf;
147     rlottie->imgdsc.data_size = allocated_buf_size;
148 
149     lv_image_set_src(obj, &rlottie->imgdsc);
150 
151     rlottie->play_ctrl = LV_RLOTTIE_CTRL_FORWARD | LV_RLOTTIE_CTRL_PLAY | LV_RLOTTIE_CTRL_LOOP;
152     rlottie->dest_frame = rlottie->total_frames; /* invalid destination frame so it's possible to pause on frame 0 */
153 
154     rlottie->task = lv_timer_create(next_frame_task_cb, 1000 / rlottie->framerate, obj);
155 
156     lv_obj_update_layout(obj);
157 }
158 
lv_rlottie_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)159 static void lv_rlottie_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
160 {
161     LV_UNUSED(class_p);
162     lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
163 
164     if(rlottie->animation) {
165         lottie_animation_destroy(rlottie->animation);
166         rlottie->animation = 0;
167         rlottie->current_frame = 0;
168         rlottie->framerate = 0;
169         rlottie->scanline_width = 0;
170         rlottie->total_frames = 0;
171     }
172 
173     if(rlottie->task) {
174         lv_timer_delete(rlottie->task);
175         rlottie->task = NULL;
176         rlottie->play_ctrl = LV_RLOTTIE_CTRL_FORWARD;
177         rlottie->dest_frame = 0;
178     }
179 
180     lv_image_cache_drop(&rlottie->imgdsc);
181 
182     if(rlottie->allocated_buf) {
183         lv_free(rlottie->allocated_buf);
184         rlottie->allocated_buf = NULL;
185         rlottie->allocated_buffer_size = 0;
186     }
187 
188 }
189 
next_frame_task_cb(lv_timer_t * t)190 static void next_frame_task_cb(lv_timer_t * t)
191 {
192     lv_obj_t * obj = lv_timer_get_user_data(t);
193     lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
194 
195     if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_PAUSE) == LV_RLOTTIE_CTRL_PAUSE) {
196         if(rlottie->current_frame == rlottie->dest_frame) {
197             /* Pause the timer too when it has run once to avoid CPU consumption */
198             lv_timer_pause(t);
199             return;
200         }
201         rlottie->dest_frame = rlottie->current_frame;
202     }
203     else {
204         if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_BACKWARD) == LV_RLOTTIE_CTRL_BACKWARD) {
205             if(rlottie->current_frame > 0)
206                 --rlottie->current_frame;
207             else { /* Looping ? */
208                 if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_LOOP) == LV_RLOTTIE_CTRL_LOOP)
209                     rlottie->current_frame = rlottie->total_frames - 1;
210                 else {
211                     lv_obj_send_event(obj, LV_EVENT_READY, NULL);
212                     lv_timer_pause(t);
213                     return;
214                 }
215             }
216         }
217         else {
218             if(rlottie->current_frame < rlottie->total_frames)
219                 ++rlottie->current_frame;
220             else { /* Looping ? */
221                 if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_LOOP) == LV_RLOTTIE_CTRL_LOOP)
222                     rlottie->current_frame = 0;
223                 else {
224                     lv_obj_send_event(obj, LV_EVENT_READY, NULL);
225                     lv_timer_pause(t);
226                     return;
227                 }
228             }
229         }
230     }
231 
232     lottie_animation_render(
233         rlottie->animation,
234         rlottie->current_frame,
235         rlottie->allocated_buf,
236         rlottie->imgdsc.header.w,
237         rlottie->imgdsc.header.h,
238         rlottie->scanline_width
239     );
240 
241     lv_obj_invalidate(obj);
242 }
243 
244 #endif /*LV_USE_RLOTTIE*/
245