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 #include "../extra/others/snapshot/lv_snapshot.h"
21 
22 #if LV_USE_PERF_MONITOR || LV_USE_MEM_MONITOR
23     #include "../widgets/lv_label.h"
24 #endif
25 
26 /*********************
27  *      DEFINES
28  *********************/
29 
30 /**********************
31  *      TYPEDEFS
32  **********************/
33 typedef struct {
34     uint32_t    perf_last_time;
35     uint32_t    elaps_sum;
36     uint32_t    frame_cnt;
37     uint32_t    fps_sum_cnt;
38     uint32_t    fps_sum_all;
39 #if LV_USE_LABEL
40     lv_obj_t  * perf_label;
41 #endif
42 } perf_monitor_t;
43 
44 typedef struct {
45     uint32_t     mem_last_time;
46 #if LV_USE_LABEL
47     lv_obj_t  *  mem_label;
48 #endif
49 } mem_monitor_t;
50 
51 /**********************
52  *  STATIC PROTOTYPES
53  **********************/
54 static void lv_refr_join_area(void);
55 static void refr_invalid_areas(void);
56 static void refr_sync_areas(void);
57 static void refr_area(const lv_area_t * area_p);
58 static void refr_area_part(lv_draw_ctx_t * draw_ctx);
59 static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj);
60 static void refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_obj);
61 static void refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj);
62 static uint32_t get_max_row(lv_disp_t * disp, lv_coord_t area_w, lv_coord_t area_h);
63 static void draw_buf_flush(lv_disp_t * disp);
64 static void call_flush_cb(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p);
65 
66 #if LV_USE_PERF_MONITOR
67     static void perf_monitor_init(perf_monitor_t * perf_monitor);
68 #endif
69 #if LV_USE_MEM_MONITOR
70     static void mem_monitor_init(mem_monitor_t * mem_monitor);
71 #endif
72 
73 /**********************
74  *  STATIC VARIABLES
75  **********************/
76 static uint32_t px_num;
77 static lv_disp_t * disp_refr; /*Display being refreshed*/
78 
79 #if LV_USE_PERF_MONITOR
80     static perf_monitor_t   perf_monitor;
81 #endif
82 
83 #if LV_USE_MEM_MONITOR
84     static mem_monitor_t    mem_monitor;
85 #endif
86 
87 /**********************
88  *      MACROS
89  **********************/
90 #if LV_LOG_TRACE_DISP_REFR
91     #define REFR_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
92 #else
93     #define REFR_TRACE(...)
94 #endif
95 
96 /**********************
97  *   GLOBAL FUNCTIONS
98  **********************/
99 
100 /**
101  * Initialize the screen refresh subsystem
102  */
_lv_refr_init(void)103 void _lv_refr_init(void)
104 {
105 #if LV_USE_PERF_MONITOR
106     perf_monitor_init(&perf_monitor);
107 #endif
108 #if LV_USE_MEM_MONITOR
109     mem_monitor_init(&mem_monitor);
110 #endif
111 }
112 
lv_refr_now(lv_disp_t * disp)113 void lv_refr_now(lv_disp_t * disp)
114 {
115     lv_anim_refr_now();
116 
117     if(disp) {
118         if(disp->refr_timer) _lv_disp_refr_timer(disp->refr_timer);
119     }
120     else {
121         lv_disp_t * d;
122         d = lv_disp_get_next(NULL);
123         while(d) {
124             if(d->refr_timer) _lv_disp_refr_timer(d->refr_timer);
125             d = lv_disp_get_next(d);
126         }
127     }
128 }
129 
lv_obj_redraw(lv_draw_ctx_t * draw_ctx,lv_obj_t * obj)130 void lv_obj_redraw(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj)
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     bool com_clip_res = _lv_area_intersect(&clip_coords_for_obj, clip_area_ori, &obj_coords_ext);
141     /*If the object is visible on the current clip area OR has overflow visible draw it.
142      *With overflow visible drawing should happen to apply the masks which might affect children */
143     bool should_draw = com_clip_res || lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE);
144     if(should_draw) {
145         draw_ctx->clip_area = &clip_coords_for_obj;
146 
147         lv_event_send(obj, LV_EVENT_DRAW_MAIN_BEGIN, draw_ctx);
148         lv_event_send(obj, LV_EVENT_DRAW_MAIN, draw_ctx);
149         lv_event_send(obj, LV_EVENT_DRAW_MAIN_END, draw_ctx);
150 #if LV_USE_REFR_DEBUG
151         lv_color_t debug_color = lv_color_make(lv_rand(0, 0xFF), lv_rand(0, 0xFF), lv_rand(0, 0xFF));
152         lv_draw_rect_dsc_t draw_dsc;
153         lv_draw_rect_dsc_init(&draw_dsc);
154         draw_dsc.bg_color.full = debug_color.full;
155         draw_dsc.bg_opa = LV_OPA_20;
156         draw_dsc.border_width = 1;
157         draw_dsc.border_opa = LV_OPA_30;
158         draw_dsc.border_color = debug_color;
159         lv_draw_rect(draw_ctx, &draw_dsc, &obj_coords_ext);
160 #endif
161     }
162 
163     /*With overflow visible keep the previous clip area to let the children visible out of this object too
164      *With not overflow visible limit the clip are to the object's coordinates to clip the children*/
165     lv_area_t clip_coords_for_children;
166     bool refr_children = true;
167     if(lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) {
168         clip_coords_for_children  = *clip_area_ori;
169     }
170     else {
171         if(!_lv_area_intersect(&clip_coords_for_children, clip_area_ori, &obj->coords)) {
172             refr_children = false;
173         }
174     }
175 
176     if(refr_children) {
177         draw_ctx->clip_area = &clip_coords_for_children;
178         uint32_t i;
179         uint32_t child_cnt = lv_obj_get_child_cnt(obj);
180         for(i = 0; i < child_cnt; i++) {
181             lv_obj_t * child = obj->spec_attr->children[i];
182             refr_obj(draw_ctx, child);
183         }
184     }
185 
186     /*If the object was visible on the clip area call the post draw events too*/
187     if(should_draw) {
188         draw_ctx->clip_area = &clip_coords_for_obj;
189 
190         /*If all the children are redrawn make 'post draw' draw*/
191         lv_event_send(obj, LV_EVENT_DRAW_POST_BEGIN, draw_ctx);
192         lv_event_send(obj, LV_EVENT_DRAW_POST, draw_ctx);
193         lv_event_send(obj, LV_EVENT_DRAW_POST_END, draw_ctx);
194     }
195 
196     draw_ctx->clip_area = clip_area_ori;
197 }
198 
199 /**
200  * Invalidate an area on display to redraw it
201  * @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
202  * @param disp pointer to display where the area should be invalidated (NULL can be used if there is
203  * only one display)
204  */
_lv_inv_area(lv_disp_t * disp,const lv_area_t * area_p)205 void _lv_inv_area(lv_disp_t * disp, const lv_area_t * area_p)
206 {
207     if(!disp) disp = lv_disp_get_default();
208     if(!disp) return;
209     if(!lv_disp_is_invalidation_enabled(disp)) return;
210 
211     if(disp->rendering_in_progress) {
212         LV_LOG_ERROR("detected modifying dirty areas in render");
213         return;
214     }
215 
216     /*Clear the invalidate buffer if the parameter is NULL*/
217     if(area_p == NULL) {
218         disp->inv_p = 0;
219         return;
220     }
221 
222     lv_area_t scr_area;
223     scr_area.x1 = 0;
224     scr_area.y1 = 0;
225     scr_area.x2 = lv_disp_get_hor_res(disp) - 1;
226     scr_area.y2 = lv_disp_get_ver_res(disp) - 1;
227 
228     lv_area_t com_area;
229     bool suc;
230 
231     suc = _lv_area_intersect(&com_area, area_p, &scr_area);
232     if(suc == false)  return; /*Out of the screen*/
233 
234     /*If there were at least 1 invalid area in full refresh mode, redraw the whole screen*/
235     if(disp->driver->full_refresh) {
236         disp->inv_areas[0] = scr_area;
237         disp->inv_p = 1;
238         if(disp->refr_timer) lv_timer_resume(disp->refr_timer);
239         return;
240     }
241 
242     if(disp->driver->rounder_cb) disp->driver->rounder_cb(disp->driver, &com_area);
243 
244     /*Save only if this area is not in one of the saved areas*/
245     uint16_t i;
246     for(i = 0; i < disp->inv_p; i++) {
247         if(_lv_area_is_in(&com_area, &disp->inv_areas[i], 0) != false) return;
248     }
249 
250     /*Save the area*/
251     if(disp->inv_p < LV_INV_BUF_SIZE) {
252         lv_area_copy(&disp->inv_areas[disp->inv_p], &com_area);
253     }
254     else {   /*If no place for the area add the screen*/
255         disp->inv_p = 0;
256         lv_area_copy(&disp->inv_areas[disp->inv_p], &scr_area);
257     }
258     disp->inv_p++;
259     if(disp->refr_timer) lv_timer_resume(disp->refr_timer);
260 }
261 
262 /**
263  * Get the display which is being refreshed
264  * @return the display being refreshed
265  */
_lv_refr_get_disp_refreshing(void)266 lv_disp_t * _lv_refr_get_disp_refreshing(void)
267 {
268     return disp_refr;
269 }
270 
271 /**
272  * Set the display which is being refreshed.
273  * It shouldn't be used directly by the user.
274  * It can be used to trick the drawing functions about there is an active display.
275  * @param the display being refreshed
276  */
_lv_refr_set_disp_refreshing(lv_disp_t * disp)277 void _lv_refr_set_disp_refreshing(lv_disp_t * disp)
278 {
279     disp_refr = disp;
280 }
281 
282 /**
283  * Called periodically to handle the refreshing
284  * @param tmr pointer to the timer itself
285  */
_lv_disp_refr_timer(lv_timer_t * tmr)286 void _lv_disp_refr_timer(lv_timer_t * tmr)
287 {
288     REFR_TRACE("begin");
289 
290     uint32_t start = lv_tick_get();
291     volatile uint32_t elaps = 0;
292 
293     if(tmr) {
294         disp_refr = tmr->user_data;
295 #if LV_USE_PERF_MONITOR == 0 && LV_USE_MEM_MONITOR == 0
296         /**
297          * Ensure the timer does not run again automatically.
298          * This is done before refreshing in case refreshing invalidates something else.
299          */
300         lv_timer_pause(tmr);
301 #endif
302     }
303     else {
304         disp_refr = lv_disp_get_default();
305     }
306 
307     /*Refresh the screen's layout if required*/
308     lv_obj_update_layout(disp_refr->act_scr);
309     if(disp_refr->prev_scr) lv_obj_update_layout(disp_refr->prev_scr);
310 
311     lv_obj_update_layout(disp_refr->top_layer);
312     lv_obj_update_layout(disp_refr->sys_layer);
313 
314     /*Do nothing if there is no active screen*/
315     if(disp_refr->act_scr == NULL) {
316         disp_refr->inv_p = 0;
317         LV_LOG_WARN("there is no active screen");
318         REFR_TRACE("finished");
319         return;
320     }
321 
322     lv_refr_join_area();
323     refr_sync_areas();
324     refr_invalid_areas();
325 
326     /*If refresh happened ...*/
327     if(disp_refr->inv_p != 0) {
328 
329         /*Copy invalid areas for sync next refresh in double buffered direct mode*/
330         if(disp_refr->driver->direct_mode && disp_refr->driver->draw_buf->buf2) {
331 
332             uint16_t i;
333             for(i = 0; i < disp_refr->inv_p; i++) {
334                 if(disp_refr->inv_area_joined[i])
335                     continue;
336 
337                 lv_area_t * sync_area = _lv_ll_ins_tail(&disp_refr->sync_areas);
338                 *sync_area = disp_refr->inv_areas[i];
339             }
340         }
341 
342         /*Clean up*/
343         lv_memset_00(disp_refr->inv_areas, sizeof(disp_refr->inv_areas));
344         lv_memset_00(disp_refr->inv_area_joined, sizeof(disp_refr->inv_area_joined));
345         disp_refr->inv_p = 0;
346 
347         elaps = lv_tick_elaps(start);
348 
349         /*Call monitor cb if present*/
350         if(disp_refr->driver->monitor_cb) {
351             disp_refr->driver->monitor_cb(disp_refr->driver, elaps, px_num);
352         }
353     }
354 
355     lv_mem_buf_free_all();
356     _lv_font_clean_up_fmt_txt();
357 
358 #if LV_DRAW_COMPLEX
359     _lv_draw_mask_cleanup();
360 #endif
361 
362 #if LV_USE_PERF_MONITOR && LV_USE_LABEL
363     lv_obj_t * perf_label = perf_monitor.perf_label;
364     if(perf_label == NULL) {
365         perf_label = lv_label_create(lv_layer_sys());
366         lv_obj_set_style_bg_opa(perf_label, LV_OPA_50, 0);
367         lv_obj_set_style_bg_color(perf_label, lv_color_black(), 0);
368         lv_obj_set_style_text_color(perf_label, lv_color_white(), 0);
369         lv_obj_set_style_pad_top(perf_label, 3, 0);
370         lv_obj_set_style_pad_bottom(perf_label, 3, 0);
371         lv_obj_set_style_pad_left(perf_label, 3, 0);
372         lv_obj_set_style_pad_right(perf_label, 3, 0);
373         lv_obj_set_style_text_align(perf_label, LV_TEXT_ALIGN_RIGHT, 0);
374         lv_label_set_text(perf_label, "?");
375         lv_obj_align(perf_label, LV_USE_PERF_MONITOR_POS, 0, 0);
376         perf_monitor.perf_label = perf_label;
377     }
378 
379     if(lv_tick_elaps(perf_monitor.perf_last_time) < 300) {
380         if(px_num > 5000) {
381             perf_monitor.elaps_sum += elaps;
382             perf_monitor.frame_cnt ++;
383         }
384     }
385     else {
386         perf_monitor.perf_last_time = lv_tick_get();
387         uint32_t fps_limit;
388         uint32_t fps;
389 
390         if(disp_refr->refr_timer) {
391             fps_limit = 1000 / disp_refr->refr_timer->period;
392         }
393         else {
394             fps_limit = 1000 / LV_DISP_DEF_REFR_PERIOD;
395         }
396 
397         if(perf_monitor.elaps_sum == 0) {
398             perf_monitor.elaps_sum = 1;
399         }
400         if(perf_monitor.frame_cnt == 0) {
401             fps = fps_limit;
402         }
403         else {
404             fps = (1000 * perf_monitor.frame_cnt) / perf_monitor.elaps_sum;
405         }
406         perf_monitor.elaps_sum = 0;
407         perf_monitor.frame_cnt = 0;
408         if(fps > fps_limit) {
409             fps = fps_limit;
410         }
411 
412         perf_monitor.fps_sum_all += fps;
413         perf_monitor.fps_sum_cnt ++;
414         uint32_t cpu = 100 - lv_timer_get_idle();
415         lv_label_set_text_fmt(perf_label, "%"LV_PRIu32" FPS\n%"LV_PRIu32"%% CPU", fps, cpu);
416     }
417 #endif
418 
419 #if LV_USE_MEM_MONITOR && LV_MEM_CUSTOM == 0 && LV_USE_LABEL
420     lv_obj_t * mem_label = mem_monitor.mem_label;
421     if(mem_label == NULL) {
422         mem_label = lv_label_create(lv_layer_sys());
423         lv_obj_set_style_bg_opa(mem_label, LV_OPA_50, 0);
424         lv_obj_set_style_bg_color(mem_label, lv_color_black(), 0);
425         lv_obj_set_style_text_color(mem_label, lv_color_white(), 0);
426         lv_obj_set_style_pad_top(mem_label, 3, 0);
427         lv_obj_set_style_pad_bottom(mem_label, 3, 0);
428         lv_obj_set_style_pad_left(mem_label, 3, 0);
429         lv_obj_set_style_pad_right(mem_label, 3, 0);
430         lv_label_set_text(mem_label, "?");
431         lv_obj_align(mem_label, LV_USE_MEM_MONITOR_POS, 0, 0);
432         mem_monitor.mem_label = mem_label;
433     }
434 
435     if(lv_tick_elaps(mem_monitor.mem_last_time) > 300) {
436         mem_monitor.mem_last_time = lv_tick_get();
437         lv_mem_monitor_t mon;
438         lv_mem_monitor(&mon);
439         uint32_t used_size = mon.total_size - mon.free_size;;
440         uint32_t used_kb = used_size / 1024;
441         uint32_t used_kb_tenth = (used_size - (used_kb * 1024)) / 102;
442         lv_label_set_text_fmt(mem_label,
443                               "%"LV_PRIu32 ".%"LV_PRIu32 " kB used (%d %%)\n"
444                               "%d%% frag.",
445                               used_kb, used_kb_tenth, mon.used_pct,
446                               mon.frag_pct);
447     }
448 #endif
449 
450     REFR_TRACE("finished");
451 }
452 
453 #if LV_USE_PERF_MONITOR
lv_refr_reset_fps_counter(void)454 void lv_refr_reset_fps_counter(void)
455 {
456     perf_monitor.fps_sum_all = 0;
457     perf_monitor.fps_sum_cnt = 0;
458 }
459 
lv_refr_get_fps_avg(void)460 uint32_t lv_refr_get_fps_avg(void)
461 {
462     if(perf_monitor.fps_sum_cnt == 0) {
463         return 0;
464     }
465     return perf_monitor.fps_sum_all / perf_monitor.fps_sum_cnt;
466 }
467 #endif
468 
469 /**********************
470  *   STATIC FUNCTIONS
471  **********************/
472 
473 /**
474  * Join the areas which has got common parts
475  */
lv_refr_join_area(void)476 static void lv_refr_join_area(void)
477 {
478     uint32_t join_from;
479     uint32_t join_in;
480     lv_area_t joined_area;
481     for(join_in = 0; join_in < disp_refr->inv_p; join_in++) {
482         if(disp_refr->inv_area_joined[join_in] != 0) continue;
483 
484         /*Check all areas to join them in 'join_in'*/
485         for(join_from = 0; join_from < disp_refr->inv_p; join_from++) {
486             /*Handle only unjoined areas and ignore itself*/
487             if(disp_refr->inv_area_joined[join_from] != 0 || join_in == join_from) {
488                 continue;
489             }
490 
491             /*Check if the areas are on each other*/
492             if(_lv_area_is_on(&disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]) == false) {
493                 continue;
494             }
495 
496             _lv_area_join(&joined_area, &disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]);
497 
498             /*Join two area only if the joined area size is smaller*/
499             if(lv_area_get_size(&joined_area) < (lv_area_get_size(&disp_refr->inv_areas[join_in]) +
500                                                  lv_area_get_size(&disp_refr->inv_areas[join_from]))) {
501                 lv_area_copy(&disp_refr->inv_areas[join_in], &joined_area);
502 
503                 /*Mark 'join_form' is joined into 'join_in'*/
504                 disp_refr->inv_area_joined[join_from] = 1;
505             }
506         }
507     }
508 }
509 
510 /**
511  * Refresh the sync areas
512  */
refr_sync_areas(void)513 static void refr_sync_areas(void)
514 {
515     /*Do not sync if not direct mode*/
516     if(!disp_refr->driver->direct_mode) return;
517 
518     /*Do not sync if not double buffered*/
519     if(disp_refr->driver->draw_buf->buf2 == NULL) return;
520 
521     /*Do not sync if no sync areas*/
522     if(_lv_ll_is_empty(&disp_refr->sync_areas)) return;
523 
524     /*The buffers are already swapped.
525      *So the active buffer is the off screen buffer where LVGL will render*/
526     void * buf_off_screen = disp_refr->driver->draw_buf->buf_act;
527     void * buf_on_screen = disp_refr->driver->draw_buf->buf_act == disp_refr->driver->draw_buf->buf1
528                            ? disp_refr->driver->draw_buf->buf2
529                            : disp_refr->driver->draw_buf->buf1;
530 
531     /*Get stride for buffer copy*/
532     lv_coord_t stride = lv_disp_get_hor_res(disp_refr);
533 
534     /*Iterate through invalidated areas to see if sync area should be copied*/
535     lv_area_t res[4] = {0};
536     int8_t res_c, j;
537     uint32_t i;
538     lv_area_t * sync_area, * new_area, * next_area;
539     for(i = 0; i < disp_refr->inv_p; i++) {
540         /*Skip joined areas*/
541         if(disp_refr->inv_area_joined[i]) continue;
542 
543         /*Iterate over sync areas*/
544         sync_area = _lv_ll_get_head(&disp_refr->sync_areas);
545         while(sync_area != NULL) {
546             /*Get next sync area*/
547             next_area = _lv_ll_get_next(&disp_refr->sync_areas, sync_area);
548 
549             /*Remove intersect of redraw area from sync area and get remaining areas*/
550             res_c = _lv_area_diff(res, sync_area, &disp_refr->inv_areas[i]);
551 
552             /*New sub areas created after removing intersect*/
553             if(res_c != -1) {
554                 /*Replace old sync area with new areas*/
555                 for(j = 0; j < res_c; j++) {
556                     new_area = _lv_ll_ins_prev(&disp_refr->sync_areas, sync_area);
557                     *new_area = res[j];
558                 }
559                 _lv_ll_remove(&disp_refr->sync_areas, sync_area);
560                 lv_mem_free(sync_area);
561             }
562 
563             /*Move on to next sync area*/
564             sync_area = next_area;
565         }
566     }
567 
568     /*Copy sync areas (if any remaining)*/
569     for(sync_area = _lv_ll_get_head(&disp_refr->sync_areas); sync_area != NULL;
570         sync_area = _lv_ll_get_next(&disp_refr->sync_areas, sync_area)) {
571         disp_refr->driver->draw_ctx->buffer_copy(
572             disp_refr->driver->draw_ctx,
573             buf_off_screen, stride, sync_area,
574             buf_on_screen, stride, sync_area
575         );
576     }
577 
578     /*Clear sync areas*/
579     _lv_ll_clear(&disp_refr->sync_areas);
580 }
581 
582 /**
583  * Refresh the joined areas
584  */
refr_invalid_areas(void)585 static void refr_invalid_areas(void)
586 {
587     px_num = 0;
588 
589     if(disp_refr->inv_p == 0) return;
590 
591     /*Find the last area which will be drawn*/
592     int32_t i;
593     int32_t last_i = 0;
594     for(i = disp_refr->inv_p - 1; i >= 0; i--) {
595         if(disp_refr->inv_area_joined[i] == 0) {
596             last_i = i;
597             break;
598         }
599     }
600 
601     /*Notify the display driven rendering has started*/
602     if(disp_refr->driver->render_start_cb) {
603         disp_refr->driver->render_start_cb(disp_refr->driver);
604     }
605 
606     disp_refr->driver->draw_buf->last_area = 0;
607     disp_refr->driver->draw_buf->last_part = 0;
608     disp_refr->rendering_in_progress = true;
609 
610     for(i = 0; i < disp_refr->inv_p; i++) {
611         /*Refresh the unjoined areas*/
612         if(disp_refr->inv_area_joined[i] == 0) {
613 
614             if(i == last_i) disp_refr->driver->draw_buf->last_area = 1;
615             disp_refr->driver->draw_buf->last_part = 0;
616             refr_area(&disp_refr->inv_areas[i]);
617 
618             px_num += lv_area_get_size(&disp_refr->inv_areas[i]);
619         }
620     }
621 
622     disp_refr->rendering_in_progress = false;
623 }
624 
625 /**
626  * Refresh an area if there is Virtual Display Buffer
627  * @param area_p  pointer to an area to refresh
628  */
refr_area(const lv_area_t * area_p)629 static void refr_area(const lv_area_t * area_p)
630 {
631     lv_draw_ctx_t * draw_ctx = disp_refr->driver->draw_ctx;
632     draw_ctx->buf = disp_refr->driver->draw_buf->buf_act;
633 
634     /*With full refresh just redraw directly into the buffer*/
635     /*In direct mode draw directly on the absolute coordinates of the buffer*/
636     if(disp_refr->driver->full_refresh || disp_refr->driver->direct_mode) {
637         lv_area_t disp_area;
638         lv_area_set(&disp_area, 0, 0, lv_disp_get_hor_res(disp_refr) - 1, lv_disp_get_ver_res(disp_refr) - 1);
639         draw_ctx->buf_area = &disp_area;
640 
641         if(disp_refr->driver->full_refresh) {
642             disp_refr->driver->draw_buf->last_part = 1;
643             draw_ctx->clip_area = &disp_area;
644             refr_area_part(draw_ctx);
645         }
646         else {
647             disp_refr->driver->draw_buf->last_part = disp_refr->driver->draw_buf->last_area;
648             draw_ctx->clip_area = area_p;
649             refr_area_part(draw_ctx);
650         }
651         return;
652     }
653 
654     /*Normal refresh: draw the area in parts*/
655     /*Calculate the max row num*/
656     lv_coord_t w = lv_area_get_width(area_p);
657     lv_coord_t h = lv_area_get_height(area_p);
658     lv_coord_t y2 = area_p->y2 >= lv_disp_get_ver_res(disp_refr) ?
659                     lv_disp_get_ver_res(disp_refr) - 1 : area_p->y2;
660 
661     int32_t max_row = get_max_row(disp_refr, w, h);
662 
663     lv_coord_t row;
664     lv_coord_t row_last = 0;
665     lv_area_t sub_area;
666     for(row = area_p->y1; row + max_row - 1 <= y2; row += max_row) {
667         /*Calc. the next y coordinates of draw_buf*/
668         sub_area.x1 = area_p->x1;
669         sub_area.x2 = area_p->x2;
670         sub_area.y1 = row;
671         sub_area.y2 = row + max_row - 1;
672         draw_ctx->buf_area = &sub_area;
673         draw_ctx->clip_area = &sub_area;
674         draw_ctx->buf = disp_refr->driver->draw_buf->buf_act;
675         if(sub_area.y2 > y2) sub_area.y2 = y2;
676         row_last = sub_area.y2;
677         if(y2 == row_last) disp_refr->driver->draw_buf->last_part = 1;
678         refr_area_part(draw_ctx);
679     }
680 
681     /*If the last y coordinates are not handled yet ...*/
682     if(y2 != row_last) {
683         /*Calc. the next y coordinates of draw_buf*/
684         sub_area.x1 = area_p->x1;
685         sub_area.x2 = area_p->x2;
686         sub_area.y1 = row;
687         sub_area.y2 = y2;
688         draw_ctx->buf_area = &sub_area;
689         draw_ctx->clip_area = &sub_area;
690         draw_ctx->buf = disp_refr->driver->draw_buf->buf_act;
691         disp_refr->driver->draw_buf->last_part = 1;
692         refr_area_part(draw_ctx);
693     }
694 }
695 
refr_area_part(lv_draw_ctx_t * draw_ctx)696 static void refr_area_part(lv_draw_ctx_t * draw_ctx)
697 {
698     lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
699 
700     if(draw_ctx->init_buf)
701         draw_ctx->init_buf(draw_ctx);
702 
703     /* Below the `area_p` area will be redrawn into the draw buffer.
704      * In single buffered mode wait here until the buffer is freed.
705      * In full double buffered mode wait here while the buffers are swapped and a buffer becomes available*/
706     bool full_sized = draw_buf->size == (uint32_t)disp_refr->driver->hor_res * disp_refr->driver->ver_res;
707     if((draw_buf->buf1 && !draw_buf->buf2) ||
708        (draw_buf->buf1 && draw_buf->buf2 && full_sized)) {
709         while(draw_buf->flushing) {
710             if(disp_refr->driver->wait_cb) disp_refr->driver->wait_cb(disp_refr->driver);
711         }
712 
713         /*If the screen is transparent initialize it when the flushing is ready*/
714 #if LV_COLOR_SCREEN_TRANSP
715         if(disp_refr->driver->screen_transp) {
716             if(disp_refr->driver->clear_cb) {
717                 disp_refr->driver->clear_cb(disp_refr->driver, disp_refr->driver->draw_buf->buf_act, disp_refr->driver->draw_buf->size);
718             }
719             else {
720                 lv_memset_00(disp_refr->driver->draw_buf->buf_act, disp_refr->driver->draw_buf->size * LV_IMG_PX_SIZE_ALPHA_BYTE);
721             }
722         }
723 #endif
724     }
725 
726     lv_obj_t * top_act_scr = NULL;
727     lv_obj_t * top_prev_scr = NULL;
728 
729     /*Get the most top object which is not covered by others*/
730     top_act_scr = lv_refr_get_top_obj(draw_ctx->buf_area, lv_disp_get_scr_act(disp_refr));
731     if(disp_refr->prev_scr) {
732         top_prev_scr = lv_refr_get_top_obj(draw_ctx->buf_area, disp_refr->prev_scr);
733     }
734 
735     /*Draw a display background if there is no top object*/
736     if(top_act_scr == NULL && top_prev_scr == NULL) {
737         lv_area_t a;
738         lv_area_set(&a, 0, 0,
739                     lv_disp_get_hor_res(disp_refr) - 1, lv_disp_get_ver_res(disp_refr) - 1);
740         if(draw_ctx->draw_bg) {
741             lv_draw_rect_dsc_t dsc;
742             lv_draw_rect_dsc_init(&dsc);
743             dsc.bg_img_src = disp_refr->bg_img;
744             dsc.bg_img_opa = disp_refr->bg_opa;
745             dsc.bg_color = disp_refr->bg_color;
746             dsc.bg_opa = disp_refr->bg_opa;
747             draw_ctx->draw_bg(draw_ctx, &dsc, &a);
748         }
749         else if(disp_refr->bg_img) {
750             lv_img_header_t header;
751             lv_res_t res = lv_img_decoder_get_info(disp_refr->bg_img, &header);
752             if(res == LV_RES_OK) {
753                 lv_draw_img_dsc_t dsc;
754                 lv_draw_img_dsc_init(&dsc);
755                 dsc.opa = disp_refr->bg_opa;
756                 lv_draw_img(draw_ctx, &dsc, &a, disp_refr->bg_img);
757             }
758             else {
759                 LV_LOG_WARN("Can't draw the background image");
760             }
761         }
762         else {
763             lv_draw_rect_dsc_t dsc;
764             lv_draw_rect_dsc_init(&dsc);
765             dsc.bg_color = disp_refr->bg_color;
766             dsc.bg_opa = disp_refr->bg_opa;
767             lv_draw_rect(draw_ctx, &dsc, draw_ctx->buf_area);
768         }
769     }
770 
771     if(disp_refr->draw_prev_over_act) {
772         if(top_act_scr == NULL) top_act_scr = disp_refr->act_scr;
773         refr_obj_and_children(draw_ctx, top_act_scr);
774 
775         /*Refresh the previous screen if any*/
776         if(disp_refr->prev_scr) {
777             if(top_prev_scr == NULL) top_prev_scr = disp_refr->prev_scr;
778             refr_obj_and_children(draw_ctx, top_prev_scr);
779         }
780     }
781     else {
782         /*Refresh the previous screen if any*/
783         if(disp_refr->prev_scr) {
784             if(top_prev_scr == NULL) top_prev_scr = disp_refr->prev_scr;
785             refr_obj_and_children(draw_ctx, top_prev_scr);
786         }
787 
788         if(top_act_scr == NULL) top_act_scr = disp_refr->act_scr;
789         refr_obj_and_children(draw_ctx, top_act_scr);
790     }
791 
792     /*Also refresh top and sys layer unconditionally*/
793     refr_obj_and_children(draw_ctx, lv_disp_get_layer_top(disp_refr));
794     refr_obj_and_children(draw_ctx, lv_disp_get_layer_sys(disp_refr));
795 
796     draw_buf_flush(disp_refr);
797 }
798 
799 /**
800  * Search the most top object which fully covers an area
801  * @param area_p pointer to an area
802  * @param obj the first object to start the searching (typically a screen)
803  * @return
804  */
lv_refr_get_top_obj(const lv_area_t * area_p,lv_obj_t * obj)805 static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj)
806 {
807     lv_obj_t * found_p = NULL;
808 
809     if(_lv_area_is_in(area_p, &obj->coords, 0) == false) return NULL;
810     if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return NULL;
811     if(_lv_obj_get_layer_type(obj) != LV_LAYER_TYPE_NONE) return NULL;
812 
813     /*If this object is fully cover the draw area then check the children too*/
814     lv_cover_check_info_t info;
815     info.res = LV_COVER_RES_COVER;
816     info.area = area_p;
817     lv_event_send(obj, LV_EVENT_COVER_CHECK, &info);
818     if(info.res == LV_COVER_RES_MASKED) return NULL;
819 
820     int32_t i;
821     int32_t child_cnt = lv_obj_get_child_cnt(obj);
822     for(i = child_cnt - 1; i >= 0; i--) {
823         lv_obj_t * child = obj->spec_attr->children[i];
824         found_p = lv_refr_get_top_obj(area_p, child);
825 
826         /*If a children is ok then break*/
827         if(found_p != NULL) {
828             break;
829         }
830     }
831 
832     /*If no better children use this object*/
833     if(found_p == NULL && info.res == LV_COVER_RES_COVER) {
834         found_p = obj;
835     }
836 
837     return found_p;
838 }
839 
840 /**
841  * Make the refreshing from an object. Draw all its children and the youngers too.
842  * @param top_p pointer to an objects. Start the drawing from it.
843  * @param mask_p pointer to an area, the objects will be drawn only here
844  */
refr_obj_and_children(lv_draw_ctx_t * draw_ctx,lv_obj_t * top_obj)845 static void refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_obj)
846 {
847     /*Normally always will be a top_obj (at least the screen)
848      *but in special cases (e.g. if the screen has alpha) it won't.
849      *In this case use the screen directly*/
850     if(top_obj == NULL) top_obj = lv_disp_get_scr_act(disp_refr);
851     if(top_obj == NULL) return;  /*Shouldn't happen*/
852 
853     /*Refresh the top object and its children*/
854     refr_obj(draw_ctx, top_obj);
855 
856     /*Draw the 'younger' sibling objects because they can be on top_obj*/
857     lv_obj_t * parent;
858     lv_obj_t * border_p = top_obj;
859 
860     parent = lv_obj_get_parent(top_obj);
861 
862     /*Do until not reach the screen*/
863     while(parent != NULL) {
864         bool go = false;
865         uint32_t i;
866         uint32_t child_cnt = lv_obj_get_child_cnt(parent);
867         for(i = 0; i < child_cnt; i++) {
868             lv_obj_t * child = parent->spec_attr->children[i];
869             if(!go) {
870                 if(child == border_p) go = true;
871             }
872             else {
873                 /*Refresh the objects*/
874                 refr_obj(draw_ctx, child);
875             }
876         }
877 
878         /*Call the post draw draw function of the parents of the to object*/
879         lv_event_send(parent, LV_EVENT_DRAW_POST_BEGIN, (void *)draw_ctx);
880         lv_event_send(parent, LV_EVENT_DRAW_POST, (void *)draw_ctx);
881         lv_event_send(parent, LV_EVENT_DRAW_POST_END, (void *)draw_ctx);
882 
883         /*The new border will be the last parents,
884          *so the 'younger' brothers of parent will be refreshed*/
885         border_p = parent;
886         /*Go a level deeper*/
887         parent = lv_obj_get_parent(parent);
888     }
889 }
890 
layer_get_area(lv_draw_ctx_t * draw_ctx,lv_obj_t * obj,lv_layer_type_t layer_type,lv_area_t * layer_area_out)891 static lv_res_t layer_get_area(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj, lv_layer_type_t layer_type,
892                                lv_area_t * layer_area_out)
893 {
894     lv_coord_t ext_draw_size = _lv_obj_get_ext_draw_size(obj);
895     lv_area_t obj_coords_ext;
896     lv_obj_get_coords(obj, &obj_coords_ext);
897     lv_area_increase(&obj_coords_ext, ext_draw_size, ext_draw_size);
898 
899     if(layer_type == LV_LAYER_TYPE_TRANSFORM) {
900         /*Get the transformed area and clip it to the current clip area.
901          *This area needs to be updated on the screen.*/
902         lv_area_t clip_coords_for_obj;
903         lv_area_t tranf_coords = obj_coords_ext;
904         lv_obj_get_transformed_area(obj, &tranf_coords, false, false);
905         if(!_lv_area_intersect(&clip_coords_for_obj, draw_ctx->clip_area, &tranf_coords)) {
906             return LV_RES_INV;
907         }
908 
909         /*Transform back (inverse) the transformed area.
910          *It will tell which area of the non-transformed widget needs to be redrawn
911          *in order to cover transformed area after transformation.*/
912         lv_area_t inverse_clip_coords_for_obj = clip_coords_for_obj;
913         lv_obj_get_transformed_area(obj, &inverse_clip_coords_for_obj, false, true);
914         if(!_lv_area_intersect(&inverse_clip_coords_for_obj, &inverse_clip_coords_for_obj, &obj_coords_ext)) {
915             return LV_RES_INV;
916         }
917 
918         *layer_area_out = inverse_clip_coords_for_obj;
919     }
920     else if(layer_type == LV_LAYER_TYPE_SIMPLE) {
921         lv_area_t clip_coords_for_obj;
922         if(!_lv_area_intersect(&clip_coords_for_obj, draw_ctx->clip_area, &obj_coords_ext)) {
923             return LV_RES_INV;
924         }
925         *layer_area_out = clip_coords_for_obj;
926     }
927     else {
928         LV_LOG_WARN("Unhandled intermediate layer type");
929         return LV_RES_INV;
930     }
931 
932     return LV_RES_OK;
933 }
934 
layer_alpha_test(lv_obj_t * obj,lv_draw_ctx_t * draw_ctx,lv_draw_layer_ctx_t * layer_ctx,lv_draw_layer_flags_t flags)935 static void layer_alpha_test(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx,
936                              lv_draw_layer_flags_t flags)
937 {
938     bool has_alpha;
939     /*If globally the layer has alpha maybe this smaller section has not (e.g. not on a rounded corner)
940      *If turns out that this section has no alpha renderer can choose faster algorithms*/
941     if(flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA) {
942         /*Test for alpha by assuming there is no alpha. If it fails, fall back to rendering with alpha*/
943         has_alpha = true;
944         if(_lv_area_is_in(&layer_ctx->area_act, &obj->coords, 0)) {
945             lv_cover_check_info_t info;
946             info.res = LV_COVER_RES_COVER;
947             info.area = &layer_ctx->area_act;
948             lv_event_send(obj, LV_EVENT_COVER_CHECK, &info);
949             if(info.res == LV_COVER_RES_COVER) has_alpha = false;
950         }
951 
952         if(has_alpha) {
953             layer_ctx->area_act.y2 = layer_ctx->area_act.y1 + layer_ctx->max_row_with_alpha - 1;
954         }
955     }
956     else {
957         has_alpha = false;
958     }
959 
960     if(layer_ctx->area_act.y2 > layer_ctx->area_full.y2) layer_ctx->area_act.y2 = layer_ctx->area_full.y2;
961     lv_draw_layer_adjust(draw_ctx, layer_ctx, has_alpha ? LV_DRAW_LAYER_FLAG_HAS_ALPHA : LV_DRAW_LAYER_FLAG_NONE);
962 }
963 
refr_obj(lv_draw_ctx_t * draw_ctx,lv_obj_t * obj)964 void refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj)
965 {
966     /*Do not refresh hidden objects*/
967     if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return;
968     lv_layer_type_t layer_type = _lv_obj_get_layer_type(obj);
969     if(layer_type == LV_LAYER_TYPE_NONE) {
970         lv_obj_redraw(draw_ctx, obj);
971     }
972     else {
973         lv_opa_t opa = lv_obj_get_style_opa_layered(obj, 0);
974         if(opa < LV_OPA_MIN) return;
975 
976         lv_area_t layer_area_full;
977         lv_res_t res = layer_get_area(draw_ctx, obj, layer_type, &layer_area_full);
978         if(res != LV_RES_OK) return;
979 
980         lv_draw_layer_flags_t flags = LV_DRAW_LAYER_FLAG_HAS_ALPHA;
981 
982         if(_lv_area_is_in(&layer_area_full, &obj->coords, 0)) {
983             lv_cover_check_info_t info;
984             info.res = LV_COVER_RES_COVER;
985             info.area = &layer_area_full;
986             lv_event_send(obj, LV_EVENT_COVER_CHECK, &info);
987             if(info.res == LV_COVER_RES_COVER) flags &= ~LV_DRAW_LAYER_FLAG_HAS_ALPHA;
988         }
989 
990         if(layer_type == LV_LAYER_TYPE_SIMPLE) flags |= LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE;
991 
992         lv_draw_layer_ctx_t * layer_ctx = lv_draw_layer_create(draw_ctx, &layer_area_full, flags);
993         if(layer_ctx == NULL) {
994             LV_LOG_WARN("Couldn't create a new layer context");
995             return;
996         }
997         lv_point_t pivot = {
998             .x = lv_obj_get_style_transform_pivot_x(obj, 0),
999             .y = lv_obj_get_style_transform_pivot_y(obj, 0)
1000         };
1001 
1002         if(LV_COORD_IS_PCT(pivot.x)) {
1003             pivot.x = (LV_COORD_GET_PCT(pivot.x) * lv_area_get_width(&obj->coords)) / 100;
1004         }
1005         if(LV_COORD_IS_PCT(pivot.y)) {
1006             pivot.y = (LV_COORD_GET_PCT(pivot.y) * lv_area_get_height(&obj->coords)) / 100;
1007         }
1008 
1009         lv_draw_img_dsc_t draw_dsc;
1010         lv_draw_img_dsc_init(&draw_dsc);
1011         draw_dsc.opa = opa;
1012         draw_dsc.angle = lv_obj_get_style_transform_angle(obj, 0);
1013         if(draw_dsc.angle > 3600) draw_dsc.angle -= 3600;
1014         else if(draw_dsc.angle < 0) draw_dsc.angle += 3600;
1015 
1016         draw_dsc.zoom = lv_obj_get_style_transform_zoom(obj, 0);
1017         draw_dsc.blend_mode = lv_obj_get_style_blend_mode(obj, 0);
1018         draw_dsc.antialias = disp_refr->driver->antialiasing;
1019 
1020         if(flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) {
1021             layer_ctx->area_act = layer_ctx->area_full;
1022             layer_ctx->area_act.y2 = layer_ctx->area_act.y1 + layer_ctx->max_row_with_no_alpha - 1;
1023             if(layer_ctx->area_act.y2 > layer_ctx->area_full.y2) layer_ctx->area_act.y2 = layer_ctx->area_full.y2;
1024         }
1025 
1026         while(layer_ctx->area_act.y1 <= layer_area_full.y2) {
1027             if(flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) {
1028                 layer_alpha_test(obj, draw_ctx, layer_ctx, flags);
1029             }
1030 
1031             lv_obj_redraw(draw_ctx, obj);
1032 
1033             draw_dsc.pivot.x = obj->coords.x1 + pivot.x - draw_ctx->buf_area->x1;
1034             draw_dsc.pivot.y = obj->coords.y1 + pivot.y - draw_ctx->buf_area->y1;
1035 
1036             /*With LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE it should also go the next chunk*/
1037             lv_draw_layer_blend(draw_ctx, layer_ctx, &draw_dsc);
1038 
1039             if((flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) == 0) break;
1040 
1041             layer_ctx->area_act.y1 = layer_ctx->area_act.y2 + 1;
1042             layer_ctx->area_act.y2 = layer_ctx->area_act.y1 + layer_ctx->max_row_with_no_alpha - 1;
1043         }
1044 
1045         lv_draw_layer_destroy(draw_ctx, layer_ctx);
1046     }
1047 }
1048 
get_max_row(lv_disp_t * disp,lv_coord_t area_w,lv_coord_t area_h)1049 static uint32_t get_max_row(lv_disp_t * disp, lv_coord_t area_w, lv_coord_t area_h)
1050 {
1051     int32_t max_row = (uint32_t)disp->driver->draw_buf->size / area_w;
1052 
1053     if(max_row > area_h) max_row = area_h;
1054 
1055     /*Round down the lines of draw_buf if rounding is added*/
1056     if(disp_refr->driver->rounder_cb) {
1057         lv_area_t tmp;
1058         tmp.x1 = 0;
1059         tmp.x2 = 0;
1060         tmp.y1 = 0;
1061 
1062         lv_coord_t h_tmp = max_row;
1063         do {
1064             tmp.y2 = h_tmp - 1;
1065             disp_refr->driver->rounder_cb(disp_refr->driver, &tmp);
1066 
1067             /*If this height fits into `max_row` then fine*/
1068             if(lv_area_get_height(&tmp) <= max_row) break;
1069 
1070             /*Decrement the height of the area until it fits into `max_row` after rounding*/
1071             h_tmp--;
1072         } while(h_tmp > 0);
1073 
1074         if(h_tmp <= 0) {
1075             LV_LOG_WARN("Can't set draw_buf height using the round function. (Wrong round_cb or to "
1076                         "small draw_buf)");
1077             return 0;
1078         }
1079         else {
1080             max_row = tmp.y2 + 1;
1081         }
1082     }
1083 
1084     return max_row;
1085 }
1086 
draw_buf_rotate_180(lv_disp_drv_t * drv,lv_area_t * area,lv_color_t * color_p)1087 static void draw_buf_rotate_180(lv_disp_drv_t * drv, lv_area_t * area, lv_color_t * color_p)
1088 {
1089     lv_coord_t area_w = lv_area_get_width(area);
1090     lv_coord_t area_h = lv_area_get_height(area);
1091     uint32_t total = area_w * area_h;
1092     /*Swap the beginning and end values*/
1093     lv_color_t tmp;
1094     uint32_t i = total - 1, j = 0;
1095     while(i > j) {
1096         tmp = color_p[i];
1097         color_p[i] = color_p[j];
1098         color_p[j] = tmp;
1099         i--;
1100         j++;
1101     }
1102     lv_coord_t tmp_coord;
1103     tmp_coord = area->y2;
1104     area->y2 = drv->ver_res - area->y1 - 1;
1105     area->y1 = drv->ver_res - tmp_coord - 1;
1106     tmp_coord = area->x2;
1107     area->x2 = drv->hor_res - area->x1 - 1;
1108     area->x1 = drv->hor_res - tmp_coord - 1;
1109 }
1110 
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)1111 static void LV_ATTRIBUTE_FAST_MEM draw_buf_rotate_90(bool invert_i, lv_coord_t area_w, lv_coord_t area_h,
1112                                                      lv_color_t * orig_color_p, lv_color_t * rot_buf)
1113 {
1114 
1115     uint32_t invert = (area_w * area_h) - 1;
1116     uint32_t initial_i = ((area_w - 1) * area_h);
1117     for(lv_coord_t y = 0; y < area_h; y++) {
1118         uint32_t i = initial_i + y;
1119         if(invert_i)
1120             i = invert - i;
1121         for(lv_coord_t x = 0; x < area_w; x++) {
1122             rot_buf[i] = *(orig_color_p++);
1123             if(invert_i)
1124                 i += area_h;
1125             else
1126                 i -= area_h;
1127         }
1128     }
1129 }
1130 
1131 /**
1132  * Helper function for draw_buf_rotate_90_sqr. Given a list of four numbers, rotate the entire list to the left.
1133  */
draw_buf_rotate4(lv_color_t * a,lv_color_t * b,lv_color_t * c,lv_color_t * d)1134 static inline void draw_buf_rotate4(lv_color_t * a, lv_color_t * b, lv_color_t * c, lv_color_t * d)
1135 {
1136     lv_color_t tmp;
1137     tmp = *a;
1138     *a = *b;
1139     *b = *c;
1140     *c = *d;
1141     *d = tmp;
1142 }
1143 
1144 /**
1145  * Rotate a square image 90/270 degrees in place.
1146  * @note inspired by https://stackoverflow.com/a/43694906
1147  */
draw_buf_rotate_90_sqr(bool is_270,lv_coord_t w,lv_color_t * color_p)1148 static void draw_buf_rotate_90_sqr(bool is_270, lv_coord_t w, lv_color_t * color_p)
1149 {
1150     for(lv_coord_t i = 0; i < w / 2; i++) {
1151         for(lv_coord_t j = 0; j < (w + 1) / 2; j++) {
1152             lv_coord_t inv_i = (w - 1) - i;
1153             lv_coord_t inv_j = (w - 1) - j;
1154             if(is_270) {
1155                 draw_buf_rotate4(
1156                     &color_p[i * w + j],
1157                     &color_p[inv_j * w + i],
1158                     &color_p[inv_i * w + inv_j],
1159                     &color_p[j * w + inv_i]
1160                 );
1161             }
1162             else {
1163                 draw_buf_rotate4(
1164                     &color_p[i * w + j],
1165                     &color_p[j * w + inv_i],
1166                     &color_p[inv_i * w + inv_j],
1167                     &color_p[inv_j * w + i]
1168                 );
1169             }
1170 
1171         }
1172     }
1173 }
1174 
1175 /**
1176  * Rotate the draw_buf to the display's native orientation.
1177  */
draw_buf_rotate(lv_area_t * area,lv_color_t * color_p)1178 static void draw_buf_rotate(lv_area_t * area, lv_color_t * color_p)
1179 {
1180     lv_disp_drv_t * drv = disp_refr->driver;
1181     if(disp_refr->driver->full_refresh && drv->sw_rotate) {
1182         LV_LOG_ERROR("cannot rotate a full refreshed display!");
1183         return;
1184     }
1185     if(drv->rotated == LV_DISP_ROT_180) {
1186         draw_buf_rotate_180(drv, area, color_p);
1187         call_flush_cb(drv, area, color_p);
1188     }
1189     else if(drv->rotated == LV_DISP_ROT_90 || drv->rotated == LV_DISP_ROT_270) {
1190         /*Allocate a temporary buffer to store rotated image*/
1191         lv_color_t * rot_buf = NULL;
1192         lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
1193         lv_coord_t area_w = lv_area_get_width(area);
1194         lv_coord_t area_h = lv_area_get_height(area);
1195         /*Determine the maximum number of rows that can be rotated at a time*/
1196         lv_coord_t max_row = LV_MIN((lv_coord_t)((LV_DISP_ROT_MAX_BUF / sizeof(lv_color_t)) / area_w), area_h);
1197         lv_coord_t init_y_off;
1198         init_y_off = area->y1;
1199         if(drv->rotated == LV_DISP_ROT_90) {
1200             area->y2 = drv->ver_res - area->x1 - 1;
1201             area->y1 = area->y2 - area_w + 1;
1202         }
1203         else {
1204             area->y1 = area->x1;
1205             area->y2 = area->y1 + area_w - 1;
1206         }
1207 
1208         /*Rotate the screen in chunks, flushing after each one*/
1209         lv_coord_t row = 0;
1210         while(row < area_h) {
1211             lv_coord_t height = LV_MIN(max_row, area_h - row);
1212             draw_buf->flushing = 1;
1213             if((row == 0) && (area_h >= area_w)) {
1214                 /*Rotate the initial area as a square*/
1215                 height = area_w;
1216                 draw_buf_rotate_90_sqr(drv->rotated == LV_DISP_ROT_270, area_w, color_p);
1217                 if(drv->rotated == LV_DISP_ROT_90) {
1218                     area->x1 = init_y_off;
1219                     area->x2 = init_y_off + area_w - 1;
1220                 }
1221                 else {
1222                     area->x2 = drv->hor_res - 1 - init_y_off;
1223                     area->x1 = area->x2 - area_w + 1;
1224                 }
1225             }
1226             else {
1227                 /*Rotate other areas using a maximum buffer size*/
1228                 if(rot_buf == NULL) rot_buf = lv_mem_buf_get(LV_DISP_ROT_MAX_BUF);
1229                 draw_buf_rotate_90(drv->rotated == LV_DISP_ROT_270, area_w, height, color_p, rot_buf);
1230 
1231                 if(drv->rotated == LV_DISP_ROT_90) {
1232                     area->x1 = init_y_off + row;
1233                     area->x2 = init_y_off + row + height - 1;
1234                 }
1235                 else {
1236                     area->x2 = drv->hor_res - 1 - init_y_off - row;
1237                     area->x1 = area->x2 - height + 1;
1238                 }
1239             }
1240 
1241             /* The original part (chunk of the current area) were split into more parts here.
1242              * Set the original last_part flag on the last part of rotation. */
1243             if(row + height >= area_h && draw_buf->last_area && draw_buf->last_part) {
1244                 draw_buf->flushing_last = 1;
1245             }
1246             else {
1247                 draw_buf->flushing_last = 0;
1248             }
1249 
1250             /*Flush the completed area to the display*/
1251             call_flush_cb(drv, area, rot_buf == NULL ? color_p : rot_buf);
1252             /*FIXME: Rotation forces legacy behavior where rendering and flushing are done serially*/
1253             while(draw_buf->flushing) {
1254                 if(drv->wait_cb) drv->wait_cb(drv);
1255             }
1256             color_p += area_w * height;
1257             row += height;
1258         }
1259         /*Free the allocated buffer at the end if necessary*/
1260         if(rot_buf != NULL) lv_mem_buf_release(rot_buf);
1261     }
1262 }
1263 
1264 /**
1265  * Flush the content of the draw buffer
1266  */
draw_buf_flush(lv_disp_t * disp)1267 static void draw_buf_flush(lv_disp_t * disp)
1268 {
1269     lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
1270 
1271     /*Flush the rendered content to the display*/
1272     lv_draw_ctx_t * draw_ctx = disp->driver->draw_ctx;
1273     if(draw_ctx->wait_for_finish) draw_ctx->wait_for_finish(draw_ctx);
1274 
1275     /* In partial double buffered mode wait until the other buffer is freed
1276      * and driver is ready to receive the new buffer */
1277     bool full_sized = draw_buf->size == (uint32_t)disp_refr->driver->hor_res * disp_refr->driver->ver_res;
1278     if(draw_buf->buf1 && draw_buf->buf2 && !full_sized) {
1279         while(draw_buf->flushing) {
1280             if(disp_refr->driver->wait_cb) disp_refr->driver->wait_cb(disp_refr->driver);
1281         }
1282     }
1283 
1284     draw_buf->flushing = 1;
1285 
1286     if(disp_refr->driver->draw_buf->last_area && disp_refr->driver->draw_buf->last_part) draw_buf->flushing_last = 1;
1287     else draw_buf->flushing_last = 0;
1288 
1289     bool flushing_last = draw_buf->flushing_last;
1290 
1291     if(disp->driver->flush_cb) {
1292         /*Rotate the buffer to the display's native orientation if necessary*/
1293         if(disp->driver->rotated != LV_DISP_ROT_NONE && disp->driver->sw_rotate) {
1294             draw_buf_rotate(draw_ctx->buf_area, draw_ctx->buf);
1295         }
1296         else {
1297             call_flush_cb(disp->driver, draw_ctx->buf_area, draw_ctx->buf);
1298         }
1299     }
1300 
1301     /*If there are 2 buffers swap them. With direct mode swap only on the last area*/
1302     if(draw_buf->buf1 && draw_buf->buf2 && (!disp->driver->direct_mode || flushing_last)) {
1303         if(draw_buf->buf_act == draw_buf->buf1)
1304             draw_buf->buf_act = draw_buf->buf2;
1305         else
1306             draw_buf->buf_act = draw_buf->buf1;
1307     }
1308 }
1309 
call_flush_cb(lv_disp_drv_t * drv,const lv_area_t * area,lv_color_t * color_p)1310 static void call_flush_cb(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p)
1311 {
1312     REFR_TRACE("Calling flush_cb on (%d;%d)(%d;%d) area with %p image pointer", area->x1, area->y1, area->x2, area->y2,
1313                (void *)color_p);
1314 
1315     lv_area_t offset_area = {
1316         .x1 = area->x1 + drv->offset_x,
1317         .y1 = area->y1 + drv->offset_y,
1318         .x2 = area->x2 + drv->offset_x,
1319         .y2 = area->y2 + drv->offset_y
1320     };
1321 
1322     drv->flush_cb(drv, &offset_area, color_p);
1323 }
1324 
1325 #if LV_USE_PERF_MONITOR
perf_monitor_init(perf_monitor_t * _perf_monitor)1326 static void perf_monitor_init(perf_monitor_t * _perf_monitor)
1327 {
1328     LV_ASSERT_NULL(_perf_monitor);
1329     _perf_monitor->elaps_sum = 0;
1330     _perf_monitor->fps_sum_all = 0;
1331     _perf_monitor->fps_sum_cnt = 0;
1332     _perf_monitor->frame_cnt = 0;
1333     _perf_monitor->perf_last_time = 0;
1334     _perf_monitor->perf_label = NULL;
1335 }
1336 #endif
1337 
1338 #if LV_USE_MEM_MONITOR
mem_monitor_init(mem_monitor_t * _mem_monitor)1339 static void mem_monitor_init(mem_monitor_t * _mem_monitor)
1340 {
1341     LV_ASSERT_NULL(_mem_monitor);
1342     _mem_monitor->mem_last_time = 0;
1343     _mem_monitor->mem_label = NULL;
1344 }
1345 #endif
1346