LCD 畫線方法及C語言實現

2021-06-24 18:22:58 字數 3789 閱讀 6733

顯示器上與數學上直線定義的區別:

數學上的直線是沒有寬度、由無數個點構成的集合,顯然,光柵顯示器只能近地似顯示直線.當我們對直線進行光柵化時,需要在顯示器有限個象素中,確定

最佳逼近

該直線的一組象素,並且按掃瞄線順序,對這些象素進行寫操作,這個過程稱為用顯示器繪製直線或直線的掃瞄轉換. 

由於在乙個圖形中,可能包含成千上萬條直線,所以要求繪製演算法應盡可能地快.本節我們介紹乙個象素寬直線繪製的

三個常用演算法:數值微分法(dda)、中點畫線法和bresenham演算法. 

(1)數值微分(dda)法 

設過端點p0(x0 ,y0)、p1(x1 ,y1)的直線段為l(p0 ,p1),則直線段l的斜率為k=(y1-y0)/(x1-x0).要在顯示器顯示l,必須

確定最佳逼近l的畫素集合

.我們從l的起點p0的橫座標x0向l的終點p1的橫座標x1步進,取步長=1(個象素),用l的直線方程y=kx+b計算相應的y座標,並取象素點(x,round(y))作為當前點的座標. 

因為: yi+1 =  kxi+1+b  =  kxi+b+kdx  =  yi+kdx 

所以,當dx =1; yi+1 = yi+k.也就是說,當x每遞增1,y遞增k(即直線斜率).根據這個原理,我們可以寫出dda畫線演算法程式. 

dda畫線演算法程式: 

void ddaline(int x0,int y0,int x1,int y1,int color)  } 

注意:我們這裡用整型變數color表示象素的顏色和灰度. 

舉例:用dda方法掃瞄轉換連線兩點p0(0,0)和p1(5,2)的直線段. 

x int(y+0.5) y+0.5 

0     0        0 

1     0      0.4+0.5 

2     1      0.8+0.5 

3     1      1.2+0.5 

4     2      1.6+0.5 

圖2.1.1 直線段的掃瞄轉換 

注意:上述分析的演算法僅適用於|k| ≤1的情形.在這種情況下,x每增加1,y最多增加1.當 |k| > 1時,必須把x,y地位互換,y每增加1,x相應增加1/k.在這個演算法中,y與k必須用浮點數表示,而且每一步都要對y進行四捨五入後取整,這使得它不利於硬體實現. 

(2)中點畫線法 

假定直線斜率k在0~1之間,當前象素點為(xp,yp),則下乙個象素點有兩種可選擇點p1(xp+1,yp)或 p2(xp+1,yp+1).若p1與p2的中點(xp+1,yp+0.5)稱為m,q為理想直線與x=xp+1垂線的交點.當m在q的下方時,則取p2 應為下乙個象素點;當m在q的上方時,則取p1為下乙個象素點.這就是中點畫線法的基本原理. 

圖2.1.2 中點畫線法每步迭代涉及的象素和中點示意圖 

下面討論中點畫線法的實現.過點(x0,y0)、(x1, y1)的直線段l的方程式為f(x, y)=ax+by+c=0,其中,a=y0-y1, b=x1-x0, c=x0y1-x1y0,欲判斷中點m在q點的上方還是下方,只要把m代入f(x,y),並判斷它的符號即可.為此,我們構造判別式: 

d=f(m)=f(xp+1, yp+0.5)=a(xp+1)+b(yp+0.5)+c 

所以: 

當d<0時,m在l(q點)下方,取p2為下乙個象素; 

當d>0時,m在l(q點)上方,取p1為下乙個象素; 

當d=0時,選p1或p2均可,約定取p1為下乙個象素; 

注意到d是xp, yp的線性函式,可採用增量計算,提高運算效率: 

若當前象素處於d>=0情況,則取正右方象素p1(xp+1, yp),要判下乙個象素位置,應計算 d1=f(xp+2, yp+0.5)=a(xp+2)+b(yp+0.5)=d+a,增量為a. 

若d<0時,則取右上方象素p2(xp+1, yp+1).要判斷再下一象素,則要計算d2= f(xp+2, yp+1.5)=a(xp+2)+b(yp+1.5)+c=d+a+b ,增量為a+b.畫線從(x0, y0)開始,d的初值 d0=f(x0+1, y0+0.5)=f(x0, y0)+a+0.5b,因  f(x0, y0)=0,所以d0=a+0.5b. 

由於我們使用的只是d的符號,而且d的增量都是整數,只是初始值包含小數.因此,我們可以用2d代替d來擺脫小數,寫出僅包含整數運算的演算法程式. 

中點畫線演算法程式: 

void midpoint line (int x0,int y0,int x1, int y1,int color) 

else       

drawpixel (x, y, color); 

} /* while */ 

} /* mid pointline */ 

舉例:用中點畫線方法掃瞄轉換連線兩點p0(0,0)和p1(5,2)的直線段. 

a=y0-y1=-2; b=x1-x0=5; d0=2*a+b=1;d1=2*a=-4;d2=2*(a+b)=6 

x   y   d 

0   0   1 

1   0   -3 

2   1   3 

3   1   -1         

4   2    5                                 

5   2   15 

圖2.1.3 中點畫線法 

(3)bresenham演算法 

bresenham演算法是計算機圖形學領域使用最廣泛的直線掃瞄轉換演算法.仍然假定直線斜率在0~1之間,該方法類似於中點法,由乙個誤差項符號決定下乙個象素點. 

演算法原理如下:過各行各列象素中心構造一組虛擬網格線.按直線從起點到終點的順序計算直線與各垂直網格線的交點,然後確定該列象素中與此交點最近的象素.該演算法的巧妙之處在於採用增量計算,使得對於每一列,只要檢查乙個誤差項的符號,就可以確定該列的所求象素. 

如圖2.1.4所示,設直線方程為yi+1=yi+k(xi+1-xi)+k.假設列座標象素已經確定為xi,其行座標為yi.那麼下乙個象素的列座標為xi+1,而行座標要麼為yi,要麼遞增1為yi+1.是否增1取決於誤差項d的值.誤差項d的初值d0=0,x座標每增加1,d的值相應遞增直線的斜率值k,即d=d+k.一旦  d≥1,就把它減去1,這樣保證d在0、1之間.當d≥0.5時,直線與垂線x=xi+1交點最接近於當前象素(xi,yi)的右上方象素(xi+1,yi+1);而當d<0.5時,更接近於右方象素(xi+1,yi).為方便計算,令e=d-0.5,e的初值為-0.5,增量為k.當e≥0時,取當前象素(xi,yi)的右上方象素(xi+1,yi+1);而當e<0時,取(xi,yi)右方象素(xi+1,yi). 

圖2.1.4 bresenham演算法所用誤差項的幾何含義 

bresenham畫線演算法程式: 

void bresenhamline (int x0,int y0,int x1, int y1,int color)  } 

} 舉例:用bresenham方法掃瞄轉換連線兩點p0(0,0)和p1(5,2)的直線段. 

x   y   e 

0   0   -0.5 

1   0   -0.1 

2   1   -0.7 

3   1   -0.3 

4   2   -0.9 

5   2   -0.5 

圖2.1.5 bresenham演算法 

上述bresenham演算法在計算直線斜率與誤差項時用到小數與除法.可以改用整數以避免除法.由於演算法中只用到誤差項的符號,因此可作如下替換:2*e*dx. 

改進的bresenham畫線演算法程式: 

/*本**並不完善,只是反映了原理,要真正實現任意畫線還需完善*/

void lcd_draw_line(u32 x0,u32 y0,u32 x1, u32 y1,u32 color) }

}

LCD 畫線方法及C語言實現 轉貼

顯示器上與數學上直線定義的區別 數學上的直線是沒有寬度 由無數個點構成的集合,顯然,光柵顯示器只能近地似顯示直線.當我們對直線進行光柵化時,需要在顯示器有限個象素中,確定 最佳逼近 該直線的一組象素,並且按掃瞄線順序,對這些象素進行寫操作,這個過程稱為用顯示器繪製直線或直線的掃瞄轉換.由於在乙個圖形...

快速排序及C語言實現

快速排序演算法最壞複雜度很差,相當於插入排序,但是平均效能很好,甚至大多數時候優於堆排序和歸併排序,並且是一種內排序演算法,因此在實際中往往用的最多。快速排序的步驟 將陣列a p.r 劃分為兩個子陣列a p.q 1 和a q 1.r 使得前者的每個元素小於等於a q 後者的每個元素大於等於a q 然...

獲取時區方法(C語言實現)

本文首先普及一下時區以及各種時間的含義。如果不需普及直接跳到最後的 為獲取時區的c語言 時區咱們一起回憶一下中學的地理知識,地球是自西向東自傳逆時針自傳,自西向東逆時針公轉。所以陽光總是自東向西掃過,也就是我們常說的太陽東昇西落。地球自傳一周的弧度是360度,時間是24小時,所以人類在公元1884年...