一、 算法原理简介:
算法原理的详细描述及部分实现可参考:
http://www.cs.helsinki.fi/group/goa/mallinnus/lines/bresenh.html
Fig. 1
假设以(x, y)为绘制起点,一般情况下的直观想法是先求m = dy /dx(即x每增加1, y的增量),然后逐步递增x, 设新的点为x1 = x + j, 则y1 = round(y + j * m)。可以看到,这个过程涉及大量的浮点运算,效率上是比较低的(特别是在嵌入式应用中,DSP可以一周期内完成2次乘法,一次浮点却要上百个周期)。
下面,我们来看一下Bresenham算法,如Fig. 1,(x, y +ε)的下一个点为(x, y + ε + m),这里ε为累加误差。可以看出,当ε+m < 0.5时,绘制(x + 1, y)点,否则绘制(x + 1, y + 1)点。每次绘制后,ε将更新为新值:
ε = ε + m ,如果(ε + m) <0.5 (或表示为2*(ε + m) < 1)
ε = ε + m – 1, 其他情况
将上述公式都乘以dx, 并将ε*dx用新符号ξ表示,可得
ξ = ξ + dy, 如果2*(ξ + dy) < dx
ξ = ξ + dy – dx, 其他情况
可以看到,此时运算已经全变为整数了。以下为算法的伪代码:
ξ ← 0, y ← y1
For x ← x1 to x2 do
Plot Point at (x, y)
If (2(ξ + dy) < dx)
ξ ←ξ + dy
Else
y ← y + 1,ξ ←ξ + dy – dx
End If
End For
二、 算法的注意点:
Fig. 2
在实际应用中,我们会发现,当dy > dx或出现Fig.2 右图情况时时,便得不到想要的结果,这是由于我们只考虑dx > dy, 且x, y的增量均为正的情况所致。经过分析,需要考虑8种不同的情况,如Fig. 3所示:
(Fig. 3)
当然,如果直接在算法中对8种情况分别枚举, 那重复代码便会显得十分臃肿,因此在设计算法时必须充分考虑上述各种情况的共性,后面将给出考虑了所有情况的实现代码。
三、 算法的实现
以下代码的测试是利用Opencv 2.0进行的,根据需要,只要稍微修改代码便能适应不同环境
void line(point_t p1, point_t p2, color_t c) { int dx = p2.x - p1.x; int dy = p2.y - p1.y; int ux = ((dx > 0) << 1) - 1; int uy = ((dy > 0) << 1) - 1; int x = p1.x, y = p1.y, eps; point_t p; eps = 0; dx = abs(dx); dy = abs(dy); if(dx > dy) { for (x = p1.x; x != p2.x ; x += ux) { p.x = x; p.y = y; pixel(p, c); eps += dy; if ((eps << 1) >= dx) { y += uy; eps -= dx; } } } else { for (y = p1.y; y != p2.y; y+= uy) { p.x = x; p.y = y; pixel(p, c); eps += dx; if((eps << 1) >= dy) { x += ux; eps -= dy; } } } }