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
lv_rlottie_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)153 static void lv_rlottie_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
154 {
155 LV_UNUSED(class_p);
156 lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
157
158 if(rlottie->animation) {
159 lottie_animation_destroy(rlottie->animation);
160 rlottie->animation = 0;
161 rlottie->current_frame = 0;
162 rlottie->framerate = 0;
163 rlottie->scanline_width = 0;
164 rlottie->total_frames = 0;
165 }
166
167 if(rlottie->task) {
168 lv_timer_del(rlottie->task);
169 rlottie->task = NULL;
170 rlottie->play_ctrl = LV_RLOTTIE_CTRL_FORWARD;
171 rlottie->dest_frame = 0;
172 }
173
174 lv_img_cache_invalidate_src(&rlottie->imgdsc);
175 if(rlottie->allocated_buf) {
176 lv_mem_free(rlottie->allocated_buf);
177 rlottie->allocated_buf = NULL;
178 rlottie->allocated_buffer_size = 0;
179 }
180
181 }
182
183 #if LV_COLOR_DEPTH == 16
convert_to_rgba5658(uint32_t * pix,const size_t width,const size_t height)184 static void convert_to_rgba5658(uint32_t * pix, const size_t width, const size_t height)
185 {
186 /* rlottie draws in ARGB32 format, but LVGL only deal with RGB565 format with (optional 8 bit alpha channel)
187 so convert in place here the received buffer to LVGL format. */
188 uint8_t * dest = (uint8_t *)pix;
189 uint32_t * src = pix;
190 for(size_t y = 0; y < height; y++) {
191 /* Convert a 4 bytes per pixel in format ARGB to R5G6B5A8 format
192 naive way:
193 r = ((c & 0xFF0000) >> 19)
194 g = ((c & 0xFF00) >> 10)
195 b = ((c & 0xFF) >> 3)
196 rgb565 = (r << 11) | (g << 5) | b
197 a = c >> 24;
198 That's 3 mask, 6 bitshift and 2 or operations
199
200 A bit better:
201 r = ((c & 0xF80000) >> 8)
202 g = ((c & 0xFC00) >> 5)
203 b = ((c & 0xFF) >> 3)
204 rgb565 = r | g | b
205 a = c >> 24;
206 That's 3 mask, 3 bitshifts and 2 or operations */
207 for(size_t x = 0; x < width; x++) {
208 uint32_t in = src[x];
209 #if LV_COLOR_16_SWAP == 0
210 uint16_t r = (uint16_t)(((in & 0xF80000) >> 8) | ((in & 0xFC00) >> 5) | ((in & 0xFF) >> 3));
211 #else
212 /* We want: rrrr rrrr GGGg gggg bbbb bbbb => gggb bbbb rrrr rGGG */
213 uint16_t r = (uint16_t)(((in & 0xF80000) >> 16) | ((in & 0xFC00) >> 13) | ((in & 0x1C00) << 3) | ((in & 0xF8) << 5));
214 #endif
215
216 lv_memcpy(dest, &r, sizeof(r));
217 dest[sizeof(r)] = (uint8_t)(in >> 24);
218 dest += LV_IMG_PX_SIZE_ALPHA_BYTE;
219 }
220 src += width;
221 }
222 }
223 #endif
224
next_frame_task_cb(lv_timer_t * t)225 static void next_frame_task_cb(lv_timer_t * t)
226 {
227 lv_obj_t * obj = t->user_data;
228 lv_rlottie_t * rlottie = (lv_rlottie_t *) obj;
229
230 if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_PAUSE) == LV_RLOTTIE_CTRL_PAUSE) {
231 if(rlottie->current_frame == rlottie->dest_frame) {
232 /* Pause the timer too when it has run once to avoid CPU consumption */
233 lv_timer_pause(t);
234 return;
235 }
236 rlottie->dest_frame = rlottie->current_frame;
237 }
238 else {
239 if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_BACKWARD) == LV_RLOTTIE_CTRL_BACKWARD) {
240 if(rlottie->current_frame > 0)
241 --rlottie->current_frame;
242 else { /* Looping ? */
243 if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_LOOP) == LV_RLOTTIE_CTRL_LOOP)
244 rlottie->current_frame = rlottie->total_frames - 1;
245 else {
246 lv_event_send(obj, LV_EVENT_READY, NULL);
247 lv_timer_pause(t);
248 return;
249 }
250 }
251 }
252 else {
253 if(rlottie->current_frame < rlottie->total_frames)
254 ++rlottie->current_frame;
255 else { /* Looping ? */
256 if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_LOOP) == LV_RLOTTIE_CTRL_LOOP)
257 rlottie->current_frame = 0;
258 else {
259 lv_event_send(obj, LV_EVENT_READY, NULL);
260 lv_timer_pause(t);
261 return;
262 }
263 }
264 }
265 }
266
267 lottie_animation_render(
268 rlottie->animation,
269 rlottie->current_frame,
270 rlottie->allocated_buf,
271 rlottie->imgdsc.header.w,
272 rlottie->imgdsc.header.h,
273 rlottie->scanline_width
274 );
275
276 #if LV_COLOR_DEPTH == 16
277 convert_to_rgba5658(rlottie->allocated_buf, rlottie->imgdsc.header.w, rlottie->imgdsc.header.h);
278 #endif
279
280 lv_obj_invalidate(obj);
281 }
282
283 #endif /*LV_USE_RLOTTIE*/
284