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