一葡盗,前言
之前的4篇littlevgl源碼分析已經(jīng)完成一個(gè)按鈕的顯示的流程的分析。但是littlevgl要比我之前看的5K行的guilite做的控件要漂亮啡浊,比如這個(gè)按鈕觅够,他有各種樣式,包括圓角矩形按鈕巷嚣,因?yàn)槲铱戳讼耮uilite源碼好像是不支持繪制圓角矩形的喘先。之前是分析框架,今天則分析細(xì)節(jié)的特色功能廷粒。
二窘拯,分析lvgl是如何繪制帶圓角的矩形
首先我要提下mask的概念,在一副圖片上坝茎,加上100%的mask遮蓋后涤姊,你再給他繪制原色,那么遮蓋的部分是不會(huì)改變顏色的景东。而把mask的概念理解為透明度疊加也可以砂轻。2個(gè)顏色若是透明的,那么就是2個(gè)顏色的rgb相加斤吐,否則就是后面繪制的顏色。比如先畫(huà)藍(lán)色厨喂,后畫(huà)白色和措,不透明的話就是白色,若是透明的話蜕煌,加入白色后的效果就是藍(lán)色變淡藍(lán)色派阱。
好了,那么lvgl中若顏色完全覆蓋斜纪,則用0xff來(lái)mask這個(gè)像素點(diǎn)贫母,若完全透明就是0文兑,在0~0xff間的就代表存在不同層度的透明度。而繪制圓角矩形的原理就是利用圓角mask腺劣。
之前學(xué)習(xí)游戲中的剛體碰撞了解過(guò)圓形和矩形碰撞绿贞,可以計(jì)算圓點(diǎn)到矩形邊界的距離。大于r則無(wú)碰撞橘原,否則有碰撞籍铁。那么要構(gòu)造一個(gè)圓角矩陣,也是可以理解為每個(gè)圓角都是一個(gè)圓趾断,見(jiàn)下圖拒名,那么要把下圖的4個(gè)白色區(qū)間為透明,其它涂色芋酌,則變成了一個(gè)圓角矩形增显。
圓角矩形繪制的方法就是描點(diǎn)法。來(lái)看代碼脐帝,要繪制矩形甸怕,就是一行行繪制。然后每行設(shè)置mask腮恩,當(dāng)mask為0則為透明梢杭,就等于上圖的白色區(qū)間。我現(xiàn)在將源碼修改了下秸滴,屏蔽了繪制矩形輪廓等武契,僅繪制矩形背景,背景色為白色荡含。此時(shí)周期刷新函數(shù)會(huì)調(diào)用draw_bg函數(shù)進(jìn)行繪制咒唆。
LV_ATTRIBUTE_FAST_MEM static void draw_bg(const lv_area_t * coords, const lv_area_t * clip,
const lv_draw_rect_dsc_t * dsc)
{
。释液。全释。。误债。浸船。
// mask數(shù)據(jù)準(zhǔn)備,為每一行準(zhǔn)備mask
for(h = draw_area.y1; h <= draw_area.y2; h++) {
int32_t y = h + vdb->area.y1;
opa2 = opa;
/*In not corner areas apply the mask only if required*/
if(y > coords_bg.y1 + rout + 1 &&
y < coords_bg.y2 - rout - 1) {
mask_res = LV_DRAW_MASK_RES_FULL_COVER;
if(simple_mode == false) {
_lv_memset(mask_buf, opa, draw_area_w);
mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w);
}
}
/*In corner areas apply the mask anyway*/
// 若有圓角寝蹈,色申請(qǐng)mask空間李命。mask空間為這一行的長(zhǎng)度
else {
_lv_memset(mask_buf, opa, draw_area_w);
mask_res = lv_draw_mask_apply(mask_buf, vdb->area.x1 + draw_area.x1, vdb->area.y1 + h, draw_area_w);
}
。箫老。封字。。。阔籽。
_lv_blend_fill(xxx) //rgb數(shù)據(jù)混合及渲染
}
然后看看lv_draw_mask_apply算法的使用流妻,它會(huì)調(diào)用res = dsc->cb(mask_buf, abs_x, abs_y, len, (void *)m->param);
等于進(jìn)入lv_draw_mask_radius函數(shù)。lv_draw_mask_radius里面做的事情就是先計(jì)算點(diǎn)的坐標(biāo)笆制,通過(guò)半徑及這一個(gè)行y和y-1行的坐標(biāo)計(jì)算x和x-1的坐標(biāo)绅这。如下
y = radius - (h - abs_y) + 1;
/* Get the x intersection points for `abs_y` and `abs_y-1`
* Use the circle's equation x = sqrt(r^2 - y^2)
* Try to use the values from the previous run*/
if((y - 1) == p->y_prev) {
x1.f = p->y_prev_x.f;
x1.i = p->y_prev_x.i;
}
else {
_lv_sqrt(r2 - ((y - 1) * (y - 1)), &x1, sqrt_mask);
}
_lv_sqrt(r2 - (y * y), &x0, sqrt_mask);
p->y_prev = y;
p->y_prev_x.f = x0.f;
p->y_prev_x.i = x0.i;
然后為x和x-1的坐標(biāo)添加mask漸變過(guò)濾。Kl和Kr就是左邊和右邊的像素位置项贺,所以用mask_buf[kl]和mask_buf[kr]來(lái)表示君躺,mask填充的值為m。
/*Set all points which are crossed by the circle*/
for(; i <= x1.i; i++) {
/* These values are very close to each other. It's enough to approximate sqrt
* The non-approximated version is lv_sqrt(r2 - (i * i), &y_next, sqrt_mask); */
sqrt_approx(&y_next, &y_prev, r2 - (i * i));
m = (y_prev.f + y_next.f) >> 1;
if(outer) m = 255 - m;
if(kl >= 0 && kl < len) mask_buf[kl] = mask_mix(mask_buf[kl], m);
if(kr >= 0 && kr < len) mask_buf[kr] = mask_mix(mask_buf[kr], m);
kl--;
kr++;
y_prev.f = y_next.f;
}
調(diào)試截圖如下开缎,x-1和x相差6棕叫。
然后除了2邊對(duì)稱需要設(shè)置mask,中間點(diǎn)也要設(shè)置mask奕删。
if(y_prev.f) {
m = (y_prev.f * x1.f) >> 9;
if(outer) m = 255 - m;
if(kl >= 0 && kl < len) mask_buf[kl] = mask_mix(mask_buf[kl], m);
if(kr >= 0 && kr < len) mask_buf[kr] = mask_mix(mask_buf[kr], m);
kl--;
kr++;
}
最后再設(shè)置x和x-1沒(méi)有交叉的部分俺泣,直接設(shè)置為0。
if(outer == 0) {
kl++;
if(kl > len) {
return LV_DRAW_MASK_RES_TRANSP;
}
if(kl >= 0) _lv_memset_00(&mask_buf[0], kl);
if(kr < 0) {
return LV_DRAW_MASK_RES_TRANSP;
}
if(kr < len) _lv_memset_00(&mask_buf[kr], len - kr);
}
交叉的部分可以理解為是要描點(diǎn)繪制曲線的完残,所以用了漸變算法伏钠,否則的話會(huì)看到明顯的鋸齒。outer應(yīng)該理解為是繪制里面或者繪制外部吧谨设!見(jiàn)下圖熟掂,對(duì)每一行矩形其實(shí)都是這樣kl和kr的對(duì)稱mask賦值。分為3個(gè)階段賦值扎拣,一個(gè)是曲線段赴肚,一個(gè)是中間點(diǎn),最后是曲線外二蓝。
我把在繪圖板中畫(huà)的一個(gè)圓誉券,進(jìn)行了5被擴(kuò)大。讓大家可以理解下描點(diǎn)與鋸齒刊愚。下圖第一行y和下一行y-1對(duì)應(yīng)的的x和x-1也差距6踊跟。看坐標(biāo)一個(gè)是30鸥诽,一個(gè)是24商玫。
我把lvgl繪制出的按鈕背景的左上角矩形圓角截圖,然后把截圖放大8倍衙传,也可以看到漸變的鋸齒效果决帖。其實(shí)就是這段代碼的作用啦~標(biāo)準(zhǔn)尺寸的時(shí)候看上去就很平滑了。
三蓖捶,總結(jié)
主要了解了下圓角矩形的繪制思路,簡(jiǎn)單來(lái)說(shuō)就是用行掃描法加上形狀mask扁远,用漸變的mask值讓描點(diǎn)的曲線效果更平滑俊鱼。