一、直線生成基本思路
圖形圖像是由屏幕上不同亮度不同顏色的光點(像素)組成。在光柵顯示器的熒光屏上生成一個對象,實質上是往幀緩存寄存器的相應單元中填入數據。
所以:對直線進行光柵化的時候,只能在顯示器所給定的有限個像素組成的點陣中確定最佳逼近於該直線的一組像素,用這些像素表示該直線。
所以:生成直線的主要工作是:快速找出距離直線最近的網格點,用這些網格點對應的像素表示該直線。
如果我們使用斜截式方程來表示直線,會用到一次乘法和一次 加法,在計算機底層乘法是費時的,那么有什么方法可以改進?
變乘法為加法!
直線是象素集合,生成算法的最終目的,就是為了尋找能更准確逼近直線的象素點。
所以,要確定直線上每個點,那么,如:m<1時,從起始點xs,xs+1, xs+2, xs+3 ……到xe的每個點(xi,yi) ,需要確定其對應的象素值。
所以,每一個(xi,yi)即准確值,都要尋找對應其的象素值(xi,yi,r),即,最接近其准確值的整數值,最簡單-取整 。
假設(xi,yi)已經確定了它的對應整數象素點,則下面就要找個規則確定下一個點即(xi+1, yi+1)的對應象素點。
對應於上述情況,即m<1的情況, xi+1=xi+1,即yi+1需要確定,即需要確定上述說的准則,即給定一個判定式,由判定式來確定yi+1的選擇。
而判定式為了計算方便也可以有更簡便的方式來計算。故判定式也可以由遞推式來確定。
二、基本增量算法(DDA)
基本增量算法也叫數字微分算法(Digital Differential Analyzer)
1、基本思想:
舍入法求解最佳逼近;利用微分思想,即每一個點坐標都可以由前一個坐標變化一個增量得到。
2、直線的表示:
設直線的起點坐標為(xs,ys),終點坐標為(xe, ye),
令Δx= xe- xs, Δy= ye-ys,則直線參數方程為
將參數區間[0,1]分為n等分,即t每次增量為 Δt= 1/n
3、提高速度的方法
乘法用加法實現,每一個點坐標都可以由前一個坐標變化一個增量得到。
設(xi,yi)是第i 步得的直線上的點,則直線上第個i+1 點是(xi+1,yi+1),其中
該算法在x或y變化比較大的方向的增量絕對值為1,而另一方向上的增量絕對值小於等於1。
當Δx> Δy>0,即直線斜率小於1,應使x方向每次增加1,y方向最多增加1,此時區Δt=1/Δx;
同理,當Δy>Δx>0,直線斜率大於1,取Δt=1/Δy,所
4、直線的DDA算法程序
void dda(Graphics g, int x1, int x2, int y1, int y2) { int k; //應該走幾步 float x, y, dx, dy; //變化的增量 k = Math.abs(x2-x1); if (Math.abs(y2-y1)>k){ k = Math.abs(y2-y1); } //k=max(x2-x1,y2-y1) dx = (float)(x2-x1)/k; dy = (float)(y2-y1)/k;//變化的增量 x = (float)x1; y = (float)y1;//起始點 for (int i = 0; i <k ; i++ ){ g.drawLine((int)(x+.5f), (int)(y+.5f), (int)(x+.5f),(int)(y+.5f)); //畫點,並完成四舍五入 x = x+dx; y = y+dy; } }
基本增量算法需要進行浮點數運算,產生一個像素需要做兩次加法和兩次取整運算,效率低且取整運算不利於硬件實現。
那有什么辦法能夠改進,不使用浮點數運算,使用整數加法運算呢?
三、Bresenham算法
核心思想
該算法的思想是通過各行、各列像素中心構造一組虛擬網絡線。按照直線起點到終點的順序,計算直線與各垂直網絡線的交點,然后根據誤差項的符號確定該列像素中於此交點最近的像素。
每次x+1,y的遞增(減)量為0或1,它取決於實際直線與最近光柵網格點的距離,這個距離最大誤差為0.5。
誤差項d的初值為0,d=d+k,一旦d>=1,就把它減去1,保證d的相對性,且在0、1之間。
關鍵是將這個算法的效率也搞到整數加法,如何提高到整數加法?
算法改進
改進1:
令e=d-0.5
改進2:
由於算法中只用到誤差項的符號,於是可以用e*2*Δx來替換e
算法步驟
Bresenham算法很像DDA算法,都是加斜率,
但DDA算法是每次求一個新的y以后取整來畫,而Bresenham算法是判斷符號來決定上下兩個點。
所以Bresenham算法集中了DDA算法的優點,而且應用范圍廣泛。
Bresenham算法程序
未改進前的算法。缺點:有除法和浮點數
m=(double)dy/(double)dx; e = m–0.5;//e表示直線在網格縱軸上的交點與中點的差值 for(i=0; i<dx; i++) { g.drawLine(x,y,x,y); if(e>=0)//e>=0,y選擇上方的像素點 { y=y+1; e=e–1; } //e<0,y選擇下方的像素點 x=x+1; e=e+m; } //其中dy=ye–ys,dx=xe–xs。
改進后的算法:
void bresenham(Graphics g, int xs, int ys, int xe, int ye) { int dx = xe - xs; int dy = ye - ys; int e = 2 * dy - dx; int x = xs; int y = ys; for (int i = 0; i < dx; i++) { g.drawLine(x, y, x, y); if (e >= 0)//e>=0,y選擇上方的像素點 { y = y + 1; e = e - 2 * dx; } //e<0,y選擇下方的像素點 x = x + 1; e = e + 2*dy; } }
Bresenham算法的優點:
1、不必計算直線之斜率,因此不做除法;
2、不用浮點數,只用整數;
3、只做整數加減法和乘2運算,而乘2運算可以用硬件移位實現。
Bresenham算法速度很快,並適於用硬件實現。