1 /**
2  * @file lv_rlottie.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_rlottie.h"
10 #if LV_USE_RLOTTIE
11 
12 #include <rlottie_capi.h>
13 
14 /*********************
15 *      DEFINES
16 *********************/
17 #define MY_CLASS &lv_rlottie_class
18 #define LV_ARGB32   32
19 
20 /**********************
21 *      TYPEDEFS
22 **********************/
23 #define LV_ARGB32   32
24 
25 /**********************
26  *  STATIC PROTOTYPES
27  **********************/
28 static void lv_rlottie_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
29 static void lv_rlottie_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
30 static void next_frame_task_cb(lv_timer_t * t);
31 
32 /**********************
33  *  STATIC VARIABLES
34  **********************/
35 const lv_obj_class_t lv_rlottie_class = {
36     .constructor_cb = lv_rlottie_constructor,
37     .destructor_cb = lv_rlottie_destructor,
38     .instance_size = sizeof(lv_rlottie_t),
39     .base_class = &lv_img_class
40 };
41 
42 static lv_coord_t create_width;
43 static lv_coord_t create_height;
44 static const char * rlottie_desc_create;
45 static const char * path_create;
46 
47 /**********************
48  *      MACROS
49  **********************/
50 
51 /**********************
52  *   GLOBAL FUNCTIONS
53  **********************/
54 
lv_rlottie_create_from_file(lv_obj_t * parent,lv_coord_t width,lv_coord_t height,const char * path)55 lv_obj_t * lv_rlottie_create_from_file(lv_obj_t * parent, lv_coord_t width, lv_coord_t height, const char * path)
56 {
57 
58     create_width = width;
59     create_height = height;
60     path_create = path;
61     rlottie_desc_create = NULL;
62 
63     LV_LOG_INFO("begin");
64     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
65     lv_obj_class_init_obj(obj);
66 
67     return obj;
68 
69 }
70 
lv_rlottie_create_from_raw(lv_obj_t * parent,lv_coord_t width,lv_coord_t height,const char * rlottie_desc)71 lv_obj_t * lv_rlottie_create_from_raw(lv_obj_t * parent, lv_coord_t width, lv_coord_t height, const char * rlottie_desc)
72 {
73 
74     create_width = width;
75     create_height = height;
76     rlottie_desc_create = rlottie_desc;
77     path_create = NULL;
78 
79     LV_LOG_INFO("begin");
80     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
81     lv_obj_class_init_obj(obj);
82 
83     return obj;
84 }
85 
lv_rlottie_set_play_mode(lv_obj_t * obj,const lv_rlottie_ctrl_t ctrl)86 void lv_rlottie_set_play_mode(lv_obj_t * obj, const lv_rlottie_ctrl_t ctrl)
87 {
88     lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
89     rlottie->play_ctrl = ctrl;
90 
91     if(rlottie->task && (rlottie->dest_frame != rlottie->current_frame ||
92                          (rlottie->play_ctrl & LV_RLOTTIE_CTRL_PAUSE) == LV_RLOTTIE_CTRL_PLAY)) {
93         lv_timer_resume(rlottie->task);
94     }
95 }
96 
lv_rlottie_set_current_frame(lv_obj_t * obj,const size_t goto_frame)97 void lv_rlottie_set_current_frame(lv_obj_t * obj, const size_t goto_frame)
98 {
99     lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
100     rlottie->current_frame = goto_frame < rlottie->total_frames ? goto_frame : rlottie->total_frames - 1;
101 }
102 
103 /**********************
104  *   STATIC FUNCTIONS
105  **********************/
106 
lv_rlottie_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)107 static void lv_rlottie_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
108 {
109     LV_UNUSED(class_p);
110     lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
111 
112     if(rlottie_desc_create) {
113         rlottie->animation = lottie_animation_from_data(rlottie_desc_create, rlottie_desc_create, "");
114     }
115     else if(path_create) {
116         rlottie->animation = lottie_animation_from_file(path_create);
117     }
118     if(rlottie->animation == NULL) {
119         LV_LOG_WARN("The aniamtion can't be opened");
120         return;
121     }
122 
123     rlottie->total_frames = lottie_animation_get_totalframe(rlottie->animation);
124     rlottie->framerate = (size_t)lottie_animation_get_framerate(rlottie->animation);
125     rlottie->current_frame = 0;
126 
127     rlottie->scanline_width = create_width * LV_ARGB32 / 8;
128 
129     size_t allocaled_buf_size = (create_width * create_height * LV_ARGB32 / 8);
130     rlottie->allocated_buf = lv_mem_alloc(allocaled_buf_size);
131     if(rlottie->allocated_buf != NULL) {
132         rlottie->allocated_buffer_size = allocaled_buf_size;
133         memset(rlottie->allocated_buf, 0, allocaled_buf_size);
134     }
135 
136     rlottie->imgdsc.header.always_zero = 0;
137     rlottie->imgdsc.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
138     rlottie->imgdsc.header.h = create_height;
139     rlottie->imgdsc.header.w = create_width;
140     rlottie->imgdsc.data = (void *)rlottie->allocated_buf;
141     rlottie->imgdsc.data_size = allocaled_buf_size;
142 
143     lv_img_set_src(obj, &rlottie->imgdsc);
144 
145     rlottie->play_ctrl = LV_RLOTTIE_CTRL_FORWARD | LV_RLOTTIE_CTRL_PLAY | LV_RLOTTIE_CTRL_LOOP;
146     rlottie->dest_frame = rlottie->total_frames; /* invalid destination frame so it's possible to pause on frame 0 */
147 
148     rlottie->task = lv_timer_create(next_frame_task_cb, 1000 / rlottie->framerate, obj);
149 
150     lv_obj_update_layout(obj);
151 }
152 
153 
lv_rlottie_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)154 static void lv_rlottie_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
155 {
156     LV_UNUSED(class_p);
157     lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
158 
159     if(rlottie->animation) {
160         lottie_animation_destroy(rlottie->animation);
161         rlottie->animation = 0;
162         rlottie->current_frame = 0;
163         rlottie->framerate = 0;
164         rlottie->scanline_width = 0;
165         rlottie->total_frames = 0;
166     }
167 
168     if(rlottie->task) {
169         lv_timer_del(rlottie->task);
170         rlottie->task = NULL;
171         rlottie->play_ctrl = LV_RLOTTIE_CTRL_FORWARD;
172         rlottie->dest_frame = 0;
173     }
174 
175     lv_img_cache_invalidate_src(&rlottie->imgdsc);
176     if(rlottie->allocated_buf) {
177         lv_mem_free(rlottie->allocated_buf);
178         rlottie->allocated_buf = NULL;
179         rlottie->allocated_buffer_size = 0;
180     }
181 
182 }
183 
184 #if LV_COLOR_DEPTH == 16
convert_to_rgba5658(uint32_t * pix,const size_t width,const size_t height)185 static void convert_to_rgba5658(uint32_t * pix, const size_t width, const size_t height)
186 {
187     /* rlottie draws in ARGB32 format, but LVGL only deal with RGB565 format with (optional 8 bit alpha channel)
188        so convert in place here the received buffer to LVGL format. */
189     uint8_t * dest = (uint8_t *)pix;
190     uint32_t * src = pix;
191     for(size_t y = 0; y < height; y++) {
192         /* Convert a 4 bytes per pixel in format ARGB to R5G6B5A8 format
193             naive way:
194                         r = ((c & 0xFF0000) >> 19)
195                         g = ((c & 0xFF00) >> 10)
196                         b = ((c & 0xFF) >> 3)
197                         rgb565 = (r << 11) | (g << 5) | b
198                         a = c >> 24;
199             That's 3 mask, 6 bitshift and 2 or operations
200 
201             A bit better:
202                         r = ((c & 0xF80000) >> 8)
203                         g = ((c & 0xFC00) >> 5)
204                         b = ((c & 0xFF) >> 3)
205                         rgb565 = r | g | b
206                         a = c >> 24;
207             That's 3 mask, 3 bitshifts and 2 or operations */
208         for(size_t x = 0; x < width; x++) {
209             uint32_t in = src[x];
210 #if LV_COLOR_16_SWAP == 0
211             uint16_t r = (uint16_t)(((in & 0xF80000) >> 8) | ((in & 0xFC00) >> 5) | ((in & 0xFF) >> 3));
212 #else
213             /* We want: rrrr rrrr GGGg gggg bbbb bbbb => gggb bbbb rrrr rGGG */
214             uint16_t r = (uint16_t)(((in & 0xF80000) >> 16) | ((in & 0xFC00) >> 13) | ((in & 0x1C00) << 3) | ((in & 0xF8) << 5));
215 #endif
216 
217             lv_memcpy(dest, &r, sizeof(r));
218             dest[sizeof(r)] = (uint8_t)(in >> 24);
219             dest += LV_IMG_PX_SIZE_ALPHA_BYTE;
220         }
221         src += width;
222     }
223 }
224 #endif
225 
next_frame_task_cb(lv_timer_t * t)226 static void next_frame_task_cb(lv_timer_t * t)
227 {
228     lv_obj_t * obj = t->user_data;
229     lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
230 
231     if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_PAUSE) == LV_RLOTTIE_CTRL_PAUSE) {
232         if(rlottie->current_frame == rlottie->dest_frame) {
233             /* Pause the timer too when it has run once to avoid CPU consumption */
234             lv_timer_pause(t);
235             return;
236         }
237         rlottie->dest_frame = rlottie->current_frame;
238     }
239     else {
240         if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_BACKWARD) == LV_RLOTTIE_CTRL_BACKWARD) {
241             if(rlottie->current_frame > 0)
242                 --rlottie->current_frame;
243             else { /* Looping ? */
244                 if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_LOOP) == LV_RLOTTIE_CTRL_LOOP)
245                     rlottie->current_frame = rlottie->total_frames - 1;
246                 else {
247                     lv_event_send(obj, LV_EVENT_READY, NULL);
248                     lv_timer_pause(t);
249                     return;
250                 }
251             }
252         }
253         else {
254             if(rlottie->current_frame < rlottie->total_frames)
255                 ++rlottie->current_frame;
256             else { /* Looping ? */
257                 if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_LOOP) == LV_RLOTTIE_CTRL_LOOP)
258                     rlottie->current_frame = 0;
259                 else {
260                     lv_event_send(obj, LV_EVENT_READY, NULL);
261                     lv_timer_pause(t);
262                     return;
263                 }
264             }
265         }
266     }
267 
268     lottie_animation_render(
269         rlottie->animation,
270         rlottie->current_frame,
271         rlottie->allocated_buf,
272         rlottie->imgdsc.header.w,
273         rlottie->imgdsc.header.h,
274         rlottie->scanline_width
275     );
276 
277 #if LV_COLOR_DEPTH == 16
278     convert_to_rgba5658(rlottie->allocated_buf, rlottie->imgdsc.header.w, rlottie->imgdsc.header.h);
279 #endif
280 
281     lv_obj_invalidate(obj);
282 }
283 
284 #endif /*LV_USE_RLOTTIE*/
285