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 "../hal/lv_hal_tick.h"
13 #include "../hal/lv_hal_disp.h"
14 #include "../misc/lv_timer.h"
15 #include "../misc/lv_mem.h"
16 #include "../misc/lv_math.h"
17 #include "../misc/lv_gc.h"
18 #include "../draw/lv_draw.h"
19 #include "../font/lv_font_fmt_txt.h"
20 
21 #if LV_USE_PERF_MONITOR || LV_USE_MEM_MONITOR
22     #include "../widgets/lv_label.h"
23 #endif
24 
25 /*********************
26  *      DEFINES
27  *********************/
28 
29 /**********************
30  *      TYPEDEFS
31  **********************/
32 typedef struct {
33     uint32_t    perf_last_time;
34     uint32_t    elaps_sum;
35     uint32_t    frame_cnt;
36     uint32_t    fps_sum_cnt;
37     uint32_t    fps_sum_all;
38 #if LV_USE_LABEL
39     lv_obj_t  * perf_label;
40 #endif
41 } perf_monitor_t;
42 
43 typedef struct {
44     uint32_t     mem_last_time;
45 #if LV_USE_LABEL
46     lv_obj_t  *  mem_label;
47 #endif
48 } mem_monitor_t;
49 
50 /**********************
51  *  STATIC PROTOTYPES
52  **********************/
53 static void lv_refr_join_area(void);
54 static void lv_refr_areas(void);
55 static void lv_refr_area(const lv_area_t * area_p);
56 static void lv_refr_area_part(lv_draw_ctx_t * draw_ctx);
57 static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj);
58 static void lv_refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_obj);
59 static uint32_t get_max_row(lv_disp_t * disp, lv_coord_t area_w, lv_coord_t area_h);
60 static void draw_buf_flush(lv_disp_t * disp);
61 static void call_flush_cb(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p);
62 
63 #if LV_USE_PERF_MONITOR
64     static void perf_monitor_init(perf_monitor_t * perf_monitor);
65 #endif
66 #if LV_USE_MEM_MONITOR
67     static void mem_monitor_init(mem_monitor_t * mem_monitor);
68 #endif
69 
70 /**********************
71  *  STATIC VARIABLES
72  **********************/
73 static uint32_t px_num;
74 static lv_disp_t * disp_refr; /*Display being refreshed*/
75 
76 #if LV_USE_PERF_MONITOR
77     static perf_monitor_t   perf_monitor;
78 #endif
79 
80 #if LV_USE_MEM_MONITOR
81     static mem_monitor_t    mem_monitor;
82 #endif
83 
84 /**********************
85  *      MACROS
86  **********************/
87 #if LV_LOG_TRACE_DISP_REFR
88     #define REFR_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
89 #else
90     #define REFR_TRACE(...)
91 #endif
92 
93 /**********************
94  *   GLOBAL FUNCTIONS
95  **********************/
96 
97 /**
98  * Initialize the screen refresh subsystem
99  */
_lv_refr_init(void)100 void _lv_refr_init(void)
101 {
102 #if LV_USE_PERF_MONITOR
103     perf_monitor_init(&perf_monitor);
104 #endif
105 #if LV_USE_MEM_MONITOR
106     mem_monitor_init(&mem_monitor);
107 #endif
108 }
109 
lv_refr_now(lv_disp_t * disp)110 void lv_refr_now(lv_disp_t * disp)
111 {
112     lv_anim_refr_now();
113 
114     if(disp) {
115         if(disp->refr_timer) _lv_disp_refr_timer(disp->refr_timer);
116     }
117     else {
118         lv_disp_t * d;
119         d = lv_disp_get_next(NULL);
120         while(d) {
121             if(d->refr_timer) _lv_disp_refr_timer(d->refr_timer);
122             d = lv_disp_get_next(d);
123         }
124     }
125 }
126 
lv_refr_obj(lv_draw_ctx_t * draw_ctx,lv_obj_t * obj)127 void lv_refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj)
128 {
129     /*Do not refresh hidden objects*/
130     if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return;
131 
132     const lv_area_t * clip_area_ori = draw_ctx->clip_area;
133     lv_area_t clip_coords_for_obj;
134 
135     /*Truncate the clip area to `obj size + ext size` area*/
136     lv_area_t obj_coords_ext;
137     lv_obj_get_coords(obj, &obj_coords_ext);
138     lv_coord_t ext_draw_size = _lv_obj_get_ext_draw_size(obj);
139     lv_area_increase(&obj_coords_ext, ext_draw_size, ext_draw_size);
140     if(!_lv_area_intersect(&clip_coords_for_obj, clip_area_ori, &obj_coords_ext)) return;
141 
142     draw_ctx->clip_area = &clip_coords_for_obj;
143 
144     /*Draw the object*/
145     lv_event_send(obj, LV_EVENT_DRAW_MAIN_BEGIN, draw_ctx);
146     lv_event_send(obj, LV_EVENT_DRAW_MAIN, draw_ctx);
147     lv_event_send(obj, LV_EVENT_DRAW_MAIN_END, draw_ctx);
148 
149 #if LV_USE_REFR_DEBUG
150     lv_color_t debug_color = lv_color_make(lv_rand(0, 0xFF), lv_rand(0, 0xFF), lv_rand(0, 0xFF));
151     lv_draw_rect_dsc_t draw_dsc;
152     lv_draw_rect_dsc_init(&draw_dsc);
153     draw_dsc.bg_color.full = debug_color.full;
154     draw_dsc.bg_opa = LV_OPA_20;
155     draw_dsc.border_width = 1;
156     draw_dsc.border_opa = LV_OPA_30;
157     draw_dsc.border_color = debug_color;
158     lv_draw_rect(draw_ctx, &draw_dsc, &obj_coords_ext);
159 #endif
160 
161     /*With overflow visible keep the previous clip area to let the children visible out of this object too
162      *With not overflow visible limit the clip are to the object's coordinates to clip the children*/
163     bool refr_children = true;
164     lv_area_t clip_coords_for_children;
165     if(lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) {
166         clip_coords_for_children  = *clip_area_ori;
167     }
168     else {
169         if(!_lv_area_intersect(&clip_coords_for_children, clip_area_ori, &obj->coords)) {
170             refr_children = false;
171         }
172     }
173 
174     if(refr_children) {
175         draw_ctx->clip_area = &clip_coords_for_children;
176         uint32_t i;
177         uint32_t child_cnt = lv_obj_get_child_cnt(obj);
178         for(i = 0; i < child_cnt; i++) {
179             lv_obj_t * child = obj->spec_attr->children[i];
180             lv_refr_obj(draw_ctx, child);
181         }
182     }
183 
184     draw_ctx->clip_area = &clip_coords_for_obj;
185 
186     /*If all the children are redrawn make 'post draw' draw*/
187     lv_event_send(obj, LV_EVENT_DRAW_POST_BEGIN, draw_ctx);
188     lv_event_send(obj, LV_EVENT_DRAW_POST, draw_ctx);
189     lv_event_send(obj, LV_EVENT_DRAW_POST_END, draw_ctx);
190 
191     draw_ctx->clip_area = clip_area_ori;
192 }
193 
194 /**
195  * Invalidate an area on display to redraw it
196  * @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
197  * @param disp pointer to display where the area should be invalidated (NULL can be used if there is
198  * only one display)
199  */
_lv_inv_area(lv_disp_t * disp,const lv_area_t * area_p)200 void _lv_inv_area(lv_disp_t * disp, const lv_area_t * area_p)
201 {
202     if(!disp) disp = lv_disp_get_default();
203     if(!disp) return;
204 
205     /*Clear the invalidate buffer if the parameter is NULL*/
206     if(area_p == NULL) {
207         disp->inv_p = 0;
208         return;
209     }
210 
211     lv_area_t scr_area;
212     scr_area.x1 = 0;
213     scr_area.y1 = 0;
214     scr_area.x2 = lv_disp_get_hor_res(disp) - 1;
215     scr_area.y2 = lv_disp_get_ver_res(disp) - 1;
216 
217     lv_area_t com_area;
218     bool suc;
219 
220     suc = _lv_area_intersect(&com_area, area_p, &scr_area);
221     if(suc == false)  return; /*Out of the screen*/
222 
223     /*If there were at least 1 invalid area in full refresh mode, redraw the whole screen*/
224     if(disp->driver->full_refresh) {
225         disp->inv_areas[0] = scr_area;
226         disp->inv_p = 1;
227         if(disp->refr_timer) lv_timer_resume(disp->refr_timer);
228         return;
229     }
230 
231     if(disp->driver->rounder_cb) disp->driver->rounder_cb(disp->driver, &com_area);
232 
233     /*Save only if this area is not in one of the saved areas*/
234     uint16_t i;
235     for(i = 0; i < disp->inv_p; i++) {
236         if(_lv_area_is_in(&com_area, &disp->inv_areas[i], 0) != false) return;
237     }
238 
239     /*Save the area*/
240     if(disp->inv_p < LV_INV_BUF_SIZE) {
241         lv_area_copy(&disp->inv_areas[disp->inv_p], &com_area);
242     }
243     else {   /*If no place for the area add the screen*/
244         disp->inv_p = 0;
245         lv_area_copy(&disp->inv_areas[disp->inv_p], &scr_area);
246     }
247     disp->inv_p++;
248     if(disp->refr_timer) lv_timer_resume(disp->refr_timer);
249 }
250 
251 /**
252  * Get the display which is being refreshed
253  * @return the display being refreshed
254  */
_lv_refr_get_disp_refreshing(void)255 lv_disp_t * _lv_refr_get_disp_refreshing(void)
256 {
257     return disp_refr;
258 }
259 
260 /**
261  * Set the display which is being refreshed.
262  * It shouldn't be used directly by the user.
263  * It can be used to trick the drawing functions about there is an active display.
264  * @param the display being refreshed
265  */
_lv_refr_set_disp_refreshing(lv_disp_t * disp)266 void _lv_refr_set_disp_refreshing(lv_disp_t * disp)
267 {
268     disp_refr = disp;
269 }
270 
271 /**
272  * Called periodically to handle the refreshing
273  * @param tmr pointer to the timer itself
274  */
_lv_disp_refr_timer(lv_timer_t * tmr)275 void _lv_disp_refr_timer(lv_timer_t * tmr)
276 {
277     REFR_TRACE("begin");
278 
279     uint32_t start = lv_tick_get();
280     volatile uint32_t elaps = 0;
281 
282     if(tmr) {
283         disp_refr = tmr->user_data;
284 #if LV_USE_PERF_MONITOR == 0 && LV_USE_MEM_MONITOR == 0
285         /**
286          * Ensure the timer does not run again automatically.
287          * This is done before refreshing in case refreshing invalidates something else.
288          */
289         lv_timer_pause(tmr);
290 #endif
291     }
292     else {
293         disp_refr = lv_disp_get_default();
294     }
295 
296     /*Refresh the screen's layout if required*/
297     lv_obj_update_layout(disp_refr->act_scr);
298     if(disp_refr->prev_scr) lv_obj_update_layout(disp_refr->prev_scr);
299 
300     lv_obj_update_layout(disp_refr->top_layer);
301     lv_obj_update_layout(disp_refr->sys_layer);
302 
303     /*Do nothing if there is no active screen*/
304     if(disp_refr->act_scr == NULL) {
305         disp_refr->inv_p = 0;
306         LV_LOG_WARN("there is no active screen");
307         REFR_TRACE("finished");
308         return;
309     }
310 
311     lv_refr_join_area();
312 
313     lv_refr_areas();
314 
315     /*If refresh happened ...*/
316     if(disp_refr->inv_p != 0) {
317         if(disp_refr->driver->full_refresh) {
318             lv_area_t disp_area;
319             lv_area_set(&disp_area, 0, 0, lv_disp_get_hor_res(disp_refr) - 1, lv_disp_get_ver_res(disp_refr) - 1);
320             disp_refr->driver->draw_ctx->buf_area = &disp_area;
321             draw_buf_flush(disp_refr);
322         }
323 
324         /*Clean up*/
325         lv_memset_00(disp_refr->inv_areas, sizeof(disp_refr->inv_areas));
326         lv_memset_00(disp_refr->inv_area_joined, sizeof(disp_refr->inv_area_joined));
327         disp_refr->inv_p = 0;
328 
329         elaps = lv_tick_elaps(start);
330         /*Call monitor cb if present*/
331         if(disp_refr->driver->monitor_cb) {
332             disp_refr->driver->monitor_cb(disp_refr->driver, elaps, px_num);
333         }
334     }
335 
336     lv_mem_buf_free_all();
337     _lv_font_clean_up_fmt_txt();
338 
339 #if LV_DRAW_COMPLEX
340     _lv_draw_mask_cleanup();
341 #endif
342 
343 #if LV_USE_PERF_MONITOR && LV_USE_LABEL
344     lv_obj_t * perf_label = perf_monitor.perf_label;
345     if(perf_label == NULL) {
346         perf_label = lv_label_create(lv_layer_sys());
347         lv_obj_set_style_bg_opa(perf_label, LV_OPA_50, 0);
348         lv_obj_set_style_bg_color(perf_label, lv_color_black(), 0);
349         lv_obj_set_style_text_color(perf_label, lv_color_white(), 0);
350         lv_obj_set_style_pad_top(perf_label, 3, 0);
351         lv_obj_set_style_pad_bottom(perf_label, 3, 0);
352         lv_obj_set_style_pad_left(perf_label, 3, 0);
353         lv_obj_set_style_pad_right(perf_label, 3, 0);
354         lv_obj_set_style_text_align(perf_label, LV_TEXT_ALIGN_RIGHT, 0);
355         lv_label_set_text(perf_label, "?");
356         lv_obj_align(perf_label, LV_USE_PERF_MONITOR_POS, 0, 0);
357         perf_monitor.perf_label = perf_label;
358     }
359 
360     if(lv_tick_elaps(perf_monitor.perf_last_time) < 300) {
361         if(px_num > 5000) {
362             perf_monitor.elaps_sum += elaps;
363             perf_monitor.frame_cnt ++;
364         }
365     }
366     else {
367         perf_monitor.perf_last_time = lv_tick_get();
368         uint32_t fps_limit = 1000 / disp_refr->refr_timer->period;
369         uint32_t fps;
370 
371         if(perf_monitor.elaps_sum == 0) {
372             perf_monitor.elaps_sum = 1;
373         }
374         if(perf_monitor.frame_cnt == 0) {
375             fps = fps_limit;
376         }
377         else {
378             fps = (1000 * perf_monitor.frame_cnt) / perf_monitor.elaps_sum;
379         }
380         perf_monitor.elaps_sum = 0;
381         perf_monitor.frame_cnt = 0;
382         if(fps > fps_limit) {
383             fps = fps_limit;
384         }
385 
386         perf_monitor.fps_sum_all += fps;
387         perf_monitor.fps_sum_cnt ++;
388         uint32_t cpu = 100 - lv_timer_get_idle();
389         lv_label_set_text_fmt(perf_label, "%"LV_PRIu32" FPS\n%"LV_PRIu32"%% CPU", fps, cpu);
390     }
391 #endif
392 
393 #if LV_USE_MEM_MONITOR && LV_MEM_CUSTOM == 0 && LV_USE_LABEL
394     lv_obj_t * mem_label = mem_monitor.mem_label;
395     if(mem_label == NULL) {
396         mem_label = lv_label_create(lv_layer_sys());
397         lv_obj_set_style_bg_opa(mem_label, LV_OPA_50, 0);
398         lv_obj_set_style_bg_color(mem_label, lv_color_black(), 0);
399         lv_obj_set_style_text_color(mem_label, lv_color_white(), 0);
400         lv_obj_set_style_pad_top(mem_label, 3, 0);
401         lv_obj_set_style_pad_bottom(mem_label, 3, 0);
402         lv_obj_set_style_pad_left(mem_label, 3, 0);
403         lv_obj_set_style_pad_right(mem_label, 3, 0);
404         lv_label_set_text(mem_label, "?");
405         lv_obj_align(mem_label, LV_USE_MEM_MONITOR_POS, 0, 0);
406         mem_monitor.mem_label = mem_label;
407     }
408 
409     if(lv_tick_elaps(mem_monitor.mem_last_time) > 300) {
410         mem_monitor.mem_last_time = lv_tick_get();
411         lv_mem_monitor_t mon;
412         lv_mem_monitor(&mon);
413         uint32_t used_size = mon.total_size - mon.free_size;;
414         uint32_t used_kb = used_size / 1024;
415         uint32_t used_kb_tenth = (used_size - (used_kb * 1024)) / 102;
416         lv_label_set_text_fmt(mem_label, "%" LV_PRIu32 ".%" LV_PRIu32 " kB used (%d %%)\n" \
417                               "%d%% frag.", used_kb, used_kb_tenth, mon.used_pct,
418                               mon.frag_pct);
419     }
420 #endif
421 
422     REFR_TRACE("finished");
423 }
424 
425 #if LV_USE_PERF_MONITOR
lv_refr_reset_fps_counter(void)426 void lv_refr_reset_fps_counter(void)
427 {
428     perf_monitor.fps_sum_all = 0;
429     perf_monitor.fps_sum_cnt = 0;
430 }
431 
lv_refr_get_fps_avg(void)432 uint32_t lv_refr_get_fps_avg(void)
433 {
434     if(perf_monitor.fps_sum_cnt == 0) {
435         return 0;
436     }
437     return perf_monitor.fps_sum_all / perf_monitor.fps_sum_cnt;
438 }
439 #endif
440 
441 
442 /**********************
443  *   STATIC FUNCTIONS
444  **********************/
445 
446 /**
447  * Join the areas which has got common parts
448  */
lv_refr_join_area(void)449 static void lv_refr_join_area(void)
450 {
451     uint32_t join_from;
452     uint32_t join_in;
453     lv_area_t joined_area;
454     for(join_in = 0; join_in < disp_refr->inv_p; join_in++) {
455         if(disp_refr->inv_area_joined[join_in] != 0) continue;
456 
457         /*Check all areas to join them in 'join_in'*/
458         for(join_from = 0; join_from < disp_refr->inv_p; join_from++) {
459             /*Handle only unjoined areas and ignore itself*/
460             if(disp_refr->inv_area_joined[join_from] != 0 || join_in == join_from) {
461                 continue;
462             }
463 
464             /*Check if the areas are on each other*/
465             if(_lv_area_is_on(&disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]) == false) {
466                 continue;
467             }
468 
469             _lv_area_join(&joined_area, &disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]);
470 
471             /*Join two area only if the joined area size is smaller*/
472             if(lv_area_get_size(&joined_area) < (lv_area_get_size(&disp_refr->inv_areas[join_in]) +
473                                                  lv_area_get_size(&disp_refr->inv_areas[join_from]))) {
474                 lv_area_copy(&disp_refr->inv_areas[join_in], &joined_area);
475 
476                 /*Mark 'join_form' is joined into 'join_in'*/
477                 disp_refr->inv_area_joined[join_from] = 1;
478             }
479         }
480     }
481 }
482 
483 /**
484  * Refresh the joined areas
485  */
lv_refr_areas(void)486 static void lv_refr_areas(void)
487 {
488     px_num = 0;
489 
490     if(disp_refr->inv_p == 0) return;
491 
492     /*Find the last area which will be drawn*/
493     int32_t i;
494     int32_t last_i = 0;
495     for(i = disp_refr->inv_p - 1; i >= 0; i--) {
496         if(disp_refr->inv_area_joined[i] == 0) {
497             last_i = i;
498             break;
499         }
500     }
501 
502     disp_refr->driver->draw_buf->last_area = 0;
503     disp_refr->driver->draw_buf->last_part = 0;
504 
505     for(i = 0; i < disp_refr->inv_p; i++) {
506         /*Refresh the unjoined areas*/
507         if(disp_refr->inv_area_joined[i] == 0) {
508 
509             if(i == last_i) disp_refr->driver->draw_buf->last_area = 1;
510             disp_refr->driver->draw_buf->last_part = 0;
511             lv_refr_area(&disp_refr->inv_areas[i]);
512 
513             px_num += lv_area_get_size(&disp_refr->inv_areas[i]);
514         }
515     }
516 }
517 
518 /**
519  * Refresh an area if there is Virtual Display Buffer
520  * @param area_p  pointer to an area to refresh
521  */
lv_refr_area(const lv_area_t * area_p)522 static void lv_refr_area(const lv_area_t * area_p)
523 {
524     lv_draw_ctx_t * draw_ctx = disp_refr->driver->draw_ctx;
525     draw_ctx->buf = disp_refr->driver->draw_buf->buf_act;
526 
527     /*With full refresh just redraw directly into the buffer*/
528     /*In direct mode draw directly on the absolute coordinates of the buffer*/
529     if(disp_refr->driver->full_refresh || disp_refr->driver->direct_mode) {
530         lv_area_t disp_area;
531         lv_area_set(&disp_area, 0, 0, lv_disp_get_hor_res(disp_refr) - 1, lv_disp_get_ver_res(disp_refr) - 1);
532         draw_ctx->buf_area = &disp_area;
533 
534         if(disp_refr->driver->full_refresh) {
535             disp_refr->driver->draw_buf->last_part = 1;
536             draw_ctx->clip_area = &disp_area;
537             lv_refr_area_part(draw_ctx);
538         }
539         else {
540             disp_refr->driver->draw_buf->last_part = disp_refr->driver->draw_buf->last_area;
541             draw_ctx->clip_area = area_p;
542             lv_refr_area_part(draw_ctx);
543         }
544         return;
545     }
546 
547     /*Normal refresh: draw the area in parts*/
548     /*Calculate the max row num*/
549     lv_coord_t w = lv_area_get_width(area_p);
550     lv_coord_t h = lv_area_get_height(area_p);
551     lv_coord_t y2 = area_p->y2 >= lv_disp_get_ver_res(disp_refr) ?
552                     lv_disp_get_ver_res(disp_refr) - 1 : area_p->y2;
553 
554     int32_t max_row = get_max_row(disp_refr, w, h);
555 
556     lv_coord_t row;
557     lv_coord_t row_last = 0;
558     lv_area_t sub_area;
559     for(row = area_p->y1; row + max_row - 1 <= y2; row += max_row) {
560         /*Calc. the next y coordinates of draw_buf*/
561         sub_area.x1 = area_p->x1;
562         sub_area.x2 = area_p->x2;
563         sub_area.y1 = row;
564         sub_area.y2 = row + max_row - 1;
565         draw_ctx->buf_area = &sub_area;
566         draw_ctx->clip_area = &sub_area;
567         draw_ctx->buf = disp_refr->driver->draw_buf->buf_act;
568         if(sub_area.y2 > y2) sub_area.y2 = y2;
569         row_last = sub_area.y2;
570         if(y2 == row_last) disp_refr->driver->draw_buf->last_part = 1;
571         lv_refr_area_part(draw_ctx);
572     }
573 
574     /*If the last y coordinates are not handled yet ...*/
575     if(y2 != row_last) {
576         /*Calc. the next y coordinates of draw_buf*/
577         sub_area.x1 = area_p->x1;
578         sub_area.x2 = area_p->x2;
579         sub_area.y1 = row;
580         sub_area.y2 = y2;
581         draw_ctx->buf_area = &sub_area;
582         draw_ctx->clip_area = &sub_area;
583         draw_ctx->buf = disp_refr->driver->draw_buf->buf_act;
584         disp_refr->driver->draw_buf->last_part = 1;
585         lv_refr_area_part(draw_ctx);
586     }
587 }
588 
lv_refr_area_part(lv_draw_ctx_t * draw_ctx)589 static void lv_refr_area_part(lv_draw_ctx_t * draw_ctx)
590 {
591     lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
592 
593     /* Below the `area_p` area will be redrawn into the draw buffer.
594      * In single buffered mode wait here until the buffer is freed.*/
595     if(draw_buf->buf1 && !draw_buf->buf2) {
596         while(draw_buf->flushing) {
597             if(disp_refr->driver->wait_cb) disp_refr->driver->wait_cb(disp_refr->driver);
598         }
599     }
600 
601     lv_obj_t * top_act_scr = NULL;
602     lv_obj_t * top_prev_scr = NULL;
603 
604     /*Get the most top object which is not covered by others*/
605     top_act_scr = lv_refr_get_top_obj(draw_ctx->buf_area, lv_disp_get_scr_act(disp_refr));
606     if(disp_refr->prev_scr) {
607         top_prev_scr = lv_refr_get_top_obj(draw_ctx->buf_area, disp_refr->prev_scr);
608     }
609 
610     /*Draw a display background if there is no top object*/
611     if(top_act_scr == NULL && top_prev_scr == NULL) {
612         if(draw_ctx->draw_bg) {
613             lv_draw_rect_dsc_t dsc;
614             lv_draw_rect_dsc_init(&dsc);
615             dsc.bg_img_src = disp_refr->bg_img;
616             dsc.bg_img_opa = disp_refr->bg_opa;
617             dsc.bg_color = disp_refr->bg_color;
618             dsc.bg_opa = disp_refr->bg_opa;
619             draw_ctx->draw_bg(draw_ctx, &dsc, draw_ctx->buf_area);
620         }
621         else if(disp_refr->bg_img) {
622             lv_img_header_t header;
623             lv_res_t res;
624             res = lv_img_decoder_get_info(disp_refr->bg_img, &header);
625             if(res == LV_RES_OK) {
626                 lv_area_t a;
627                 lv_area_set(&a, 0, 0, header.w - 1, header.h - 1);
628                 lv_draw_img_dsc_t dsc;
629                 lv_draw_img_dsc_init(&dsc);
630                 dsc.opa = disp_refr->bg_opa;
631                 lv_draw_img(draw_ctx, &dsc, &a, disp_refr->bg_img);
632             }
633             else {
634                 LV_LOG_WARN("Can't draw the background image");
635             }
636         }
637         else {
638             lv_draw_rect_dsc_t dsc;
639             lv_draw_rect_dsc_init(&dsc);
640             dsc.bg_color = disp_refr->bg_color;
641             dsc.bg_opa = disp_refr->bg_opa;
642             lv_draw_rect(draw_ctx, &dsc, draw_ctx->buf_area);
643         }
644     }
645     /*Refresh the previous screen if any*/
646     if(disp_refr->prev_scr) {
647         if(top_prev_scr == NULL) top_prev_scr = disp_refr->prev_scr;
648         lv_refr_obj_and_children(draw_ctx, top_prev_scr);
649     }
650 
651     if(top_act_scr == NULL) top_act_scr = disp_refr->act_scr;
652     lv_refr_obj_and_children(draw_ctx, top_act_scr);
653 
654     /*Also refresh top and sys layer unconditionally*/
655     lv_refr_obj_and_children(draw_ctx, lv_disp_get_layer_top(disp_refr));
656     lv_refr_obj_and_children(draw_ctx, lv_disp_get_layer_sys(disp_refr));
657 
658     /*In true double buffered mode flush only once when all areas were rendered.
659      *In normal mode flush after every area*/
660     if(disp_refr->driver->full_refresh == false) {
661         draw_buf_flush(disp_refr);
662     }
663 }
664 
665 /**
666  * Search the most top object which fully covers an area
667  * @param area_p pointer to an area
668  * @param obj the first object to start the searching (typically a screen)
669  * @return
670  */
lv_refr_get_top_obj(const lv_area_t * area_p,lv_obj_t * obj)671 static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj)
672 {
673     lv_obj_t * found_p = NULL;
674 
675     /*If this object is fully cover the draw area then check the children too*/
676     if(_lv_area_is_in(area_p, &obj->coords, 0) && lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN) == false) {
677         lv_cover_check_info_t info;
678         info.res = LV_COVER_RES_COVER;
679         info.area = area_p;
680         lv_event_send(obj, LV_EVENT_COVER_CHECK, &info);
681         if(info.res == LV_COVER_RES_MASKED) return NULL;
682 
683         uint32_t i;
684         uint32_t child_cnt = lv_obj_get_child_cnt(obj);
685         for(i = 0; i < child_cnt; i++) {
686             lv_obj_t * child = obj->spec_attr->children[i];
687             found_p = lv_refr_get_top_obj(area_p, child);
688 
689             /*If a children is ok then break*/
690             if(found_p != NULL) {
691                 break;
692             }
693         }
694 
695         /*If no better children use this object*/
696         if(found_p == NULL) {
697             if(info.res == LV_COVER_RES_COVER) {
698                 found_p = obj;
699             }
700         }
701     }
702 
703     return found_p;
704 }
705 
706 /**
707  * Make the refreshing from an object. Draw all its children and the youngers too.
708  * @param top_p pointer to an objects. Start the drawing from it.
709  * @param mask_p pointer to an area, the objects will be drawn only here
710  */
lv_refr_obj_and_children(lv_draw_ctx_t * draw_ctx,lv_obj_t * top_obj)711 static void lv_refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_obj)
712 {
713     /*Normally always will be a top_obj (at least the screen)
714      *but in special cases (e.g. if the screen has alpha) it won't.
715      *In this case use the screen directly*/
716     if(top_obj == NULL) top_obj = lv_disp_get_scr_act(disp_refr);
717     if(top_obj == NULL) return;  /*Shouldn't happen*/
718 
719     /*Refresh the top object and its children*/
720     lv_refr_obj(draw_ctx, top_obj);
721 
722     /*Draw the 'younger' sibling objects because they can be on top_obj*/
723     lv_obj_t * parent;
724     lv_obj_t * border_p = top_obj;
725 
726     parent = lv_obj_get_parent(top_obj);
727 
728     /*Do until not reach the screen*/
729     while(parent != NULL) {
730         bool go = false;
731         uint32_t i;
732         uint32_t child_cnt = lv_obj_get_child_cnt(parent);
733         for(i = 0; i < child_cnt; i++) {
734             lv_obj_t * child = parent->spec_attr->children[i];
735             if(!go) {
736                 if(child == border_p) go = true;
737             }
738             else {
739                 /*Refresh the objects*/
740                 lv_refr_obj(draw_ctx, child);
741             }
742         }
743 
744         /*Call the post draw draw function of the parents of the to object*/
745         lv_event_send(parent, LV_EVENT_DRAW_POST_BEGIN, (void *)draw_ctx);
746         lv_event_send(parent, LV_EVENT_DRAW_POST, (void *)draw_ctx);
747         lv_event_send(parent, LV_EVENT_DRAW_POST_END, (void *)draw_ctx);
748 
749         /*The new border will be the last parents,
750          *so the 'younger' brothers of parent will be refreshed*/
751         border_p = parent;
752         /*Go a level deeper*/
753         parent = lv_obj_get_parent(parent);
754     }
755 }
756 
get_max_row(lv_disp_t * disp,lv_coord_t area_w,lv_coord_t area_h)757 static uint32_t get_max_row(lv_disp_t * disp, lv_coord_t area_w, lv_coord_t area_h)
758 {
759     int32_t max_row = (uint32_t)disp->driver->draw_buf->size / area_w;
760 
761     if(max_row > area_h) max_row = area_h;
762 
763     /*Round down the lines of draw_buf if rounding is added*/
764     if(disp_refr->driver->rounder_cb) {
765         lv_area_t tmp;
766         tmp.x1 = 0;
767         tmp.x2 = 0;
768         tmp.y1 = 0;
769 
770         lv_coord_t h_tmp = max_row;
771         do {
772             tmp.y2 = h_tmp - 1;
773             disp_refr->driver->rounder_cb(disp_refr->driver, &tmp);
774 
775             /*If this height fits into `max_row` then fine*/
776             if(lv_area_get_height(&tmp) <= max_row) break;
777 
778             /*Decrement the height of the area until it fits into `max_row` after rounding*/
779             h_tmp--;
780         } while(h_tmp > 0);
781 
782         if(h_tmp <= 0) {
783             LV_LOG_WARN("Can't set draw_buf height using the round function. (Wrong round_cb or to "
784                         "small draw_buf)");
785             return 0;
786         }
787         else {
788             max_row = tmp.y2 + 1;
789         }
790     }
791 
792     return max_row;
793 }
794 
draw_buf_rotate_180(lv_disp_drv_t * drv,lv_area_t * area,lv_color_t * color_p)795 static void draw_buf_rotate_180(lv_disp_drv_t * drv, lv_area_t * area, lv_color_t * color_p)
796 {
797     lv_coord_t area_w = lv_area_get_width(area);
798     lv_coord_t area_h = lv_area_get_height(area);
799     uint32_t total = area_w * area_h;
800     /*Swap the beginning and end values*/
801     lv_color_t tmp;
802     uint32_t i = total - 1, j = 0;
803     while(i > j) {
804         tmp = color_p[i];
805         color_p[i] = color_p[j];
806         color_p[j] = tmp;
807         i--;
808         j++;
809     }
810     lv_coord_t tmp_coord;
811     tmp_coord = area->y2;
812     area->y2 = drv->ver_res - area->y1 - 1;
813     area->y1 = drv->ver_res - tmp_coord - 1;
814     tmp_coord = area->x2;
815     area->x2 = drv->hor_res - area->x1 - 1;
816     area->x1 = drv->hor_res - tmp_coord - 1;
817 }
818 
draw_buf_rotate_90(bool invert_i,lv_coord_t area_w,lv_coord_t area_h,lv_color_t * orig_color_p,lv_color_t * rot_buf)819 static LV_ATTRIBUTE_FAST_MEM void draw_buf_rotate_90(bool invert_i, lv_coord_t area_w, lv_coord_t area_h,
820                                                      lv_color_t * orig_color_p, lv_color_t * rot_buf)
821 {
822 
823     uint32_t invert = (area_w * area_h) - 1;
824     uint32_t initial_i = ((area_w - 1) * area_h);
825     for(lv_coord_t y = 0; y < area_h; y++) {
826         uint32_t i = initial_i + y;
827         if(invert_i)
828             i = invert - i;
829         for(lv_coord_t x = 0; x < area_w; x++) {
830             rot_buf[i] = *(orig_color_p++);
831             if(invert_i)
832                 i += area_h;
833             else
834                 i -= area_h;
835         }
836     }
837 }
838 
839 /**
840  * Helper function for draw_buf_rotate_90_sqr. Given a list of four numbers, rotate the entire list to the left.
841  */
draw_buf_rotate4(lv_color_t * a,lv_color_t * b,lv_color_t * c,lv_color_t * d)842 static inline void draw_buf_rotate4(lv_color_t * a, lv_color_t * b, lv_color_t * c, lv_color_t * d)
843 {
844     lv_color_t tmp;
845     tmp = *a;
846     *a = *b;
847     *b = *c;
848     *c = *d;
849     *d = tmp;
850 }
851 
852 /**
853  * Rotate a square image 90/270 degrees in place.
854  * @note inspired by https://stackoverflow.com/a/43694906
855  */
draw_buf_rotate_90_sqr(bool is_270,lv_coord_t w,lv_color_t * color_p)856 static void draw_buf_rotate_90_sqr(bool is_270, lv_coord_t w, lv_color_t * color_p)
857 {
858     for(lv_coord_t i = 0; i < w / 2; i++) {
859         for(lv_coord_t j = 0; j < (w + 1) / 2; j++) {
860             lv_coord_t inv_i = (w - 1) - i;
861             lv_coord_t inv_j = (w - 1) - j;
862             if(is_270) {
863                 draw_buf_rotate4(
864                     &color_p[i * w + j],
865                     &color_p[inv_j * w + i],
866                     &color_p[inv_i * w + inv_j],
867                     &color_p[j * w + inv_i]
868                 );
869             }
870             else {
871                 draw_buf_rotate4(
872                     &color_p[i * w + j],
873                     &color_p[j * w + inv_i],
874                     &color_p[inv_i * w + inv_j],
875                     &color_p[inv_j * w + i]
876                 );
877             }
878 
879         }
880     }
881 }
882 
883 /**
884  * Rotate the draw_buf to the display's native orientation.
885  */
draw_buf_rotate(lv_area_t * area,lv_color_t * color_p)886 static void draw_buf_rotate(lv_area_t * area, lv_color_t * color_p)
887 {
888     lv_disp_drv_t * drv = disp_refr->driver;
889     if(disp_refr->driver->full_refresh && drv->sw_rotate) {
890         LV_LOG_ERROR("cannot rotate a full refreshed display!");
891         return;
892     }
893     if(drv->rotated == LV_DISP_ROT_180) {
894         draw_buf_rotate_180(drv, area, color_p);
895         call_flush_cb(drv, area, color_p);
896     }
897     else if(drv->rotated == LV_DISP_ROT_90 || drv->rotated == LV_DISP_ROT_270) {
898         /*Allocate a temporary buffer to store rotated image*/
899         lv_color_t * rot_buf = NULL;
900         lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
901         lv_coord_t area_w = lv_area_get_width(area);
902         lv_coord_t area_h = lv_area_get_height(area);
903         /*Determine the maximum number of rows that can be rotated at a time*/
904         lv_coord_t max_row = LV_MIN((lv_coord_t)((LV_DISP_ROT_MAX_BUF / sizeof(lv_color_t)) / area_w), area_h);
905         lv_coord_t init_y_off;
906         init_y_off = area->y1;
907         if(drv->rotated == LV_DISP_ROT_90) {
908             area->y2 = drv->ver_res - area->x1 - 1;
909             area->y1 = area->y2 - area_w + 1;
910         }
911         else {
912             area->y1 = area->x1;
913             area->y2 = area->y1 + area_w - 1;
914         }
915 
916         /*Rotate the screen in chunks, flushing after each one*/
917         lv_coord_t row = 0;
918         while(row < area_h) {
919             lv_coord_t height = LV_MIN(max_row, area_h - row);
920             draw_buf->flushing = 1;
921             if((row == 0) && (area_h >= area_w)) {
922                 /*Rotate the initial area as a square*/
923                 height = area_w;
924                 draw_buf_rotate_90_sqr(drv->rotated == LV_DISP_ROT_270, area_w, color_p);
925                 if(drv->rotated == LV_DISP_ROT_90) {
926                     area->x1 = init_y_off;
927                     area->x2 = init_y_off + area_w - 1;
928                 }
929                 else {
930                     area->x2 = drv->hor_res - 1 - init_y_off;
931                     area->x1 = area->x2 - area_w + 1;
932                 }
933             }
934             else {
935                 /*Rotate other areas using a maximum buffer size*/
936                 if(rot_buf == NULL) rot_buf = lv_mem_buf_get(LV_DISP_ROT_MAX_BUF);
937                 draw_buf_rotate_90(drv->rotated == LV_DISP_ROT_270, area_w, height, color_p, rot_buf);
938 
939                 if(drv->rotated == LV_DISP_ROT_90) {
940                     area->x1 = init_y_off + row;
941                     area->x2 = init_y_off + row + height - 1;
942                 }
943                 else {
944                     area->x2 = drv->hor_res - 1 - init_y_off - row;
945                     area->x1 = area->x2 - height + 1;
946                 }
947             }
948 
949             /* The original part (chunk of the current area) were split into more parts here.
950              * Set the original last_part flag on the last part of rotation. */
951             if(row + height >= area_h && draw_buf->last_area && draw_buf->last_part) {
952                 draw_buf->flushing_last = 1;
953             }
954             else {
955                 draw_buf->flushing_last = 0;
956             }
957 
958             /*Flush the completed area to the display*/
959             call_flush_cb(drv, area, rot_buf == NULL ? color_p : rot_buf);
960             /*FIXME: Rotation forces legacy behavior where rendering and flushing are done serially*/
961             while(draw_buf->flushing) {
962                 if(drv->wait_cb) drv->wait_cb(drv);
963             }
964             color_p += area_w * height;
965             row += height;
966         }
967         /*Free the allocated buffer at the end if necessary*/
968         if(rot_buf != NULL) lv_mem_buf_release(rot_buf);
969     }
970 }
971 
972 /**
973  * Flush the content of the draw buffer
974  */
draw_buf_flush(lv_disp_t * disp)975 static void draw_buf_flush(lv_disp_t * disp)
976 {
977     lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
978 
979     /*Flush the rendered content to the display*/
980     lv_draw_ctx_t * draw_ctx = disp->driver->draw_ctx;
981     if(draw_ctx->wait_for_finish) draw_ctx->wait_for_finish(draw_ctx);
982 
983     /* In double buffered mode wait until the other buffer is freed
984      * and driver is ready to receive the new buffer */
985     if(draw_buf->buf1 && draw_buf->buf2) {
986         while(draw_buf->flushing) {
987             if(disp_refr->driver->wait_cb) disp_refr->driver->wait_cb(disp_refr->driver);
988         }
989     }
990 
991     draw_buf->flushing = 1;
992 
993     if(disp_refr->driver->draw_buf->last_area && disp_refr->driver->draw_buf->last_part) draw_buf->flushing_last = 1;
994     else draw_buf->flushing_last = 0;
995 
996     bool flushing_last = draw_buf->flushing_last;
997 
998     if(disp->driver->flush_cb) {
999         /*Rotate the buffer to the display's native orientation if necessary*/
1000         if(disp->driver->rotated != LV_DISP_ROT_NONE && disp->driver->sw_rotate) {
1001             draw_buf_rotate(draw_ctx->buf_area, draw_ctx->buf);
1002         }
1003         else {
1004             call_flush_cb(disp->driver, draw_ctx->buf_area, draw_ctx->buf);
1005         }
1006     }
1007     /*If there are 2 buffers swap them. With direct mode swap only on the last area*/
1008     if(draw_buf->buf1 && draw_buf->buf2 && (!disp->driver->direct_mode || flushing_last)) {
1009         if(draw_buf->buf_act == draw_buf->buf1)
1010             draw_buf->buf_act = draw_buf->buf2;
1011         else
1012             draw_buf->buf_act = draw_buf->buf1;
1013     }
1014 }
1015 
call_flush_cb(lv_disp_drv_t * drv,const lv_area_t * area,lv_color_t * color_p)1016 static void call_flush_cb(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p)
1017 {
1018     REFR_TRACE("Calling flush_cb on (%d;%d)(%d;%d) area with %p image pointer", area->x1, area->y1, area->x2, area->y2,
1019                (void *)color_p);
1020 
1021     lv_area_t offset_area = {
1022         .x1 = area->x1 + drv->offset_x,
1023         .y1 = area->y1 + drv->offset_y,
1024         .x2 = area->x2 + drv->offset_x,
1025         .y2 = area->y2 + drv->offset_y
1026     };
1027 
1028     drv->flush_cb(drv, &offset_area, color_p);
1029 }
1030 
1031 #if LV_USE_PERF_MONITOR
perf_monitor_init(perf_monitor_t * _perf_monitor)1032 static void perf_monitor_init(perf_monitor_t * _perf_monitor)
1033 {
1034     LV_ASSERT_NULL(_perf_monitor);
1035     _perf_monitor->elaps_sum = 0;
1036     _perf_monitor->fps_sum_all = 0;
1037     _perf_monitor->fps_sum_cnt = 0;
1038     _perf_monitor->frame_cnt = 0;
1039     _perf_monitor->perf_last_time = 0;
1040     _perf_monitor->perf_label = NULL;
1041 }
1042 #endif
1043 
1044 #if LV_USE_MEM_MONITOR
mem_monitor_init(mem_monitor_t * _mem_monitor)1045 static void mem_monitor_init(mem_monitor_t * _mem_monitor)
1046 {
1047     LV_ASSERT_NULL(_mem_monitor);
1048     _mem_monitor->mem_last_time = 0;
1049     _mem_monitor->mem_label = NULL;
1050 }
1051 #endif
1052 
1053