acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_refr.c (35915B)

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