顯示器上與數學上直線定義的區別:
數學上的直線是沒有寬度、由無數個點構成的集合,顯然,光柵顯示器只能近地似顯示直線.當我們對直線進行光柵化時,需要在顯示器有限個象素中,確定
最佳逼近
該直線的一組象素,並且按掃瞄線順序,對這些象素進行寫操作,這個過程稱為用顯示器繪製直線或直線的掃瞄轉換.
由於在乙個圖形中,可能包含成千上萬條直線,所以要求繪製演算法應盡可能地快.本節我們介紹乙個象素寬直線繪製的
三個常用演算法:數值微分法(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年...