littlevgl_7.11源码分析(2)--Apple的学习笔记
⼀,前⾔
接着上⼀篇littlevgl_7.11源码分析(1)--Apple的学习笔记,继续看更新绘图到显⽰的过程吧!就把重点放在接着要执⾏的task任务
_lv_disp_refr_task函数中。不过这个函数源码写的好难看,已经超过50⾏了,不能封装⼀下嘛!我把⾥⾯的⼀些stm32 3D加速的宏开关关闭的内容都删除了,依然那么长。因为看惯优秀代码了,所以⽐较挑剔,哈哈~
⼆,_lv_disp_refr_task绘图源码分析
我理解的⼀些含义都⽤中⽂注释写在了语句的上⽅了。
我先分析的是如下第⼀层的代码。⽽且是我⼀边调试验证⼀边分析的,通过此⽅法来降低难度,并且及时纠正错误的理解。其中⽤双缓冲⽅式进⾏重绘⾥⾯lv_refr_vdb_flush就直接绘制,绘制完就切换framebuffer指针,等到while(vdb->flushing)显⽰更新完成后,再将已经绘制显⽰的内容copy到另外⼀个framebuffer。简单来看双framebuffer的切换⽤法是绘制完成后切换指针,并且copy显⽰内容便于下次更新。但是我之前理解的双缓冲概念是渲染完成后交换指针,然后⼀个⽤于显⽰,⼀个⽤于继续渲染整个
屏幕。这⾥是显⽰结束后交换指针。
感觉哪⾥不对,于是我去官⽹⼜看了下porting,原来它都是⽤的简单的porting填充,所以flush_cb中⽤绘制像素的⽅式⼀点点进⾏绘制,其实按双缓冲的设计思路flush_cb回调函数中⽤户应该不直接进⾏lcd显⽰,通知另外的线程进⾏显⽰,那么就和我之前理解⼀样了,渲染完成后交换指针,原先的已完成渲染则⽤于显⽰,⽽后⾯的⽤于继续渲染。⽽我的stm32是DMA⽅式的lcd显⽰,所以flush_cb会调⽤函数中我问填写的就直接显⽰的函数了。但是其实我验证了下,在flush_cb中添加while(1),lv_task_handler函数就被阻塞了,说明lvgl的flush_cb中若添加了lcd显⽰,那么渲染和lcd是在⼀个线程按顺序来的,双缓存没⽤起来。但是若添加其他task线程就另当别论咯~⽐如⼀个线程渲染完成后在更新显⽰中,另外⼀个线程⼜开始绘图了,此时双缓冲就起到做⽤了。
在此函数中lv_refr_join_area合并区域。⽬的就是可以少绘制些,也算是性能优化的设计,达到同⼀个位置只画⼀次。
lv_refr_areas就有讲究了,⼀开始我没理解,后来看了官⽹说是有3中framebuffer。第⼀种是只有⼀个framebuffer。第⼆种是有双framebuffer,但是⼩于显⽰区域。第三种是有双framebuffer⼤于等于显⽰区域。我上⾯描述的关于双framebuffer就是第3种,⽽
lv_refr_join_area是针对初始化为第⼆种双framebuffer时候,flush_cb直接在lv_refr_join_area中画的,
此
时if(lv_disp_is_true_double_buf(disp_refr))条件不满⾜,所以要么在lv_refr_join_area中更新显⽰,要么在lv_refr_vdb_flush中更新显⽰,这取决于初始化的时候设置的双缓存的⼤⼩符合第⼆类还是第三类。然后lv_refr_areas中除了显⽰,在显⽰前还会填充区域内容。因为昨天分析的⽆效区域待显⽰,这个区域都是rect,⾥⾯没有包括像素内容填充。添加像素内容可以叫做渲染。图像GUI都是先渲染到framebuffer然后显⽰,所以
lv_refr_areas函数很重要。
void _lv_disp_refr_task(lv_task_t * task)
{
LV_LOG_TRACE("lv_refr_task: started");
uint32_t start = lv_tick_get();
uint32_t elaps = 0;
disp_refr = task->ur_data;
问候语有哪些#if LV_USE_PERF_MONITOR == 0
鞋的英文>米糕的家庭的做法
/* Ensure the task does not run again automatically.
* This is done before refreshing in ca refreshing invalidates something el.黑狸猫
*/
lv_task_t_prio(task, LV_TASK_PRIO_OFF);
#endif
/*Do nothing if there is no active screen*/
if(disp_refr->act_scr == NULL) {
disp_refr->inv_p = 0;
return;
}
/
* 连接的区域进⾏拼接 */
lv_refr_join_area();
/* 在区域中重绘,并且⾮真正意义的双缓存则在此函数中直接⼀部分⼀部分的进⾏显⽰ */
工程估价
lv_refr_areas();
/* ⽤双缓冲⽅式进⾏重绘 */
/*If refresh happened ...*/
if(disp_refr->inv_p != 0) {
美图在线/* In true double buffered mode copy the refreshed areas to the new VDB to keep it up to date.
* With t_px_cb we don't know anything about the buffer (even it's size) so skip copying.*/误工证明模板
vdb->area.y2 = lv_disp_get_ver_res(disp_refr) - 1;
disp_refr->driver.buffer->last_part = 1;
/* 1个area就⼀个part,重绘 */
lv_refr_area_part(area_p);
}
/* 进⼊⾮真正意义的双缓存,即第⼆种初始化⽅式对应的的绘制⽅法 */
/*The buffer is smaller: refresh the area in parts*/
el {
lv_disp_buf_t * vdb = lv_disp_get_buf(disp_refr);
/*Calculate the max row num*/
lv_coord_t w = lv_area_get_width(area_p);
lv_coord_t h = lv_area_get_height(area_p);
lv_coord_t y2 =
area_p->y2 >= lv_disp_get_ver_res(disp_refr) ? lv_disp_get_ver_res(disp_refr) - 1 : area_p->y2; /* 进⾏分割,每max_row当做⼀个part进⾏绘制及更新显⽰ */
int32_t max_row = (uint32_t)vdb->size / w;
if(max_row > h) max_row = h;
/* 这个回调函数官⽹port章节中介绍将2*2可以改成2*8,我先不分析 */
/*Round down the lines of VDB if rounding is added*/
if(disp_refr-&under_cb) {
lv_area_t tmp;
tmp.x1 = 0;
tmp.x2 = 0;
tmp.y1 = 0;
lv_coord_t h_tmp = max_row;
do {
tmp.y2 = h_tmp - 1;
disp_refr-&under_cb(&disp_refr->driver, &tmp);
/*If this height fits into `max_row` then fine*/
if(lv_area_get_height(&tmp) <= max_row) break;
/*Decrement the height of the area until it fits into `max_row` after rounding*/
h_tmp--;
} while(h_tmp > 0);
if(h_tmp <= 0) {
LV_LOG_WARN("Can't t VDB height using the round function. (Wrong round_cb or to "
"small VDB)");
return;
}
el {
max_row = tmp.y2 + 1;
证券从业人员}
}
/*Always u the full row*/
lv_coord_t row;
lv_coord_t row_last = 0;
/
* 每隔max_row⾏进⾏区间内绘制 */
for(row = area_p->y1; row + max_row - 1 <= y2; row += max_row) {
/*Calc. the next y coordinates of VDB*/
vdb->area.x1 = area_p->x1;
vdb->area.x2 = area_p->x2;
vdb->area.y1 = row;
vdb->area.y2 = row + max_row - 1;
if(vdb->area.y2 > y2) vdb->area.y2 = y2;
row_last = vdb->area.y2;
/* 若是整除,则标记已完成last_part绘制 */
if(y2 == row_last) disp_refr->driver.buffer->last_part = 1;
/
* 重绘 */
lv_refr_area_part(area_p);
}
/* 不是整除,则将剩余⾏数进⾏绘制,标记已完成last_part */
/*If the last y coordinates are not handled yet ...*/
if(y2 != row_last) {
/*Calc. the next y coordinates of VDB*/
vdb->area.x1 = area_p->x1;
vdb->area.x2 = area_p->x2;
vdb->area.y1 = row;
vdb->area.y2 = y2;