1 /**
2  * @file lv_refr.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_refr_private.h"
10 #include "lv_obj_draw_private.h"
11 #include "../misc/lv_area_private.h"
12 #include "../draw/sw/lv_draw_sw_mask_private.h"
13 #include "../draw/lv_draw_mask_private.h"
14 #include "lv_obj_private.h"
15 #include "lv_obj_event_private.h"
16 #include "../display/lv_display.h"
17 #include "../display/lv_display_private.h"
18 #include "../tick/lv_tick.h"
19 #include "../misc/lv_timer_private.h"
20 #include "../misc/lv_math.h"
21 #include "../misc/lv_profiler.h"
22 #include "../misc/lv_types.h"
23 #include "../draw/lv_draw_private.h"
24 #include "../font/lv_font_fmt_txt.h"
25 #include "../stdlib/lv_string.h"
26 #include "lv_global.h"
27 
28 /*********************
29  *      DEFINES
30  *********************/
31 
32 /*Display being refreshed*/
33 #define disp_refr LV_GLOBAL_DEFAULT()->disp_refresh
34 
35 /**********************
36  *      TYPEDEFS
37  **********************/
38 
39 /**********************
40  *  STATIC PROTOTYPES
41  **********************/
42 static void lv_refr_join_area(void);
43 static void refr_invalid_areas(void);
44 static void refr_sync_areas(void);
45 static void refr_area(const lv_area_t * area_p);
46 static void refr_configured_layer(lv_layer_t * layer);
47 static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj);
48 static void refr_obj_and_children(lv_layer_t * layer, lv_obj_t * top_obj);
49 static void refr_obj(lv_layer_t * layer, lv_obj_t * obj);
50 static uint32_t get_max_row(lv_display_t * disp, int32_t area_w, int32_t area_h);
51 static void draw_buf_flush(lv_display_t * disp);
52 static void call_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map);
53 static void wait_for_flushing(lv_display_t * disp);
54 
55 /**********************
56  *  STATIC VARIABLES
57  **********************/
58 
59 /**********************
60  *      MACROS
61  **********************/
62 #if LV_USE_LOG && LV_LOG_TRACE_DISP_REFR
63     #define LV_TRACE_REFR(...) LV_LOG_TRACE(__VA_ARGS__)
64 #else
65     #define LV_TRACE_REFR(...)
66 #endif
67 
68 /**********************
69  *   GLOBAL FUNCTIONS
70  **********************/
71 
72 /**
73  * Initialize the screen refresh subsystem
74  */
lv_refr_init(void)75 void lv_refr_init(void)
76 {
77 }
78 
lv_refr_deinit(void)79 void lv_refr_deinit(void)
80 {
81 }
82 
lv_refr_now(lv_display_t * disp)83 void lv_refr_now(lv_display_t * disp)
84 {
85     lv_anim_refr_now();
86 
87     if(disp) {
88         if(disp->refr_timer) lv_display_refr_timer(disp->refr_timer);
89     }
90     else {
91         lv_display_t * d;
92         d = lv_display_get_next(NULL);
93         while(d) {
94             if(d->refr_timer) lv_display_refr_timer(d->refr_timer);
95             d = lv_display_get_next(d);
96         }
97     }
98 }
99 
lv_obj_redraw(lv_layer_t * layer,lv_obj_t * obj)100 void lv_obj_redraw(lv_layer_t * layer, lv_obj_t * obj)
101 {
102     LV_PROFILER_REFR_BEGIN;
103     lv_area_t clip_area_ori = layer->_clip_area;
104     lv_area_t clip_coords_for_obj;
105 
106     /*Truncate the clip area to `obj size + ext size` area*/
107     lv_area_t obj_coords_ext;
108     lv_obj_get_coords(obj, &obj_coords_ext);
109     int32_t ext_draw_size = lv_obj_get_ext_draw_size(obj);
110     lv_area_increase(&obj_coords_ext, ext_draw_size, ext_draw_size);
111 
112     if(!lv_area_intersect(&clip_coords_for_obj, &clip_area_ori, &obj_coords_ext)) {
113         LV_PROFILER_REFR_END;
114         return;
115     }
116     /*If the object is visible on the current clip area*/
117     layer->_clip_area = clip_coords_for_obj;
118 
119     lv_obj_send_event(obj, LV_EVENT_DRAW_MAIN_BEGIN, layer);
120     lv_obj_send_event(obj, LV_EVENT_DRAW_MAIN, layer);
121     lv_obj_send_event(obj, LV_EVENT_DRAW_MAIN_END, layer);
122 #if LV_USE_REFR_DEBUG
123     lv_color_t debug_color = lv_color_make(lv_rand(0, 0xFF), lv_rand(0, 0xFF), lv_rand(0, 0xFF));
124     lv_draw_rect_dsc_t draw_dsc;
125     lv_draw_rect_dsc_init(&draw_dsc);
126     draw_dsc.bg_color = debug_color;
127     draw_dsc.bg_opa = LV_OPA_20;
128     draw_dsc.border_width = 1;
129     draw_dsc.border_opa = LV_OPA_30;
130     draw_dsc.border_color = debug_color;
131     lv_draw_rect(layer, &draw_dsc, &obj_coords_ext);
132 #endif
133 
134     const lv_area_t * obj_coords;
135     if(lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) {
136         obj_coords = &obj_coords_ext;
137     }
138     else {
139         obj_coords = &obj->coords;
140     }
141     lv_area_t clip_coords_for_children;
142     bool refr_children = true;
143     if(!lv_area_intersect(&clip_coords_for_children, &clip_area_ori, obj_coords)) {
144         refr_children = false;
145     }
146 
147     if(refr_children) {
148         uint32_t i;
149         uint32_t child_cnt = lv_obj_get_child_count(obj);
150         if(child_cnt == 0) {
151             /*If the object was visible on the clip area call the post draw events too*/
152             layer->_clip_area = clip_coords_for_obj;
153             /*If all the children are redrawn make 'post draw' draw*/
154             lv_obj_send_event(obj, LV_EVENT_DRAW_POST_BEGIN, layer);
155             lv_obj_send_event(obj, LV_EVENT_DRAW_POST, layer);
156             lv_obj_send_event(obj, LV_EVENT_DRAW_POST_END, layer);
157         }
158         else {
159             layer->_clip_area = clip_coords_for_children;
160             bool clip_corner = lv_obj_get_style_clip_corner(obj, LV_PART_MAIN);
161 
162             int32_t radius = 0;
163             if(clip_corner) {
164                 radius = lv_obj_get_style_radius(obj, LV_PART_MAIN);
165                 if(radius == 0) clip_corner = false;
166             }
167 
168             if(clip_corner == false) {
169                 for(i = 0; i < child_cnt; i++) {
170                     lv_obj_t * child = obj->spec_attr->children[i];
171                     refr_obj(layer, child);
172                 }
173 
174                 /*If the object was visible on the clip area call the post draw events too*/
175                 layer->_clip_area = clip_coords_for_obj;
176                 /*If all the children are redrawn make 'post draw' draw*/
177                 lv_obj_send_event(obj, LV_EVENT_DRAW_POST_BEGIN, layer);
178                 lv_obj_send_event(obj, LV_EVENT_DRAW_POST, layer);
179                 lv_obj_send_event(obj, LV_EVENT_DRAW_POST_END, layer);
180             }
181             else {
182                 lv_layer_t * layer_children;
183                 lv_draw_mask_rect_dsc_t mask_draw_dsc;
184                 lv_draw_mask_rect_dsc_init(&mask_draw_dsc);
185                 mask_draw_dsc.radius = radius;
186                 mask_draw_dsc.area = obj->coords;
187 
188                 lv_draw_image_dsc_t img_draw_dsc;
189                 lv_draw_image_dsc_init(&img_draw_dsc);
190 
191                 int32_t short_side = LV_MIN(lv_area_get_width(&obj->coords), lv_area_get_height(&obj->coords));
192                 int32_t rout = LV_MIN(radius, short_side >> 1);
193 
194                 lv_area_t bottom = obj->coords;
195                 bottom.y1 = bottom.y2 - rout + 1;
196                 if(lv_area_intersect(&bottom, &bottom, &clip_area_ori)) {
197                     layer_children = lv_draw_layer_create(layer, LV_COLOR_FORMAT_ARGB8888, &bottom);
198 
199                     for(i = 0; i < child_cnt; i++) {
200                         lv_obj_t * child = obj->spec_attr->children[i];
201                         refr_obj(layer_children, child);
202                     }
203 
204                     /*If all the children are redrawn send 'post draw' draw*/
205                     lv_obj_send_event(obj, LV_EVENT_DRAW_POST_BEGIN, layer_children);
206                     lv_obj_send_event(obj, LV_EVENT_DRAW_POST, layer_children);
207                     lv_obj_send_event(obj, LV_EVENT_DRAW_POST_END, layer_children);
208 
209                     lv_draw_mask_rect(layer_children, &mask_draw_dsc);
210 
211                     img_draw_dsc.src = layer_children;
212                     lv_draw_layer(layer, &img_draw_dsc, &bottom);
213                 }
214 
215                 lv_area_t top = obj->coords;
216                 top.y2 = top.y1 + rout - 1;
217                 if(lv_area_intersect(&top, &top, &clip_area_ori)) {
218                     layer_children = lv_draw_layer_create(layer, LV_COLOR_FORMAT_ARGB8888, &top);
219 
220                     for(i = 0; i < child_cnt; i++) {
221                         lv_obj_t * child = obj->spec_attr->children[i];
222                         refr_obj(layer_children, child);
223                     }
224 
225                     /*If all the children are redrawn send 'post draw' draw*/
226                     lv_obj_send_event(obj, LV_EVENT_DRAW_POST_BEGIN, layer_children);
227                     lv_obj_send_event(obj, LV_EVENT_DRAW_POST, layer_children);
228                     lv_obj_send_event(obj, LV_EVENT_DRAW_POST_END, layer_children);
229 
230                     lv_draw_mask_rect(layer_children, &mask_draw_dsc);
231 
232                     img_draw_dsc.src = layer_children;
233                     lv_draw_layer(layer, &img_draw_dsc, &top);
234 
235                 }
236 
237                 lv_area_t mid = obj->coords;
238                 mid.y1 += rout;
239                 mid.y2 -= rout;
240                 if(lv_area_intersect(&mid, &mid, &clip_area_ori)) {
241                     layer->_clip_area = mid;
242                     for(i = 0; i < child_cnt; i++) {
243                         lv_obj_t * child = obj->spec_attr->children[i];
244                         refr_obj(layer, child);
245                     }
246 
247                     /*If all the children are redrawn make 'post draw' draw*/
248                     lv_obj_send_event(obj, LV_EVENT_DRAW_POST_BEGIN, layer);
249                     lv_obj_send_event(obj, LV_EVENT_DRAW_POST, layer);
250                     lv_obj_send_event(obj, LV_EVENT_DRAW_POST_END, layer);
251 
252                 }
253 
254             }
255         }
256     }
257 
258     layer->_clip_area = clip_area_ori;
259     LV_PROFILER_REFR_END;
260 }
261 
lv_inv_area(lv_display_t * disp,const lv_area_t * area_p)262 void lv_inv_area(lv_display_t * disp, const lv_area_t * area_p)
263 {
264     if(!disp) disp = lv_display_get_default();
265     if(!disp) return;
266     if(!lv_display_is_invalidation_enabled(disp)) return;
267 
268     /**
269      * There are two reasons for this issue:
270      *  1.LVGL API is being used across threads, such as modifying widget properties in another thread
271      *    or within an interrupt handler during the main thread rendering process.
272      *  2.User-customized widget modify widget properties/styles again within the DRAW event.
273      *
274      * Therefore, ensure that LVGL is used in a single-threaded manner, or refer to
275      * documentation: https://docs.lvgl.io/master/porting/os.html for proper locking mechanisms.
276      * Additionally, ensure that only drawing-related tasks are performed within the DRAW event,
277      * and move widget property/style modifications to other events.
278      */
279     LV_ASSERT_MSG(!disp->rendering_in_progress, "Invalidate area is not allowed during rendering.");
280 
281     /*Clear the invalidate buffer if the parameter is NULL*/
282     if(area_p == NULL) {
283         disp->inv_p = 0;
284         return;
285     }
286 
287     lv_area_t scr_area;
288     scr_area.x1 = 0;
289     scr_area.y1 = 0;
290     scr_area.x2 = lv_display_get_horizontal_resolution(disp) - 1;
291     scr_area.y2 = lv_display_get_vertical_resolution(disp) - 1;
292 
293     lv_area_t com_area;
294     bool suc;
295 
296     suc = lv_area_intersect(&com_area, area_p, &scr_area);
297     if(suc == false)  return; /*Out of the screen*/
298 
299     if(disp->color_format == LV_COLOR_FORMAT_I1) {
300         /*Make sure that the X coordinates start and end on byte boundary.
301          *E.g. convert 11;27 to 8;31*/
302         com_area.x1 &= ~0x7; /*Round down: Nx8*/
303         com_area.x2 |= 0x7;    /*Round up: Nx8 - 1*/
304     }
305 
306     /*If there were at least 1 invalid area in full refresh mode, redraw the whole screen*/
307     if(disp->render_mode == LV_DISPLAY_RENDER_MODE_FULL) {
308         disp->inv_areas[0] = scr_area;
309         disp->inv_p = 1;
310         lv_display_send_event(disp, LV_EVENT_REFR_REQUEST, NULL);
311         return;
312     }
313 
314     lv_result_t res = lv_display_send_event(disp, LV_EVENT_INVALIDATE_AREA, &com_area);
315     if(res != LV_RESULT_OK) return;
316 
317     /*Save only if this area is not in one of the saved areas*/
318     uint16_t i;
319     for(i = 0; i < disp->inv_p; i++) {
320         if(lv_area_is_in(&com_area, &disp->inv_areas[i], 0) != false) return;
321     }
322 
323     /*Save the area*/
324     lv_area_t * tmp_area_p = &com_area;
325     if(disp->inv_p >= LV_INV_BUF_SIZE) { /*If no place for the area add the screen*/
326         disp->inv_p = 0;
327         tmp_area_p = &scr_area;
328     }
329     lv_area_copy(&disp->inv_areas[disp->inv_p], tmp_area_p);
330     disp->inv_p++;
331 
332     lv_display_send_event(disp, LV_EVENT_REFR_REQUEST, NULL);
333 }
334 
335 /**
336  * Get the display which is being refreshed
337  * @return the display being refreshed
338  */
lv_refr_get_disp_refreshing(void)339 lv_display_t * lv_refr_get_disp_refreshing(void)
340 {
341     return disp_refr;
342 }
343 
344 /**
345  * Get the display which is being refreshed
346  * @return the display being refreshed
347  */
lv_refr_set_disp_refreshing(lv_display_t * disp)348 void lv_refr_set_disp_refreshing(lv_display_t * disp)
349 {
350     disp_refr = disp;
351 }
352 
lv_display_refr_timer(lv_timer_t * tmr)353 void lv_display_refr_timer(lv_timer_t * tmr)
354 {
355     LV_PROFILER_REFR_BEGIN;
356     LV_TRACE_REFR("begin");
357 
358     if(tmr) {
359         disp_refr = tmr->user_data;
360         /* Ensure the timer does not run again automatically.
361          * This is done before refreshing in case refreshing invalidates something else.
362          * However if the performance monitor is enabled keep the timer running to count the FPS.*/
363 #if !LV_USE_PERF_MONITOR
364         lv_timer_pause(tmr);
365 #endif
366     }
367     else {
368         disp_refr = lv_display_get_default();
369     }
370 
371     if(disp_refr == NULL) {
372         LV_LOG_WARN("No display registered");
373         LV_PROFILER_REFR_END;
374         return;
375     }
376 
377     lv_draw_buf_t * buf_act = disp_refr->buf_act;
378     if(!(buf_act && buf_act->data && buf_act->data_size)) {
379         LV_LOG_WARN("No draw buffer");
380         LV_PROFILER_REFR_END;
381         return;
382     }
383 
384     lv_display_send_event(disp_refr, LV_EVENT_REFR_START, NULL);
385 
386     /*Refresh the screen's layout if required*/
387     LV_PROFILER_LAYOUT_BEGIN_TAG("layout");
388     lv_obj_update_layout(disp_refr->act_scr);
389     if(disp_refr->prev_scr) lv_obj_update_layout(disp_refr->prev_scr);
390 
391     lv_obj_update_layout(disp_refr->bottom_layer);
392     lv_obj_update_layout(disp_refr->top_layer);
393     lv_obj_update_layout(disp_refr->sys_layer);
394     LV_PROFILER_LAYOUT_END_TAG("layout");
395 
396     /*Do nothing if there is no active screen*/
397     if(disp_refr->act_scr == NULL) {
398         disp_refr->inv_p = 0;
399         LV_LOG_WARN("there is no active screen");
400         goto refr_finish;
401     }
402 
403     lv_refr_join_area();
404     refr_sync_areas();
405     refr_invalid_areas();
406 
407     if(disp_refr->inv_p == 0) goto refr_finish;
408     /*In double buffered direct mode save the updated areas.
409      *They will be used on the next call to synchronize the buffers.*/
410     if(lv_display_is_double_buffered(disp_refr) && disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_DIRECT) {
411         uint32_t i;
412         for(i = 0; i < disp_refr->inv_p; i++) {
413             if(disp_refr->inv_area_joined[i])
414                 continue;
415 
416             lv_area_t * sync_area = lv_ll_ins_tail(&disp_refr->sync_areas);
417             *sync_area = disp_refr->inv_areas[i];
418         }
419     }
420 
421     lv_memzero(disp_refr->inv_areas, sizeof(disp_refr->inv_areas));
422     lv_memzero(disp_refr->inv_area_joined, sizeof(disp_refr->inv_area_joined));
423     disp_refr->inv_p = 0;
424 
425 refr_finish:
426 
427 #if LV_DRAW_SW_COMPLEX == 1
428     lv_draw_sw_mask_cleanup();
429 #endif
430 
431     lv_display_send_event(disp_refr, LV_EVENT_REFR_READY, NULL);
432 
433     LV_TRACE_REFR("finished");
434     LV_PROFILER_REFR_END;
435 }
436 
437 /**********************
438  *   STATIC FUNCTIONS
439  **********************/
440 
441 /**
442  * Join the areas which has got common parts
443  */
lv_refr_join_area(void)444 static void lv_refr_join_area(void)
445 {
446     LV_PROFILER_REFR_BEGIN;
447     uint32_t join_from;
448     uint32_t join_in;
449     lv_area_t joined_area;
450     for(join_in = 0; join_in < disp_refr->inv_p; join_in++) {
451         if(disp_refr->inv_area_joined[join_in] != 0) continue;
452 
453         /*Check all areas to join them in 'join_in'*/
454         for(join_from = 0; join_from < disp_refr->inv_p; join_from++) {
455             /*Handle only unjoined areas and ignore itself*/
456             if(disp_refr->inv_area_joined[join_from] != 0 || join_in == join_from) {
457                 continue;
458             }
459 
460             /*Check if the areas are on each other*/
461             if(lv_area_is_on(&disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]) == false) {
462                 continue;
463             }
464 
465             lv_area_join(&joined_area, &disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]);
466 
467             /*Join two area only if the joined area size is smaller*/
468             if(lv_area_get_size(&joined_area) < (lv_area_get_size(&disp_refr->inv_areas[join_in]) +
469                                                  lv_area_get_size(&disp_refr->inv_areas[join_from]))) {
470                 lv_area_copy(&disp_refr->inv_areas[join_in], &joined_area);
471 
472                 /*Mark 'join_form' is joined into 'join_in'*/
473                 disp_refr->inv_area_joined[join_from] = 1;
474             }
475         }
476     }
477     LV_PROFILER_REFR_END;
478 }
479 
480 /**
481  * Refresh the sync areas
482  */
refr_sync_areas(void)483 static void refr_sync_areas(void)
484 {
485     /*Do not sync if not direct or double buffered*/
486     if(disp_refr->render_mode != LV_DISPLAY_RENDER_MODE_DIRECT) return;
487 
488     /*Do not sync if not double buffered*/
489     if(!lv_display_is_double_buffered(disp_refr)) return;
490 
491     /*Do not sync if no sync areas*/
492     if(lv_ll_is_empty(&disp_refr->sync_areas)) return;
493 
494     LV_PROFILER_REFR_BEGIN;
495     /*With double buffered direct mode synchronize the rendered areas to the other buffer*/
496     /*We need to wait for ready here to not mess up the active screen*/
497     wait_for_flushing(disp_refr);
498 
499     /*The buffers are already swapped.
500      *So the active buffer is the off screen buffer where LVGL will render*/
501     lv_draw_buf_t * off_screen = disp_refr->buf_act;
502     lv_draw_buf_t * on_screen = disp_refr->buf_act == disp_refr->buf_1 ? disp_refr->buf_2 : disp_refr->buf_1;
503 
504     uint32_t hor_res = lv_display_get_horizontal_resolution(disp_refr);
505     uint32_t ver_res = lv_display_get_vertical_resolution(disp_refr);
506 
507     /*Iterate through invalidated areas to see if sync area should be copied*/
508     uint16_t i;
509     int8_t j;
510     lv_area_t res[4] = {0};
511     int8_t res_c;
512     lv_area_t * sync_area, * new_area, * next_area;
513     for(i = 0; i < disp_refr->inv_p; i++) {
514         /*Skip joined areas*/
515         if(disp_refr->inv_area_joined[i]) continue;
516 
517         /*Iterate over sync areas*/
518         sync_area = lv_ll_get_head(&disp_refr->sync_areas);
519         while(sync_area != NULL) {
520             /*Get next sync area*/
521             next_area = lv_ll_get_next(&disp_refr->sync_areas, sync_area);
522 
523             /*Remove intersect of redraw area from sync area and get remaining areas*/
524             res_c = lv_area_diff(res, sync_area, &disp_refr->inv_areas[i]);
525 
526             /*New sub areas created after removing intersect*/
527             if(res_c != -1) {
528                 /*Replace old sync area with new areas*/
529                 for(j = 0; j < res_c; j++) {
530                     new_area = lv_ll_ins_prev(&disp_refr->sync_areas, sync_area);
531                     *new_area = res[j];
532                 }
533                 lv_ll_remove(&disp_refr->sync_areas, sync_area);
534                 lv_free(sync_area);
535             }
536 
537             /*Move on to next sync area*/
538             sync_area = next_area;
539         }
540     }
541 
542     lv_area_t disp_area = {0, 0, (int32_t)hor_res - 1, (int32_t)ver_res - 1};
543     /*Copy sync areas (if any remaining)*/
544     for(sync_area = lv_ll_get_head(&disp_refr->sync_areas); sync_area != NULL;
545         sync_area = lv_ll_get_next(&disp_refr->sync_areas, sync_area)) {
546         /**
547          * @todo Resize SDL window will trigger crash because of sync_area is larger than disp_area
548          */
549         lv_area_intersect(sync_area, sync_area, &disp_area);
550         lv_draw_buf_copy(off_screen, sync_area, on_screen, sync_area);
551     }
552 
553     /*Clear sync areas*/
554     lv_ll_clear(&disp_refr->sync_areas);
555     LV_PROFILER_REFR_END;
556 }
557 
558 /**
559  * Refresh the joined areas
560  */
refr_invalid_areas(void)561 static void refr_invalid_areas(void)
562 {
563     if(disp_refr->inv_p == 0) return;
564     LV_PROFILER_REFR_BEGIN;
565 
566     /*Find the last area which will be drawn*/
567     int32_t i;
568     int32_t last_i = 0;
569     for(i = disp_refr->inv_p - 1; i >= 0; i--) {
570         if(disp_refr->inv_area_joined[i] == 0) {
571             last_i = i;
572             break;
573         }
574     }
575 
576     /*Notify the display driven rendering has started*/
577     lv_display_send_event(disp_refr, LV_EVENT_RENDER_START, NULL);
578 
579     disp_refr->last_area = 0;
580     disp_refr->last_part = 0;
581     disp_refr->rendering_in_progress = true;
582 
583     for(i = 0; i < (int32_t)disp_refr->inv_p; i++) {
584         /*Refresh the unjoined areas*/
585         if(disp_refr->inv_area_joined[i]) continue;
586 
587         if(i == last_i) disp_refr->last_area = 1;
588         disp_refr->last_part = 0;
589 
590         lv_area_t inv_a = disp_refr->inv_areas[i];
591         if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_PARTIAL) {
592             /*Calculate the max row num*/
593             int32_t w = lv_area_get_width(&inv_a);
594             int32_t h = lv_area_get_height(&inv_a);
595 
596             int32_t max_row = get_max_row(disp_refr, w, h);
597 
598             int32_t row;
599             int32_t row_last = 0;
600             lv_area_t sub_area;
601             sub_area.x1 = inv_a.x1;
602             sub_area.x2 = inv_a.x2;
603             for(row = inv_a.y1; row + max_row - 1 <= inv_a.y2; row += max_row) {
604                 /*Calc. the next y coordinates of draw_buf*/
605                 sub_area.y1 = row;
606                 sub_area.y2 = row + max_row - 1;
607                 if(sub_area.y2 > inv_a.y2) sub_area.y2 = inv_a.y2;
608                 row_last = sub_area.y2;
609                 if(inv_a.y2 == row_last) disp_refr->last_part = 1;
610                 refr_area(&sub_area);
611                 draw_buf_flush(disp_refr);
612             }
613 
614             /*If the last y coordinates are not handled yet ...*/
615             if(inv_a.y2 != row_last) {
616                 /*Calc. the next y coordinates of draw_buf*/
617                 sub_area.y1 = row;
618                 sub_area.y2 = inv_a.y2;
619                 disp_refr->last_part = 1;
620                 refr_area(&sub_area);
621                 draw_buf_flush(disp_refr);
622             }
623         }
624         else if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_FULL ||
625                 disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_DIRECT) {
626             disp_refr->last_part = 1;
627             refr_area(&disp_refr->inv_areas[i]);
628             draw_buf_flush(disp_refr);
629         }
630     }
631 
632     lv_display_send_event(disp_refr, LV_EVENT_RENDER_READY, NULL);
633     disp_refr->rendering_in_progress = false;
634     LV_PROFILER_REFR_END;
635 }
636 
637 /**
638  * Reshape the draw buffer if required
639  * @param layer  pointer to a layer which will be drawn
640  */
layer_reshape_draw_buf(lv_layer_t * layer,uint32_t stride)641 static void layer_reshape_draw_buf(lv_layer_t * layer, uint32_t stride)
642 {
643     lv_draw_buf_t * ret = lv_draw_buf_reshape(
644                               layer->draw_buf,
645                               layer->color_format,
646                               lv_area_get_width(&layer->buf_area),
647                               lv_area_get_height(&layer->buf_area),
648                               stride);
649     LV_UNUSED(ret);
650     LV_ASSERT_NULL(ret);
651 }
652 
653 /**
654  * Refresh an area if there is Virtual Display Buffer
655  * @param area_p  pointer to an area to refresh
656  */
refr_area(const lv_area_t * area_p)657 static void refr_area(const lv_area_t * area_p)
658 {
659     LV_PROFILER_REFR_BEGIN;
660     lv_layer_t * layer = disp_refr->layer_head;
661     layer->draw_buf = disp_refr->buf_act;
662     layer->_clip_area = *area_p;
663     layer->phy_clip_area = *area_p;
664 
665     if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_FULL) {
666         /*In full mode the area is always the full screen, so the buffer area to it too*/
667         layer->buf_area = *area_p;
668         layer_reshape_draw_buf(layer, layer->draw_buf->header.stride);
669 
670     }
671     else if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_PARTIAL) {
672         /*In partial mode render this area to the buffer*/
673         layer->buf_area = *area_p;
674         layer_reshape_draw_buf(layer, LV_STRIDE_AUTO);
675     }
676     else if(disp_refr->render_mode == LV_DISPLAY_RENDER_MODE_DIRECT) {
677         /*In direct mode the the buffer area is always the whole screen*/
678         layer->buf_area.x1 = 0;
679         layer->buf_area.y1 = 0;
680         layer->buf_area.x2 = lv_display_get_horizontal_resolution(disp_refr) - 1;
681         layer->buf_area.y2 = lv_display_get_vertical_resolution(disp_refr) - 1;
682         layer_reshape_draw_buf(layer, layer->draw_buf->header.stride);
683     }
684 
685     /*Try to divide the area to smaller tiles*/
686     uint32_t tile_cnt = 1;
687     int32_t tile_h = lv_area_get_height(area_p);
688     if(LV_COLOR_FORMAT_IS_INDEXED(layer->color_format) == false) {
689         /* Assume that the the buffer size (can be screen sized or smaller in case of partial mode)
690          * and max tile size are the optimal scenario. From this calculate the ideal tile size
691          * and set the tile count and tile height accordingly.
692          */
693         uint32_t max_tile_cnt = disp_refr->tile_cnt;
694         uint32_t total_buf_size = layer->draw_buf->data_size;
695         uint32_t ideal_tile_size = total_buf_size / max_tile_cnt;
696         uint32_t area_buf_size = lv_area_get_size(area_p) * lv_color_format_get_size(layer->color_format);
697 
698         tile_cnt = (area_buf_size + (ideal_tile_size - 1)) / ideal_tile_size; /*Round up*/
699         tile_h = lv_area_get_height(area_p) / tile_cnt;
700     }
701 
702     if(tile_cnt == 1) {
703         refr_configured_layer(layer);
704     }
705     else {
706         /* Don't draw to the layers buffer of the display but create smaller dummy layers which are using the
707          * display's layer buffer. These will be the tiles. By using tiles it's more likely that there will
708          * be independent areas for each draw unit. */
709         lv_layer_t * tile_layers = lv_malloc(tile_cnt * sizeof(lv_layer_t));
710         LV_ASSERT_MALLOC(tile_layers);
711         if(tile_layers == NULL) {
712             disp_refr->refreshed_area = *area_p;
713             LV_PROFILER_REFR_END;
714             return;
715         }
716         uint32_t i;
717         for(i = 0; i < tile_cnt; i++) {
718             lv_area_t tile_area;
719             lv_area_set(&tile_area, area_p->x1, area_p->y1 + i * tile_h,
720                         area_p->x2, area_p->y1 + (i + 1) * tile_h - 1);
721 
722             if(i == tile_cnt - 1) {
723                 tile_area.y2 = area_p->y2;
724             }
725 
726             lv_layer_t * tile_layer = &tile_layers[i];
727             lv_draw_layer_init(tile_layer, NULL, layer->color_format, &tile_area);
728             tile_layer->buf_area = layer->buf_area; /*the buffer is still large*/
729             tile_layer->draw_buf = layer->draw_buf;
730             refr_configured_layer(tile_layer);
731         }
732 
733 
734         /*Wait until all tiles are ready and destroy remove them*/
735         for(i = 0; i < tile_cnt; i++) {
736             lv_layer_t * tile_layer = &tile_layers[i];
737             while(tile_layer->draw_task_head) {
738                 lv_draw_dispatch_wait_for_request();
739                 lv_draw_dispatch();
740             }
741 
742             lv_layer_t * layer_i = disp_refr->layer_head;
743             while(layer_i) {
744                 if(layer_i->next == tile_layer) {
745                     layer_i->next = tile_layer->next;
746                     break;
747                 }
748                 layer_i = layer_i->next;
749             }
750 
751             if(disp_refr->layer_deinit) disp_refr->layer_deinit(disp_refr, tile_layer);
752         }
753         lv_free(tile_layers);
754     }
755 
756     disp_refr->refreshed_area = *area_p;
757     LV_PROFILER_REFR_END;
758 }
759 
refr_configured_layer(lv_layer_t * layer)760 static void refr_configured_layer(lv_layer_t * layer)
761 {
762     LV_PROFILER_REFR_BEGIN;
763 
764     lv_layer_reset(layer);
765 
766     /* In single buffered mode wait here until the buffer is freed.
767      * Else we would draw into the buffer while it's still being transferred to the display*/
768     if(!lv_display_is_double_buffered(disp_refr)) {
769         wait_for_flushing(disp_refr);
770     }
771     /*If the screen is transparent initialize it when the flushing is ready*/
772     if(lv_color_format_has_alpha(disp_refr->color_format)) {
773         lv_area_t clear_area = layer->_clip_area;
774         lv_area_move(&clear_area, -layer->buf_area.x1, -layer->buf_area.y1);
775         lv_draw_buf_clear(layer->draw_buf, &clear_area);
776     }
777 
778     lv_obj_t * top_act_scr = NULL;
779     lv_obj_t * top_prev_scr = NULL;
780 
781     /*Get the most top object which is not covered by others*/
782     top_act_scr = lv_refr_get_top_obj(&layer->_clip_area, lv_display_get_screen_active(disp_refr));
783     if(disp_refr->prev_scr) {
784         top_prev_scr = lv_refr_get_top_obj(&layer->_clip_area, disp_refr->prev_scr);
785     }
786 
787     /*Draw a bottom layer background if there is no top object*/
788     if(top_act_scr == NULL && top_prev_scr == NULL) {
789         refr_obj_and_children(layer, lv_display_get_layer_bottom(disp_refr));
790     }
791 
792     if(disp_refr->draw_prev_over_act) {
793         if(top_act_scr == NULL) top_act_scr = disp_refr->act_scr;
794         refr_obj_and_children(layer, top_act_scr);
795 
796         /*Refresh the previous screen if any*/
797         if(disp_refr->prev_scr) {
798             if(top_prev_scr == NULL) top_prev_scr = disp_refr->prev_scr;
799             refr_obj_and_children(layer, top_prev_scr);
800         }
801     }
802     else {
803         /*Refresh the previous screen if any*/
804         if(disp_refr->prev_scr) {
805             if(top_prev_scr == NULL) top_prev_scr = disp_refr->prev_scr;
806             refr_obj_and_children(layer, top_prev_scr);
807         }
808 
809         if(top_act_scr == NULL) top_act_scr = disp_refr->act_scr;
810         refr_obj_and_children(layer, top_act_scr);
811     }
812 
813     /*Also refresh top and sys layer unconditionally*/
814     refr_obj_and_children(layer, lv_display_get_layer_top(disp_refr));
815     refr_obj_and_children(layer, lv_display_get_layer_sys(disp_refr));
816 
817     LV_PROFILER_REFR_END;
818 }
819 
820 /**
821  * Search the most top object which fully covers an area
822  * @param area_p pointer to an area
823  * @param obj the first object to start the searching (typically a screen)
824  * @return
825  */
lv_refr_get_top_obj(const lv_area_t * area_p,lv_obj_t * obj)826 static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj)
827 {
828     lv_obj_t * found_p = NULL;
829 
830     if(lv_area_is_in(area_p, &obj->coords, 0) == false) return NULL;
831     if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return NULL;
832     if(lv_obj_get_layer_type(obj) != LV_LAYER_TYPE_NONE) return NULL;
833     if(lv_obj_get_style_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) return NULL;
834 
835     /*If this object is fully cover the draw area then check the children too*/
836     lv_cover_check_info_t info;
837     info.res = LV_COVER_RES_COVER;
838     info.area = area_p;
839     lv_obj_send_event(obj, LV_EVENT_COVER_CHECK, &info);
840     if(info.res == LV_COVER_RES_MASKED) return NULL;
841 
842     int32_t i;
843     int32_t child_cnt = lv_obj_get_child_count(obj);
844     for(i = child_cnt - 1; i >= 0; i--) {
845         lv_obj_t * child = obj->spec_attr->children[i];
846         found_p = lv_refr_get_top_obj(area_p, child);
847 
848         /*If a children is ok then break*/
849         if(found_p != NULL) {
850             break;
851         }
852     }
853 
854     /*If no better children use this object*/
855     if(found_p == NULL && info.res == LV_COVER_RES_COVER) {
856         found_p = obj;
857     }
858 
859     return found_p;
860 }
861 
862 /**
863  * Make the refreshing from an object. Draw all its children and the youngers too.
864  * @param top_p pointer to an objects. Start the drawing from it.
865  * @param mask_p pointer to an area, the objects will be drawn only here
866  */
refr_obj_and_children(lv_layer_t * layer,lv_obj_t * top_obj)867 static void refr_obj_and_children(lv_layer_t * layer, lv_obj_t * top_obj)
868 {
869     /*Normally always will be a top_obj (at least the screen)
870      *but in special cases (e.g. if the screen has alpha) it won't.
871      *In this case use the screen directly*/
872     if(top_obj == NULL) top_obj = lv_display_get_screen_active(disp_refr);
873     if(top_obj == NULL) return;  /*Shouldn't happen*/
874 
875     LV_PROFILER_REFR_BEGIN;
876     /*Refresh the top object and its children*/
877     refr_obj(layer, top_obj);
878 
879     /*Draw the 'younger' sibling objects because they can be on top_obj*/
880     lv_obj_t * parent;
881     lv_obj_t * border_p = top_obj;
882 
883     parent = lv_obj_get_parent(top_obj);
884 
885     /*Do until not reach the screen*/
886     while(parent != NULL) {
887         bool go = false;
888         uint32_t i;
889         uint32_t child_cnt = lv_obj_get_child_count(parent);
890         for(i = 0; i < child_cnt; i++) {
891             lv_obj_t * child = parent->spec_attr->children[i];
892             if(!go) {
893                 if(child == border_p) go = true;
894             }
895             else {
896                 /*Refresh the objects*/
897                 refr_obj(layer, child);
898             }
899         }
900 
901         /*Call the post draw function of the parents of the to object*/
902         lv_obj_send_event(parent, LV_EVENT_DRAW_POST_BEGIN, (void *)layer);
903         lv_obj_send_event(parent, LV_EVENT_DRAW_POST, (void *)layer);
904         lv_obj_send_event(parent, LV_EVENT_DRAW_POST_END, (void *)layer);
905 
906         /*The new border will be the last parents,
907          *so the 'younger' brothers of parent will be refreshed*/
908         border_p = parent;
909         /*Go a level deeper*/
910         parent = lv_obj_get_parent(parent);
911     }
912     LV_PROFILER_REFR_END;
913 }
914 
layer_get_area(lv_layer_t * layer,lv_obj_t * obj,lv_layer_type_t layer_type,lv_area_t * layer_area_out,lv_area_t * obj_draw_size_out)915 static lv_result_t layer_get_area(lv_layer_t * layer, lv_obj_t * obj, lv_layer_type_t layer_type,
916                                   lv_area_t * layer_area_out, lv_area_t * obj_draw_size_out)
917 {
918     int32_t ext_draw_size = lv_obj_get_ext_draw_size(obj);
919     lv_obj_get_coords(obj, obj_draw_size_out);
920     lv_area_increase(obj_draw_size_out, ext_draw_size, ext_draw_size);
921 
922     if(layer_type == LV_LAYER_TYPE_TRANSFORM) {
923         /*Get the transformed area and clip it to the current clip area.
924          *This area needs to be updated on the screen.*/
925         lv_area_t clip_coords_for_obj;
926         lv_area_t tranf_coords = *obj_draw_size_out;
927         lv_obj_get_transformed_area(obj, &tranf_coords, LV_OBJ_POINT_TRANSFORM_FLAG_NONE);
928         if(!lv_area_intersect(&clip_coords_for_obj, &layer->_clip_area, &tranf_coords)) {
929             return LV_RESULT_INVALID;
930         }
931 
932         /*Transform back (inverse) the transformed area.
933          *It will tell which area of the non-transformed widget needs to be redrawn
934          *in order to cover transformed area after transformation.*/
935         lv_area_t inverse_clip_coords_for_obj = clip_coords_for_obj;
936         lv_obj_get_transformed_area(obj, &inverse_clip_coords_for_obj, LV_OBJ_POINT_TRANSFORM_FLAG_INVERSE);
937         if(!lv_area_intersect(&inverse_clip_coords_for_obj, &inverse_clip_coords_for_obj, obj_draw_size_out)) {
938             return LV_RESULT_INVALID;
939         }
940 
941         *layer_area_out = inverse_clip_coords_for_obj;
942         lv_area_increase(layer_area_out, 5, 5); /*To avoid rounding error*/
943     }
944     else if(layer_type == LV_LAYER_TYPE_SIMPLE) {
945         lv_area_t clip_coords_for_obj;
946         if(!lv_area_intersect(&clip_coords_for_obj, &layer->_clip_area, obj_draw_size_out)) {
947             return LV_RESULT_INVALID;
948         }
949         *layer_area_out = clip_coords_for_obj;
950     }
951     else {
952         LV_LOG_WARN("Unhandled layer type");
953         return LV_RESULT_INVALID;
954     }
955 
956     return LV_RESULT_OK;
957 }
958 
alpha_test_area_on_obj(lv_obj_t * obj,const lv_area_t * area)959 static bool alpha_test_area_on_obj(lv_obj_t * obj, const lv_area_t * area)
960 {
961     /*Test for alpha by assuming there is no alpha. If it fails, fall back to rendering with alpha*/
962     /*If the layer area is not fully on the object, it can't fully cover it*/
963     if(!lv_area_is_on(area, &obj->coords)) return true;
964 
965     lv_cover_check_info_t info;
966     info.res = LV_COVER_RES_COVER;
967     info.area = area;
968     lv_obj_send_event(obj, LV_EVENT_COVER_CHECK, &info);
969     if(info.res == LV_COVER_RES_COVER) return false;
970     else return true;
971 }
972 
973 #if LV_DRAW_TRANSFORM_USE_MATRIX
974 
obj_get_matrix(lv_obj_t * obj,lv_matrix_t * matrix)975 static bool obj_get_matrix(lv_obj_t * obj, lv_matrix_t * matrix)
976 {
977     lv_matrix_identity(matrix);
978 
979     const lv_matrix_t * obj_matrix = lv_obj_get_transform(obj);
980     if(obj_matrix) {
981         lv_matrix_translate(matrix, obj->coords.x1, obj->coords.y1);
982         lv_matrix_multiply(matrix, obj_matrix);
983         lv_matrix_translate(matrix, -obj->coords.x1, -obj->coords.y1);
984         return true;
985     }
986 
987     lv_point_t pivot = {
988         .x = lv_obj_get_style_transform_pivot_x(obj, 0),
989         .y = lv_obj_get_style_transform_pivot_y(obj, 0)
990     };
991 
992     pivot.x = obj->coords.x1 + lv_pct_to_px(pivot.x, lv_area_get_width(&obj->coords));
993     pivot.y = obj->coords.y1 + lv_pct_to_px(pivot.y, lv_area_get_height(&obj->coords));
994 
995     int32_t rotation = lv_obj_get_style_transform_rotation(obj, 0);
996     int32_t scale_x = lv_obj_get_style_transform_scale_x(obj, 0);
997     int32_t scale_y = lv_obj_get_style_transform_scale_y(obj, 0);
998     int32_t skew_x = lv_obj_get_style_transform_skew_x(obj, 0);
999     int32_t skew_y = lv_obj_get_style_transform_skew_y(obj, 0);
1000 
1001     if(scale_x <= 0 || scale_y <= 0) {
1002         /* NOT draw if scale is negative or zero */
1003         return false;
1004     }
1005 
1006     /* generate the obj matrix */
1007     lv_matrix_translate(matrix, pivot.x, pivot.y);
1008     if(rotation != 0) {
1009         lv_matrix_rotate(matrix, rotation * 0.1f);
1010     }
1011 
1012     if(scale_x != LV_SCALE_NONE || scale_y != LV_SCALE_NONE) {
1013         lv_matrix_scale(
1014             matrix,
1015             (float)scale_x / LV_SCALE_NONE,
1016             (float)scale_y / LV_SCALE_NONE
1017         );
1018     }
1019 
1020     if(skew_x != 0 || skew_y != 0) {
1021         lv_matrix_skew(matrix, skew_x, skew_y);
1022     }
1023 
1024     lv_matrix_translate(matrix, -pivot.x, -pivot.y);
1025     return true;
1026 }
1027 
refr_obj_matrix(lv_layer_t * layer,lv_obj_t * obj)1028 static void refr_obj_matrix(lv_layer_t * layer, lv_obj_t * obj)
1029 {
1030     LV_PROFILER_REFR_BEGIN;
1031     lv_matrix_t obj_matrix;
1032     if(!obj_get_matrix(obj, &obj_matrix)) {
1033         /* NOT draw if obj matrix is not available */
1034         LV_PROFILER_REFR_END;
1035         return;
1036     }
1037 
1038     lv_matrix_t matrix_inv;
1039     if(!lv_matrix_inverse(&matrix_inv, &obj_matrix)) {
1040         /* NOT draw if matrix is not invertible */
1041         LV_PROFILER_REFR_END;
1042         return;
1043     }
1044 
1045     /* save original matrix */
1046     lv_matrix_t ori_matrix = layer->matrix;
1047 
1048     /* apply the obj matrix */
1049     lv_matrix_multiply(&layer->matrix, &obj_matrix);
1050 
1051     /* calculate clip area without transform */
1052     lv_area_t clip_area = layer->_clip_area;
1053     lv_area_t clip_area_ori = layer->_clip_area;
1054     clip_area = lv_matrix_transform_area(&matrix_inv, &clip_area);
1055 
1056     /* increase the clip area by 1 pixel to avoid rounding errors */
1057     if(!lv_matrix_is_identity_or_translation(&obj_matrix)) {
1058         lv_area_increase(&clip_area, 1, 1);
1059     }
1060 
1061     layer->_clip_area = clip_area;
1062 
1063     /* redraw obj */
1064     lv_obj_redraw(layer, obj);
1065 
1066     /* restore original matrix */
1067     layer->matrix = ori_matrix;
1068     /* restore clip area */
1069     layer->_clip_area = clip_area_ori;
1070     LV_PROFILER_REFR_END;
1071 }
1072 
refr_check_obj_clip_overflow(lv_layer_t * layer,lv_obj_t * obj)1073 static bool refr_check_obj_clip_overflow(lv_layer_t * layer, lv_obj_t * obj)
1074 {
1075     if(lv_obj_get_style_transform_rotation(obj, 0) == 0) {
1076         return false;
1077     }
1078 
1079     /*Truncate the area to the object*/
1080     lv_area_t obj_coords;
1081     int32_t ext_size = lv_obj_get_ext_draw_size(obj);
1082     lv_area_copy(&obj_coords, &obj->coords);
1083     lv_area_increase(&obj_coords, ext_size, ext_size);
1084 
1085     lv_obj_get_transformed_area(obj, &obj_coords, LV_OBJ_POINT_TRANSFORM_FLAG_RECURSIVE);
1086 
1087     lv_area_t clip_coords_for_obj;
1088     if(!lv_area_intersect(&clip_coords_for_obj, &layer->_clip_area, &obj_coords)) {
1089         return false;
1090     }
1091 
1092     bool has_clip = lv_memcmp(&clip_coords_for_obj, &obj_coords, sizeof(lv_area_t)) != 0;
1093     return has_clip;
1094 }
1095 
1096 #endif /* LV_DRAW_TRANSFORM_USE_MATRIX */
1097 
refr_obj(lv_layer_t * layer,lv_obj_t * obj)1098 static void refr_obj(lv_layer_t * layer, lv_obj_t * obj)
1099 {
1100     if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return;
1101 
1102     /*If `opa_layered != LV_OPA_COVER` draw the widget on a new layer and blend that layer with the given opacity.*/
1103     const lv_opa_t opa_layered = lv_obj_get_style_opa_layered(obj, LV_PART_MAIN);
1104     if(opa_layered < LV_OPA_MIN) return;
1105 
1106     const lv_opa_t layer_opa_ori = layer->opa;
1107 
1108     /*Normal `opa` (not layered) will just scale down `bg_opa`, `text_opa`, etc, in the upcoming drawings.*/
1109     const lv_opa_t opa_main = lv_obj_get_style_opa(obj, LV_PART_MAIN);
1110     if(opa_main < LV_OPA_MAX) {
1111         layer->opa = LV_OPA_MIX2(layer_opa_ori, opa_main);
1112     }
1113 
1114     lv_layer_type_t layer_type = lv_obj_get_layer_type(obj);
1115     if(layer_type == LV_LAYER_TYPE_NONE) {
1116         lv_obj_redraw(layer, obj);
1117     }
1118 #if LV_DRAW_TRANSFORM_USE_MATRIX
1119     /*If the layer opa is full then use the matrix transform*/
1120     else if(opa_layered >= LV_OPA_MAX && !refr_check_obj_clip_overflow(layer, obj)) {
1121         refr_obj_matrix(layer, obj);
1122     }
1123 #endif /* LV_DRAW_TRANSFORM_USE_MATRIX */
1124     else {
1125         lv_area_t layer_area_full;
1126         lv_area_t obj_draw_size;
1127         lv_result_t res = layer_get_area(layer, obj, layer_type, &layer_area_full, &obj_draw_size);
1128         if(res != LV_RESULT_OK) return;
1129 
1130         /*Simple layers can be subdivided into smaller layers*/
1131         uint32_t max_rgb_row_height = lv_area_get_height(&layer_area_full);
1132         uint32_t max_argb_row_height = lv_area_get_height(&layer_area_full);
1133         if(layer_type == LV_LAYER_TYPE_SIMPLE) {
1134             int32_t w = lv_area_get_width(&layer_area_full);
1135             uint8_t px_size = lv_color_format_get_size(disp_refr->color_format);
1136             max_rgb_row_height = LV_DRAW_LAYER_SIMPLE_BUF_SIZE / w / px_size;
1137             max_argb_row_height = LV_DRAW_LAYER_SIMPLE_BUF_SIZE / w / sizeof(lv_color32_t);
1138         }
1139 
1140         lv_area_t layer_area_act;
1141         layer_area_act.x1 = layer_area_full.x1;
1142         layer_area_act.x2 = layer_area_full.x2;
1143         layer_area_act.y1 = layer_area_full.y1;
1144         layer_area_act.y2 = layer_area_full.y1;
1145 
1146         while(layer_area_act.y2 < layer_area_full.y2) {
1147             /* Test with an RGB layer size (which is larger than the ARGB layer size)
1148              * If it really doesn't need alpha use it. Else switch to the ARGB size*/
1149             layer_area_act.y2 = layer_area_act.y1 + max_rgb_row_height - 1;
1150             if(layer_area_act.y2 > layer_area_full.y2) layer_area_act.y2 = layer_area_full.y2;
1151 
1152             const void * bitmap_mask_src = lv_obj_get_style_bitmap_mask_src(obj, 0);
1153             bool area_need_alpha = bitmap_mask_src || alpha_test_area_on_obj(obj, &layer_area_act);
1154 
1155             if(area_need_alpha) {
1156                 layer_area_act.y2 = layer_area_act.y1 + max_argb_row_height - 1;
1157                 if(layer_area_act.y2 > layer_area_full.y2) layer_area_act.y2 = layer_area_full.y2;
1158             }
1159 
1160             lv_layer_t * new_layer = lv_draw_layer_create(layer,
1161                                                           area_need_alpha ? LV_COLOR_FORMAT_ARGB8888 : LV_COLOR_FORMAT_NATIVE, &layer_area_act);
1162             lv_obj_redraw(new_layer, obj);
1163 
1164             lv_point_t pivot = {
1165                 .x = lv_obj_get_style_transform_pivot_x(obj, 0),
1166                 .y = lv_obj_get_style_transform_pivot_y(obj, 0)
1167             };
1168 
1169             if(LV_COORD_IS_PCT(pivot.x)) {
1170                 pivot.x = (LV_COORD_GET_PCT(pivot.x) * lv_area_get_width(&obj->coords)) / 100;
1171             }
1172             if(LV_COORD_IS_PCT(pivot.y)) {
1173                 pivot.y = (LV_COORD_GET_PCT(pivot.y) * lv_area_get_height(&obj->coords)) / 100;
1174             }
1175 
1176             lv_draw_image_dsc_t layer_draw_dsc;
1177             lv_draw_image_dsc_init(&layer_draw_dsc);
1178             layer_draw_dsc.pivot.x = obj->coords.x1 + pivot.x - new_layer->buf_area.x1;
1179             layer_draw_dsc.pivot.y = obj->coords.y1 + pivot.y - new_layer->buf_area.y1;
1180 
1181             layer_draw_dsc.opa = opa_layered;
1182             layer_draw_dsc.rotation = lv_obj_get_style_transform_rotation(obj, 0);
1183             while(layer_draw_dsc.rotation > 3600) layer_draw_dsc.rotation -= 3600;
1184             while(layer_draw_dsc.rotation < 0) layer_draw_dsc.rotation += 3600;
1185             layer_draw_dsc.scale_x = lv_obj_get_style_transform_scale_x(obj, 0);
1186             layer_draw_dsc.scale_y = lv_obj_get_style_transform_scale_y(obj, 0);
1187             layer_draw_dsc.skew_x = lv_obj_get_style_transform_skew_x(obj, 0);
1188             layer_draw_dsc.skew_y = lv_obj_get_style_transform_skew_y(obj, 0);
1189             layer_draw_dsc.blend_mode = lv_obj_get_style_blend_mode(obj, 0);
1190             layer_draw_dsc.antialias = disp_refr->antialiasing;
1191             layer_draw_dsc.bitmap_mask_src = bitmap_mask_src;
1192             layer_draw_dsc.image_area = obj_draw_size;
1193             layer_draw_dsc.src = new_layer;
1194 
1195             lv_draw_layer(layer, &layer_draw_dsc, &layer_area_act);
1196 
1197             layer_area_act.y1 = layer_area_act.y2 + 1;
1198         }
1199     }
1200 
1201     /* Restore the original layer opa */
1202     layer->opa = layer_opa_ori;
1203 }
1204 
get_max_row(lv_display_t * disp,int32_t area_w,int32_t area_h)1205 static uint32_t get_max_row(lv_display_t * disp, int32_t area_w, int32_t area_h)
1206 {
1207     lv_color_format_t cf = disp->color_format;
1208     uint32_t stride = lv_draw_buf_width_to_stride(area_w, cf);
1209     uint32_t overhead = LV_COLOR_INDEXED_PALETTE_SIZE(cf) * sizeof(lv_color32_t);
1210 
1211     if(stride == 0) {
1212         LV_LOG_WARN("Invalid stride. Value is 0");
1213         return 0;
1214     }
1215 
1216     int32_t max_row = (uint32_t)(disp->buf_act->data_size - overhead) / stride;
1217 
1218     if(max_row > area_h) max_row = area_h;
1219 
1220     /*Round down the lines of draw_buf if rounding is added*/
1221     lv_area_t tmp;
1222     tmp.x1 = 0;
1223     tmp.x2 = 0;
1224     tmp.y1 = 0;
1225 
1226     int32_t h_tmp = max_row;
1227     do {
1228         tmp.y2 = h_tmp - 1;
1229         lv_display_send_event(disp_refr, LV_EVENT_INVALIDATE_AREA, &tmp);
1230 
1231         /*If this height fits into `max_row` then fine*/
1232         if(lv_area_get_height(&tmp) <= max_row) break;
1233 
1234         /*Decrement the height of the area until it fits into `max_row` after rounding*/
1235         h_tmp--;
1236     } while(h_tmp > 0);
1237 
1238     if(h_tmp <= 0) {
1239         LV_LOG_WARN("Can't set draw_buf height using the round function. (Wrong round_cb or too "
1240                     "small draw_buf)");
1241         return 0;
1242     }
1243     else {
1244         max_row = tmp.y2 + 1;
1245     }
1246 
1247     return max_row;
1248 }
1249 
1250 /**
1251  * Flush the content of the draw buffer
1252  */
draw_buf_flush(lv_display_t * disp)1253 static void draw_buf_flush(lv_display_t * disp)
1254 {
1255     /*Flush the rendered content to the display*/
1256     lv_layer_t * layer = disp->layer_head;
1257 
1258     while(layer->draw_task_head) {
1259         lv_draw_dispatch_wait_for_request();
1260         lv_draw_dispatch();
1261     }
1262 
1263     /* In double buffered mode wait until the other buffer is freed
1264      * and driver is ready to receive the new buffer.
1265      * If we need to wait here it means that the content of one buffer is being sent to display
1266      * and other buffer already contains the new rendered image. */
1267     if(lv_display_is_double_buffered(disp)) {
1268         wait_for_flushing(disp_refr);
1269     }
1270 
1271     disp->flushing = 1;
1272 
1273     if(disp->last_area && disp->last_part) disp->flushing_last = 1;
1274     else disp->flushing_last = 0;
1275 
1276     bool flushing_last = disp->flushing_last;
1277 
1278     if(disp->flush_cb) {
1279         call_flush_cb(disp, &disp->refreshed_area, layer->draw_buf->data);
1280     }
1281     /*If there are 2 buffers swap them. With direct mode swap only on the last area*/
1282     if(lv_display_is_double_buffered(disp) && (disp->render_mode != LV_DISPLAY_RENDER_MODE_DIRECT || flushing_last)) {
1283         if(disp->buf_act == disp->buf_1) {
1284             disp->buf_act = disp->buf_2;
1285         }
1286         else {
1287             disp->buf_act = disp->buf_1;
1288         }
1289     }
1290 }
1291 
call_flush_cb(lv_display_t * disp,const lv_area_t * area,uint8_t * px_map)1292 static void call_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map)
1293 {
1294     LV_PROFILER_REFR_BEGIN;
1295     LV_TRACE_REFR("Calling flush_cb on (%d;%d)(%d;%d) area with %p image pointer",
1296                   (int)area->x1, (int)area->y1, (int)area->x2, (int)area->y2, (void *)px_map);
1297 
1298     lv_area_t offset_area = {
1299         .x1 = area->x1 + disp->offset_x,
1300         .y1 = area->y1 + disp->offset_y,
1301         .x2 = area->x2 + disp->offset_x,
1302         .y2 = area->y2 + disp->offset_y
1303     };
1304 
1305     lv_display_send_event(disp, LV_EVENT_FLUSH_START, &offset_area);
1306 
1307     /*For backward compatibility support LV_COLOR_16_SWAP (from v8)*/
1308 #if defined(LV_COLOR_16_SWAP) && LV_COLOR_16_SWAP
1309     lv_draw_sw_rgb565_swap(px_map, lv_area_get_size(&offset_area));
1310 #endif
1311 
1312     disp->flush_cb(disp, &offset_area, px_map);
1313     lv_display_send_event(disp, LV_EVENT_FLUSH_FINISH, &offset_area);
1314 
1315     LV_PROFILER_REFR_END;
1316 }
1317 
wait_for_flushing(lv_display_t * disp)1318 static void wait_for_flushing(lv_display_t * disp)
1319 {
1320     LV_PROFILER_REFR_BEGIN;
1321     LV_LOG_TRACE("begin");
1322 
1323     lv_display_send_event(disp, LV_EVENT_FLUSH_WAIT_START, NULL);
1324 
1325     if(disp->flush_wait_cb) {
1326         if(disp->flushing) {
1327             disp->flush_wait_cb(disp);
1328         }
1329         disp->flushing = 0;
1330     }
1331     else {
1332         while(disp->flushing);
1333     }
1334     disp->flushing_last = 0;
1335 
1336     lv_display_send_event(disp, LV_EVENT_FLUSH_WAIT_FINISH, NULL);
1337 
1338     LV_LOG_TRACE("end");
1339     LV_PROFILER_REFR_END;
1340 }
1341