lvgl源碼分析5(圓角矩形繪制算法)--Apple的學(xué)習(xí)筆記

一葡盗,前言

之前的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è)圓角矩形增显。


image.png

圓角矩形繪制的方法就是描點(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棕叫。


image.png

然后除了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),最后是曲線外二蓝。


image.png

我把在繪圖板中畫(huà)的一個(gè)圓誉券,進(jìn)行了5被擴(kuò)大。讓大家可以理解下描點(diǎn)與鋸齒刊愚。下圖第一行y和下一行y-1對(duì)應(yīng)的的x和x-1也差距6踊跟。看坐標(biāo)一個(gè)是30鸥诽,一個(gè)是24商玫。


image.png

我把lvgl繪制出的按鈕背景的左上角矩形圓角截圖,然后把截圖放大8倍衙传,也可以看到漸變的鋸齒效果决帖。其實(shí)就是這段代碼的作用啦~標(biāo)準(zhǔn)尺寸的時(shí)候看上去就很平滑了。
image.png

三蓖捶,總結(jié)

主要了解了下圓角矩形的繪制思路,簡(jiǎn)單來(lái)說(shuō)就是用行掃描法加上形狀mask扁远,用漸變的mask值讓描點(diǎn)的曲線效果更平滑俊鱼。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末刻像,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子并闲,更是在濱河造成了極大的恐慌细睡,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件帝火,死亡現(xiàn)場(chǎng)離奇詭異溜徙,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)犀填,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)蠢壹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人九巡,你說(shuō)我怎么就攤上這事图贸。” “怎么了冕广?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵疏日,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我撒汉,道長(zhǎng)沟优,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任睬辐,我火速辦了婚禮挠阁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好裙戏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布两蟀。 她就那樣靜靜地躺著,像睡著了一般杖剪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天洪橘,我揣著相機(jī)與錄音,去河邊找鬼棵帽。 笑死熄求,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的逗概。 我是一名探鬼主播弟晚,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了卿城?” 一聲冷哼從身側(cè)響起枚钓,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瑟押,沒(méi)想到半個(gè)月后搀捷,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡多望,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年嫩舟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怀偷。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡家厌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出枢纠,到底是詐尸還是另有隱情像街,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布晋渺,位于F島的核電站镰绎,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏木西。R本人自食惡果不足惜畴栖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望八千。 院中可真熱鬧吗讶,春花似錦、人聲如沸恋捆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沸停。三九已至膜毁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間愤钾,已是汗流浹背瘟滨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留能颁,地道東北人杂瘸。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像伙菊,于是被迫代替她去往敵國(guó)和親败玉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子敌土,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容