1 /**
2  * @file lv_refr.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include <stddef.h>
10 #include "lv_refr.h"
11 #include "lv_disp.h"
12 #include "../lv_hal/lv_hal_tick.h"
13 #include "../lv_hal/lv_hal_disp.h"
14 #include "../lv_misc/lv_task.h"
15 #include "../lv_misc/lv_mem.h"
16 #include "../lv_misc/lv_math.h"
17 #include "../lv_misc/lv_gc.h"
18 #include "../lv_draw/lv_draw.h"
19 #include "../lv_font/lv_font_fmt_txt.h"
20 #include "../lv_gpu/lv_gpu_stm32_dma2d.h"
21 
22 #if LV_USE_PERF_MONITOR
23     #include "../lv_widgets/lv_label.h"
24 #endif
25 
26 #if defined(LV_GC_INCLUDE)
27     #include LV_GC_INCLUDE
28 #endif /* LV_ENABLE_GC */
29 
30 /*********************
31  *      DEFINES
32  *********************/
33 /* Draw translucent random colored areas on the invalidated (redrawn) areas*/
34 #define MASK_AREA_DEBUG 0
35 
36 /**********************
37  *      TYPEDEFS
38  **********************/
39 
40 /**********************
41  *  STATIC PROTOTYPES
42  **********************/
43 static void lv_refr_join_area(void);
44 static void lv_refr_areas(void);
45 static void lv_refr_area(const lv_area_t * area_p);
46 static void lv_refr_area_part(const lv_area_t * area_p);
47 static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj);
48 static void lv_refr_obj_and_children(lv_obj_t * top_p, const lv_area_t * mask_p);
49 static void lv_refr_obj(lv_obj_t * obj, const lv_area_t * mask_ori_p);
50 static void lv_refr_vdb_flush(void);
51 
52 /**********************
53  *  STATIC VARIABLES
54  **********************/
55 static uint32_t px_num;
56 static lv_disp_t * disp_refr; /*Display being refreshed*/
57 
58 /**********************
59  *      MACROS
60  **********************/
61 
62 /**********************
63  *   GLOBAL FUNCTIONS
64  **********************/
65 
66 /**
67  * Initialize the screen refresh subsystem
68  */
_lv_refr_init(void)69 void _lv_refr_init(void)
70 {
71     /*Nothing to do*/
72 }
73 
74 /**
75  * Redraw the invalidated areas now.
76  * Normally the redrawing is periodically executed in `lv_task_handler` but a long blocking process
77  * can prevent the call of `lv_task_handler`. In this case if the the GUI is updated in the process
78  * (e.g. progress bar) this function can be called when the screen should be updated.
79  * @param disp pointer to display to refresh. NULL to refresh all displays.
80  */
lv_refr_now(lv_disp_t * disp)81 void lv_refr_now(lv_disp_t * disp)
82 {
83 #if LV_USE_ANIMATION
84     lv_anim_refr_now();
85 #endif
86 
87     if(disp) {
88         _lv_disp_refr_task(disp->refr_task);
89     }
90     else {
91         lv_disp_t * d;
92         d = lv_disp_get_next(NULL);
93         while(d) {
94             _lv_disp_refr_task(d->refr_task);
95             d = lv_disp_get_next(d);
96         }
97     }
98 }
99 
100 /**
101  * Invalidate an area on display to redraw it
102  * @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
103  * @param disp pointer to display where the area should be invalidated (NULL can be used if there is
104  * only one display)
105  */
_lv_inv_area(lv_disp_t * disp,const lv_area_t * area_p)106 void _lv_inv_area(lv_disp_t * disp, const lv_area_t * area_p)
107 {
108     if(!disp) disp = lv_disp_get_default();
109     if(!disp) return;
110 
111     /*Clear the invalidate buffer if the parameter is NULL*/
112     if(area_p == NULL) {
113         disp->inv_p = 0;
114         return;
115     }
116 
117     lv_area_t scr_area;
118     scr_area.x1 = 0;
119     scr_area.y1 = 0;
120     scr_area.x2 = lv_disp_get_hor_res(disp) - 1;
121     scr_area.y2 = lv_disp_get_ver_res(disp) - 1;
122 
123     lv_area_t com_area;
124     bool suc;
125 
126     suc = _lv_area_intersect(&com_area, area_p, &scr_area);
127 
128     /*The area is truncated to the screen*/
129     if(suc != false) {
130         if(disp->driver.rounder_cb) disp->driver.rounder_cb(&disp->driver, &com_area);
131 
132         /*Save only if this area is not in one of the saved areas*/
133         uint16_t i;
134         for(i = 0; i < disp->inv_p; i++) {
135             if(_lv_area_is_in(&com_area, &disp->inv_areas[i], 0) != false) return;
136         }
137 
138         /*Save the area*/
139         if(disp->inv_p < LV_INV_BUF_SIZE) {
140             lv_area_copy(&disp->inv_areas[disp->inv_p], &com_area);
141         }
142         else {   /*If no place for the area add the screen*/
143             disp->inv_p = 0;
144             lv_area_copy(&disp->inv_areas[disp->inv_p], &scr_area);
145         }
146         disp->inv_p++;
147         lv_task_set_prio(disp->refr_task, LV_REFR_TASK_PRIO);
148     }
149 }
150 
151 /**
152  * Get the display which is being refreshed
153  * @return the display being refreshed
154  */
_lv_refr_get_disp_refreshing(void)155 lv_disp_t * _lv_refr_get_disp_refreshing(void)
156 {
157     return disp_refr;
158 }
159 
160 /**
161  * Set the display which is being refreshed.
162  * It shouldn't be used directly by the user.
163  * It can be used to trick the drawing functions about there is an active display.
164  * @param the display being refreshed
165  */
_lv_refr_set_disp_refreshing(lv_disp_t * disp)166 void _lv_refr_set_disp_refreshing(lv_disp_t * disp)
167 {
168     disp_refr = disp;
169 }
170 
171 /**
172  * Called periodically to handle the refreshing
173  * @param task pointer to the task itself
174  */
_lv_disp_refr_task(lv_task_t * task)175 void _lv_disp_refr_task(lv_task_t * task)
176 {
177     LV_LOG_TRACE("lv_refr_task: started");
178 
179     uint32_t start = lv_tick_get();
180     uint32_t elaps = 0;
181 
182     disp_refr = task->user_data;
183 
184 #if LV_USE_PERF_MONITOR == 0
185     /* Ensure the task does not run again automatically.
186      * This is done before refreshing in case refreshing invalidates something else.
187      */
188     lv_task_set_prio(task, LV_TASK_PRIO_OFF);
189 #endif
190 
191     /*Do nothing if there is no active screen*/
192     if(disp_refr->act_scr == NULL) {
193         disp_refr->inv_p = 0;
194         return;
195     }
196 
197     lv_refr_join_area();
198 
199     lv_refr_areas();
200 
201     /*If refresh happened ...*/
202     if(disp_refr->inv_p != 0) {
203         /* In true double buffered mode copy the refreshed areas to the new VDB to keep it up to date.
204          * With set_px_cb we don't know anything about the buffer (even it's size) so skip copying.*/
205         if(lv_disp_is_true_double_buf(disp_refr)) {
206             if(disp_refr->driver.set_px_cb) {
207                 LV_LOG_WARN("Can't handle 2 screen sized buffers with set_px_cb. Display is not refreshed.");
208             }
209             else {
210                 lv_disp_buf_t * vdb = lv_disp_get_buf(disp_refr);
211 
212                 /*Flush the content of the VDB*/
213                 lv_refr_vdb_flush();
214 
215                 /* With true double buffering the flushing should be only the address change of the
216                  * current frame buffer. Wait until the address change is ready and copy the changed
217                  * content to the other frame buffer (new active VDB) to keep the buffers synchronized*/
218                 while(vdb->flushing);
219 
220                 lv_color_t * copy_buf = NULL;
221 #if LV_USE_GPU_STM32_DMA2D
222                 LV_UNUSED(copy_buf);
223 #else
224                 copy_buf = _lv_mem_buf_get(disp_refr->driver.hor_res * sizeof(lv_color_t));
225 #endif
226 
227                 uint8_t * buf_act = (uint8_t *)vdb->buf_act;
228                 uint8_t * buf_ina = (uint8_t *)vdb->buf_act == vdb->buf1 ? vdb->buf2 : vdb->buf1;
229 
230                 lv_coord_t hres = lv_disp_get_hor_res(disp_refr);
231                 uint16_t a;
232                 for(a = 0; a < disp_refr->inv_p; a++) {
233                     if(disp_refr->inv_area_joined[a] == 0) {
234                         uint32_t start_offs =
235                             (hres * disp_refr->inv_areas[a].y1 + disp_refr->inv_areas[a].x1) * sizeof(lv_color_t);
236 #if LV_USE_GPU_STM32_DMA2D
237                         lv_gpu_stm32_dma2d_copy((lv_color_t *)(buf_act + start_offs), disp_refr->driver.hor_res,
238                                                 (lv_color_t *)(buf_ina + start_offs), disp_refr->driver.hor_res,
239                                                 lv_area_get_width(&disp_refr->inv_areas[a]),
240                                                 lv_area_get_height(&disp_refr->inv_areas[a]));
241 #else
242 
243                         lv_coord_t y;
244                         uint32_t line_length = lv_area_get_width(&disp_refr->inv_areas[a]) * sizeof(lv_color_t);
245 
246                         for(y = disp_refr->inv_areas[a].y1; y <= disp_refr->inv_areas[a].y2; y++) {
247                             /* The frame buffer is probably in an external RAM where sequential access is much faster.
248                              * So first copy a line into a buffer and write it back the ext. RAM */
249                             _lv_memcpy(copy_buf, buf_ina + start_offs, line_length);
250                             _lv_memcpy(buf_act + start_offs, copy_buf, line_length);
251                             start_offs += hres * sizeof(lv_color_t);
252                         }
253 #endif
254                     }
255                 }
256 
257                 if(copy_buf) _lv_mem_buf_release(copy_buf);
258             }
259         } /*End of true double buffer handling*/
260 
261         /*Clean up*/
262         _lv_memset_00(disp_refr->inv_areas, sizeof(disp_refr->inv_areas));
263         _lv_memset_00(disp_refr->inv_area_joined, sizeof(disp_refr->inv_area_joined));
264         disp_refr->inv_p = 0;
265 
266         elaps = lv_tick_elaps(start);
267         /*Call monitor cb if present*/
268         if(disp_refr->driver.monitor_cb) {
269             disp_refr->driver.monitor_cb(&disp_refr->driver, elaps, px_num);
270         }
271     }
272 
273     _lv_mem_buf_free_all();
274     _lv_font_clean_up_fmt_txt();
275 
276 #if LV_USE_PERF_MONITOR && LV_USE_LABEL
277     static lv_obj_t * perf_label = NULL;
278     if(perf_label == NULL) {
279         perf_label = lv_label_create(lv_layer_sys(), NULL);
280         lv_label_set_align(perf_label, LV_LABEL_ALIGN_RIGHT);
281         lv_obj_set_style_local_bg_opa(perf_label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER);
282         lv_obj_set_style_local_bg_color(perf_label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
283         lv_obj_set_style_local_text_color(perf_label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
284         lv_obj_set_style_local_pad_top(perf_label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 3);
285         lv_obj_set_style_local_pad_bottom(perf_label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 3);
286         lv_obj_set_style_local_pad_left(perf_label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 3);
287         lv_obj_set_style_local_pad_right(perf_label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 3);
288         lv_label_set_text(perf_label, "?");
289         lv_obj_align(perf_label, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
290     }
291 
292     static uint32_t perf_last_time = 0;
293     static uint32_t elaps_max = 1;
294     if(lv_tick_elaps(perf_last_time) < 300) {
295         elaps_max = LV_MATH_MAX(elaps, elaps_max);
296     }
297     else {
298         perf_last_time = lv_tick_get();
299         uint32_t fps = 1000 / (elaps_max == 0 ? 1 : elaps_max);
300         elaps_max = 1;
301         uint32_t fps_limit = 1000 / disp_refr->refr_task->period;
302         if(fps > fps_limit) fps = fps_limit;
303 
304         uint32_t cpu = 100 - lv_task_get_idle();
305         lv_label_set_text_fmt(perf_label, "%d FPS\n%d%% CPU", fps, cpu);
306         lv_obj_align(perf_label, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0);
307     }
308 #endif
309 
310     LV_LOG_TRACE("lv_refr_task: ready");
311 }
312 
313 /**********************
314  *   STATIC FUNCTIONS
315  **********************/
316 
317 /**
318  * Join the areas which has got common parts
319  */
lv_refr_join_area(void)320 static void lv_refr_join_area(void)
321 {
322     uint32_t join_from;
323     uint32_t join_in;
324     lv_area_t joined_area;
325     for(join_in = 0; join_in < disp_refr->inv_p; join_in++) {
326         if(disp_refr->inv_area_joined[join_in] != 0) continue;
327 
328         /*Check all areas to join them in 'join_in'*/
329         for(join_from = 0; join_from < disp_refr->inv_p; join_from++) {
330             /*Handle only unjoined areas and ignore itself*/
331             if(disp_refr->inv_area_joined[join_from] != 0 || join_in == join_from) {
332                 continue;
333             }
334 
335             /*Check if the areas are on each other*/
336             if(_lv_area_is_on(&disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]) == false) {
337                 continue;
338             }
339 
340             _lv_area_join(&joined_area, &disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]);
341 
342             /*Join two area only if the joined area size is smaller*/
343             if(lv_area_get_size(&joined_area) < (lv_area_get_size(&disp_refr->inv_areas[join_in]) +
344                                                  lv_area_get_size(&disp_refr->inv_areas[join_from]))) {
345                 lv_area_copy(&disp_refr->inv_areas[join_in], &joined_area);
346 
347                 /*Mark 'join_form' is joined into 'join_in'*/
348                 disp_refr->inv_area_joined[join_from] = 1;
349             }
350         }
351     }
352 }
353 
354 /**
355  * Refresh the joined areas
356  */
lv_refr_areas(void)357 static void lv_refr_areas(void)
358 {
359     px_num = 0;
360 
361     if(disp_refr->inv_p == 0) return;
362 
363     /*Find the last area which will be drawn*/
364     int32_t i;
365     int32_t last_i = 0;
366     for(i = disp_refr->inv_p - 1; i >= 0; i--) {
367         if(disp_refr->inv_area_joined[i] == 0) {
368             last_i = i;
369             break;
370         }
371     }
372 
373     disp_refr->driver.buffer->last_area = 0;
374     disp_refr->driver.buffer->last_part = 0;
375 
376     for(i = 0; i < disp_refr->inv_p; i++) {
377         /*Refresh the unjoined areas*/
378         if(disp_refr->inv_area_joined[i] == 0) {
379 
380             if(i == last_i) disp_refr->driver.buffer->last_area = 1;
381             disp_refr->driver.buffer->last_part = 0;
382             lv_refr_area(&disp_refr->inv_areas[i]);
383 
384             if(disp_refr->driver.monitor_cb) px_num += lv_area_get_size(&disp_refr->inv_areas[i]);
385         }
386     }
387 }
388 
389 /**
390  * Refresh an area if there is Virtual Display Buffer
391  * @param area_p  pointer to an area to refresh
392  */
lv_refr_area(const lv_area_t * area_p)393 static void lv_refr_area(const lv_area_t * area_p)
394 {
395     /*True double buffering: there are two screen sized buffers. Just redraw directly into a
396      * buffer*/
397     if(lv_disp_is_true_double_buf(disp_refr)) {
398         lv_disp_buf_t * vdb = lv_disp_get_buf(disp_refr);
399         vdb->area.x1        = 0;
400         vdb->area.x2        = lv_disp_get_hor_res(disp_refr) - 1;
401         vdb->area.y1        = 0;
402         vdb->area.y2        = lv_disp_get_ver_res(disp_refr) - 1;
403         disp_refr->driver.buffer->last_part = 1;
404         lv_refr_area_part(area_p);
405     }
406     /*The buffer is smaller: refresh the area in parts*/
407     else {
408         lv_disp_buf_t * vdb = lv_disp_get_buf(disp_refr);
409         /*Calculate the max row num*/
410         lv_coord_t w = lv_area_get_width(area_p);
411         lv_coord_t h = lv_area_get_height(area_p);
412         lv_coord_t y2 =
413             area_p->y2 >= lv_disp_get_ver_res(disp_refr) ? lv_disp_get_ver_res(disp_refr) - 1 : area_p->y2;
414 
415         int32_t max_row = (uint32_t)vdb->size / w;
416 
417         if(max_row > h) max_row = h;
418 
419         /*Round down the lines of VDB if rounding is added*/
420         if(disp_refr->driver.rounder_cb) {
421             lv_area_t tmp;
422             tmp.x1 = 0;
423             tmp.x2 = 0;
424             tmp.y1 = 0;
425 
426             lv_coord_t h_tmp = max_row;
427             do {
428                 tmp.y2 = h_tmp - 1;
429                 disp_refr->driver.rounder_cb(&disp_refr->driver, &tmp);
430 
431                 /*If this height fits into `max_row` then fine*/
432                 if(lv_area_get_height(&tmp) <= max_row) break;
433 
434                 /*Decrement the height of the area until it fits into `max_row` after rounding*/
435                 h_tmp--;
436             } while(h_tmp > 0);
437 
438             if(h_tmp <= 0) {
439                 LV_LOG_WARN("Can't set VDB height using the round function. (Wrong round_cb or to "
440                             "small VDB)");
441                 return;
442             }
443             else {
444                 max_row = tmp.y2 + 1;
445             }
446         }
447 
448         /*Always use the full row*/
449         lv_coord_t row;
450         lv_coord_t row_last = 0;
451         for(row = area_p->y1; row + max_row - 1 <= y2; row += max_row) {
452             /*Calc. the next y coordinates of VDB*/
453             vdb->area.x1 = area_p->x1;
454             vdb->area.x2 = area_p->x2;
455             vdb->area.y1 = row;
456             vdb->area.y2 = row + max_row - 1;
457             if(vdb->area.y2 > y2) vdb->area.y2 = y2;
458             row_last = vdb->area.y2;
459             if(y2 == row_last) disp_refr->driver.buffer->last_part = 1;
460             lv_refr_area_part(area_p);
461         }
462 
463         /*If the last y coordinates are not handled yet ...*/
464         if(y2 != row_last) {
465             /*Calc. the next y coordinates of VDB*/
466             vdb->area.x1 = area_p->x1;
467             vdb->area.x2 = area_p->x2;
468             vdb->area.y1 = row;
469             vdb->area.y2 = y2;
470 
471             disp_refr->driver.buffer->last_part = 1;
472             lv_refr_area_part(area_p);
473         }
474     }
475 }
476 
477 /**
478  * Refresh a part of an area which is on the actual Virtual Display Buffer
479  * @param area_p pointer to an area to refresh
480  */
lv_refr_area_part(const lv_area_t * area_p)481 static void lv_refr_area_part(const lv_area_t * area_p)
482 {
483     lv_disp_buf_t * vdb = lv_disp_get_buf(disp_refr);
484 
485     /*In non double buffered mode, before rendering the next part wait until the previous image is
486      * flushed*/
487     if(lv_disp_is_double_buf(disp_refr) == false) {
488         while(vdb->flushing) {
489             if(disp_refr->driver.wait_cb) disp_refr->driver.wait_cb(&disp_refr->driver);
490         }
491     }
492 
493     lv_obj_t * top_act_scr = NULL;
494     lv_obj_t * top_prev_scr = NULL;
495 
496     /*Get the new mask from the original area and the act. VDB
497      It will be a part of 'area_p'*/
498     lv_area_t start_mask;
499     _lv_area_intersect(&start_mask, area_p, &vdb->area);
500 
501     /*Get the most top object which is not covered by others*/
502     top_act_scr = lv_refr_get_top_obj(&start_mask, lv_disp_get_scr_act(disp_refr));
503     if(disp_refr->prev_scr) {
504         top_prev_scr = lv_refr_get_top_obj(&start_mask, disp_refr->prev_scr);
505     }
506 
507     /*Draw a display background if there is no top object*/
508     if(top_act_scr == NULL && top_prev_scr == NULL) {
509         if(disp_refr->bg_img) {
510             lv_draw_img_dsc_t dsc;
511             lv_draw_img_dsc_init(&dsc);
512             dsc.opa = disp_refr->bg_opa;
513             lv_img_header_t header;
514             lv_res_t res;
515             res = lv_img_decoder_get_info(disp_refr->bg_img, &header);
516             if(res == LV_RES_OK) {
517                 lv_area_t a;
518                 lv_area_set(&a, 0, 0, header.w - 1, header.h - 1);
519                 lv_draw_img(&a, &start_mask, disp_refr->bg_img, &dsc);
520             }
521             else {
522                 LV_LOG_WARN("Can't draw the background image")
523             }
524         }
525         else {
526             lv_draw_rect_dsc_t dsc;
527             lv_draw_rect_dsc_init(&dsc);
528             dsc.bg_color = disp_refr->bg_color;
529             dsc.bg_opa = disp_refr->bg_opa;
530             lv_draw_rect(&start_mask, &start_mask, &dsc);
531 
532         }
533     }
534     /*Refresh the previous screen if any*/
535     if(disp_refr->prev_scr) {
536         /*Get the most top object which is not covered by others*/
537         if(top_prev_scr == NULL) {
538             top_prev_scr = disp_refr->prev_scr;
539         }
540         /*Do the refreshing from the top object*/
541         lv_refr_obj_and_children(top_prev_scr, &start_mask);
542 
543     }
544 
545 
546     if(top_act_scr == NULL) {
547         top_act_scr = disp_refr->act_scr;
548     }
549     /*Do the refreshing from the top object*/
550     lv_refr_obj_and_children(top_act_scr, &start_mask);
551 
552     /*Also refresh top and sys layer unconditionally*/
553     lv_refr_obj_and_children(lv_disp_get_layer_top(disp_refr), &start_mask);
554     lv_refr_obj_and_children(lv_disp_get_layer_sys(disp_refr), &start_mask);
555 
556     /* In true double buffered mode flush only once when all areas were rendered.
557      * In normal mode flush after every area */
558     if(lv_disp_is_true_double_buf(disp_refr) == false) {
559         lv_refr_vdb_flush();
560     }
561 }
562 
563 /**
564  * Search the most top object which fully covers an area
565  * @param area_p pointer to an area
566  * @param obj the first object to start the searching (typically a screen)
567  * @return
568  */
lv_refr_get_top_obj(const lv_area_t * area_p,lv_obj_t * obj)569 static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj)
570 {
571     lv_obj_t * found_p = NULL;
572 
573     /*If this object is fully cover the draw area check the children too */
574     if(_lv_area_is_in(area_p, &obj->coords, 0) && obj->hidden == 0) {
575         lv_design_res_t design_res = obj->design_cb(obj, area_p, LV_DESIGN_COVER_CHK);
576         if(design_res == LV_DESIGN_RES_MASKED) return NULL;
577 
578 #if LV_USE_OPA_SCALE
579         if(design_res == LV_DESIGN_RES_COVER && lv_obj_get_style_opa_scale(obj, LV_OBJ_PART_MAIN) != LV_OPA_COVER) {
580             design_res = LV_DESIGN_RES_NOT_COVER;
581         }
582 #endif
583 
584         lv_obj_t * i;
585         _LV_LL_READ(obj->child_ll, i) {
586             found_p = lv_refr_get_top_obj(area_p, i);
587 
588             /*If a children is ok then break*/
589             if(found_p != NULL) {
590                 break;
591             }
592         }
593 
594         /*If no better children use this object*/
595         if(found_p == NULL) {
596             if(design_res == LV_DESIGN_RES_COVER) {
597                 found_p = obj;
598             }
599         }
600     }
601 
602     return found_p;
603 }
604 
605 /**
606  * Make the refreshing from an object. Draw all its children and the youngers too.
607  * @param top_p pointer to an objects. Start the drawing from it.
608  * @param mask_p pointer to an area, the objects will be drawn only here
609  */
lv_refr_obj_and_children(lv_obj_t * top_p,const lv_area_t * mask_p)610 static void lv_refr_obj_and_children(lv_obj_t * top_p, const lv_area_t * mask_p)
611 {
612     /* Normally always will be a top_obj (at least the screen)
613      * but in special cases (e.g. if the screen has alpha) it won't.
614      * In this case use the screen directly */
615     if(top_p == NULL) top_p = lv_disp_get_scr_act(disp_refr);
616     if(top_p == NULL) return;  /*Shouldn't happen*/
617 
618     /*Refresh the top object and its children*/
619     lv_refr_obj(top_p, mask_p);
620 
621     /*Draw the 'younger' sibling objects because they can be on top_obj */
622     lv_obj_t * par;
623     lv_obj_t * border_p = top_p;
624 
625     par = lv_obj_get_parent(top_p);
626 
627     /*Do until not reach the screen*/
628     while(par != NULL) {
629         /*object before border_p has to be redrawn*/
630         lv_obj_t * i = _lv_ll_get_prev(&(par->child_ll), border_p);
631 
632         while(i != NULL) {
633             /*Refresh the objects*/
634             lv_refr_obj(i, mask_p);
635             i = _lv_ll_get_prev(&(par->child_ll), i);
636         }
637 
638         /*Call the post draw design function of the parents of the to object*/
639         if(par->design_cb) par->design_cb(par, mask_p, LV_DESIGN_DRAW_POST);
640 
641         /*The new border will be there last parents,
642          *so the 'younger' brothers of parent will be refreshed*/
643         border_p = par;
644         /*Go a level deeper*/
645         par = lv_obj_get_parent(par);
646     }
647 }
648 
649 /**
650  * Refresh an object an all of its children. (Called recursively)
651  * @param obj pointer to an object to refresh
652  * @param mask_ori_p pointer to an area, the objects will be drawn only here
653  */
lv_refr_obj(lv_obj_t * obj,const lv_area_t * mask_ori_p)654 static void lv_refr_obj(lv_obj_t * obj, const lv_area_t * mask_ori_p)
655 {
656     /*Do not refresh hidden objects*/
657     if(obj->hidden != 0) return;
658 
659     bool union_ok; /* Store the return value of area_union */
660     /* Truncate the original mask to the coordinates of the parent
661      * because the parent and its children are visible only here */
662     lv_area_t obj_mask;
663     lv_area_t obj_ext_mask;
664     lv_area_t obj_area;
665     lv_coord_t ext_size = obj->ext_draw_pad;
666     lv_obj_get_coords(obj, &obj_area);
667     obj_area.x1 -= ext_size;
668     obj_area.y1 -= ext_size;
669     obj_area.x2 += ext_size;
670     obj_area.y2 += ext_size;
671     union_ok = _lv_area_intersect(&obj_ext_mask, mask_ori_p, &obj_area);
672 
673     /*Draw the parent and its children only if they ore on 'mask_parent'*/
674     if(union_ok != false) {
675 
676         /* Redraw the object */
677         if(obj->design_cb) obj->design_cb(obj, &obj_ext_mask, LV_DESIGN_DRAW_MAIN);
678 
679 #if MASK_AREA_DEBUG
680         static lv_color_t debug_color = LV_COLOR_RED;
681         lv_draw_rect_dsc_t draw_dsc;
682         lv_draw_rect_dsc_init(&draw_dsc);
683         draw_dsc.bg_color.full = debug_color.full;
684         draw_dsc.bg_opa = LV_OPA_20;
685         draw_dsc.border_width = 2;
686         draw_dsc.border_opa = LV_OPA_50;
687         draw_dsc.border_color.full = (debug_color.full + 0x13) * 9;
688 
689         lv_draw_rect(&obj_ext_mask, &obj_ext_mask, &draw_dsc);
690         debug_color.full *= 17;
691         debug_color.full += 0xA1;
692 #if LV_COLOR_DEPTH == 32
693         debug_color.ch.alpha = 0xff;
694 #endif
695 #endif
696         /*Create a new 'obj_mask' without 'ext_size' because the children can't be visible there*/
697         lv_obj_get_coords(obj, &obj_area);
698         union_ok = _lv_area_intersect(&obj_mask, mask_ori_p, &obj_area);
699         if(union_ok != false) {
700             lv_area_t mask_child; /*Mask from obj and its child*/
701             lv_obj_t * child_p;
702             lv_area_t child_area;
703             _LV_LL_READ_BACK(obj->child_ll, child_p) {
704                 lv_obj_get_coords(child_p, &child_area);
705                 ext_size = child_p->ext_draw_pad;
706                 child_area.x1 -= ext_size;
707                 child_area.y1 -= ext_size;
708                 child_area.x2 += ext_size;
709                 child_area.y2 += ext_size;
710                 /* Get the union (common parts) of original mask (from obj)
711                  * and its child */
712                 union_ok = _lv_area_intersect(&mask_child, &obj_mask, &child_area);
713 
714                 /*If the parent and the child has common area then refresh the child */
715                 if(union_ok) {
716                     /*Refresh the next children*/
717                     lv_refr_obj(child_p, &mask_child);
718                 }
719             }
720         }
721 
722         /* If all the children are redrawn make 'post draw' design */
723         if(obj->design_cb) obj->design_cb(obj, &obj_ext_mask, LV_DESIGN_DRAW_POST);
724     }
725 }
726 
727 /**
728  * Flush the content of the VDB
729  */
lv_refr_vdb_flush(void)730 static void lv_refr_vdb_flush(void)
731 {
732     lv_disp_buf_t * vdb = lv_disp_get_buf(disp_refr);
733 
734     /*In double buffered mode wait until the other buffer is flushed before flushing the current
735      * one*/
736     if(lv_disp_is_double_buf(disp_refr)) {
737         while(vdb->flushing) {
738             if(disp_refr->driver.wait_cb) disp_refr->driver.wait_cb(&disp_refr->driver);
739         }
740     }
741 
742     vdb->flushing = 1;
743 
744     if(disp_refr->driver.buffer->last_area && disp_refr->driver.buffer->last_part) vdb->flushing_last = 1;
745     else vdb->flushing_last = 0;
746 
747     /*Flush the rendered content to the display*/
748     lv_disp_t * disp = _lv_refr_get_disp_refreshing();
749     if(disp->driver.gpu_wait_cb) disp->driver.gpu_wait_cb(&disp->driver);
750 
751     if(disp->driver.flush_cb) disp->driver.flush_cb(&disp->driver, &vdb->area, vdb->buf_act);
752 
753     if(vdb->buf1 && vdb->buf2) {
754         if(vdb->buf_act == vdb->buf1)
755             vdb->buf_act = vdb->buf2;
756         else
757             vdb->buf_act = vdb->buf1;
758     }
759 }
760