作者:gnuhpc
出處:http://www.cnblogs.com/gnuhpc/
0.算法目的
這個算法是要畫一條平滑的直線,這個工作的難點是確定兩點之間的那些像素點,使其近可能的靠近手工繪制的直線。
1.基本算法描述
現在我們要在一個光柵格子上畫一條直線,我們將直線的斜率嚴格控制在。
若我們進一步限定畫線的路徑:給定一個點后x軸要有一個增量來繪制這條直線的第二個點。這樣一來,顯而易見若給定點(x,y)后,線上的下一個點就只能在有限的范圍之內進行選擇了:
它可能是畫在(x+1,y),或者畫在(x+1,y+1)點。
所以我們在平面上轉角為45度的位置進行畫線,畫線也就是在每個步長上決定這兩種可能。
我們以下圖為說明,注意這是個柵格的局部放大圖,你可以想象是用一個高倍放大鏡來看:
在點(x,y)處畫一個點,那么線的路徑一般就會在它本應該繪制成什么樣子和屏幕的分辨率實際上允許怎樣繪制成什么樣子這兩個問題上進行折衷。通常點 (x,y)的繪制是錯誤的,所謂"錯誤"也就是說在事實上,這些數學意義上的點是無法繪制在像素柵格上的。我們設每個實際繪制點的縱坐標y相對應的誤差為 ,那么這些點的數學真值為
。這個誤差應該在-0.5到+0.5之間。
在從x移動到x+1間,我們對其數學上的y縱坐標也增加了斜率大小的值m。那么,若 ,我們將選擇繪制點(x+1,y)。否則,我們繪制點(x+1,y+1)。其實看看圖中的含義,這一步大有四舍五入之感。我們在這一步所作的決定就是在使在屏幕上繪制出來的線和數學上所要畫的線之間的總誤差最小化。當然前者繪出的圖像在局部看來是有些問題的,不過你就忍了吧,這畢竟是機器。
這樣,新繪制的點帶來的誤差我們再次記為 ,這樣一來我們就能夠在以下的繪制中重復這個過程。新的誤差可能采用以下兩個可能的值中的一個:若(x+1,y)被選擇使用了,那么新的誤差就滿足
,反之則新的誤差就是
。這個是顯而易見的,自己動手畫一畫就知道了。
寫成程序描述一下就是:
這依然需要浮點值,我們現在要做的就是將浮點數轉化為整數,那么我們試着做如下運算:
這樣一來在這個不等式中出現的變量都成為整數了。
用 替代
,我們得到
。
這就是我們來判斷點的繪制所用的不等式了,都是整數運算。
每一步誤差的更新也可以寫成 的形式:
不等式兩邊都乘以 ,則得到
寫成 的形式則是:
我們重新寫一下這個整數版的bresenham算法描述:
這個描述有一下特點:
都是整數運算當然速度上比較好,而且乘以2這個運算可以用左移運算這個為運算,速度上也不錯。
我們用C語言描述的方式再寫一遍這個算法:
void linev6(Screen &s,
unsigned x1, unsigned y1,
unsigned x2, unsigned y2,
unsigned char colour )
{
int dx = x2 - x1,
dy = y2 - y1,
y = y1,
eps = 0;
for ( int x = x1; x <= x2; x++ )
{
s.Plot(x,y,colour);
eps += dy;
if ( (eps <<1)>= dx )
{
y++;
eps -= dx;
}
}
}