前段时间做了一下光栅直线生成算法的研究,并且在VC下实现了DDA算法、Bresenham算法、对称算法、两步算法、及四步算法。这里给个总结,希望和大家交流。
主要研究的算法主要有DDA算法、Bresenham算法、对称算法、两步算法、及四步算法,此外还对自适应多步位移码画线算法进行了一定研究。其中,DDA、Bresenham算法都是单步画线算法,其它是多步画线算法。 DDA算法和Bresenham算法是经典的画线算法,并且业已证明Bresenham画线算法使用了最小的计算量,是最高效的单步画线算法,这里不再对其进行说明。以下对其余各个算法进行必要的说明。 对称直线生成算法基于这样一个事实:直线以中点位界,其两边是对称的。因而可以利用这个对称性,对Bresenham算法进行改进,使得每进行一次判断就可以生成相对于直线中点的两个对称点。如此以来,直线就由两端向中间生成。算法用C++语言描述如下: int x1 = m_start.m_x; // 起点x int y1 = m_start.m_y; // 起点y int x2 = m_end.m_x; // 终点x int y2 = m_end.m_y; // 终点y int dx = m_end.m_x - m_start.m_x; int dy = m_end.m_y - m_start.m_y; int dx2 = dx << 1; int dy2 = dy << 1; int e = dy2 - dx; // 决策量 int half = (dx+1) >> 1; for (int i=0; i<half; i++) { pDC->SetPixel(x1, y1, color); pDC->SetPixel(x2, y2, color); if (e > 0) // 当e>0时,始端y向加1,末端y向减1 { y1++; y2--; e -= dx2; } //始端x向加1,末端x向减1 x1++; x2--; e += dy2; } 两步画线算法每判断一次就生成两个点,这与对称算法相似,不同的是对称算法是从两端向中间进行的,而该算法是始终沿一个方向进行的,一次生成2个连续的点。当斜率k满足条件0≤k<1时,考虑当前点P,如图1所示,直线与AB的交点一定落在AB区间的四等分中之一。 图1 下一待选点位置必在四个等分区间之一 实际上,两个连续点可能出现的形式只有四种情况,即DD、DX、XD和XX。其中X表示沿水平方向移动一个像素,D表示沿对角方向移动一个像素,见图2。 图2 连续两点可能出现的4种形式 决策量e的初值为e=4dy–dx,对于Pattern 1,e的增量为4dy-4dx; 对于Pattern 2和3,e的增量为4dy-2dx;对于Pattern 4, e的增量为4dy。算法用C++语言描述如下: int x = m_start.m_x; // 起点x int y = m_start.m_y; // 起点y int dx = m_end.m_x - m_start.m_x; int dy = m_end.m_y - m_start.m_y; int dx2 = dx << 1; int dy2 = dy << 1; int dx4 = dx2 << 1; int dy4 = dy2 << 1; int inc4 = dy4 - dx4; int inc2 = dy4 - dx2; int e = dy4 - dx; pDC->SetPixel(x, y, color); for (int i=0; i<dx; i+=2) { if (e > dx) { // Pattern 4 if (e > dx2) { e += inc4; x++; y++; pDC->SetPixel(x, y, color); x++; y++; pDC->SetPixel(x, y, color); } // Pattern 3 else { e += inc2; x++; y++; pDC->SetPixel(x, y, color); x++; pDC->SetPixel(x, y, color); } } else { // Pattern 2 if (e > 0) { e += inc2; x++; pDC->SetPixel(x, y, color); x++; y++; pDC->SetPixel(x, y, color); } // Pattern 1 else { x++; pDC->SetPixel(x ,y, color); x++; pDC->SetPixel(x, y, color); e += dy4; } } } 每计算一次e值就生成两个点,从而提高画线效率,但是要进行较多的判断,所以效率提高并不明显。 四步画线算法类似域两步画线算法,不同的时每判断一次,就生成四个点。如图3所示,直线一次前进四个点的情形。 图3 四步画线算法示意图 对于四步画线算法,规的跟两步算法相似的表示方法,共有14种形式,如图4。 图4 连续四点肯能出现的14种形式 图4中,自底向上依次是Pattern 1到Pattern 14,Pattern 1为XXXX,Pattern 2为XXXD,……,Pattern 14为DDDD。 初始时,e=-dx, Pattern 1时e的增量为8dy;Pattern 2到Pattern 4时e的增量为8dy-2dx;Pattern 5到Pattern 8时e的增量为8dy-4dx;Pattern 9到Pattern 13时e的增量为8dy-6dx;Pattern 14时e的增量为8dy-8dx。鉴于代码过长,这里不再给出。 与传统的直线算法不同,自适应多步位移码画线算法首先将待绘制直线表达成一串由0和1组成的位移码,根据位移码的特点,自适应的确定一次生成的像素数目,从而获得更高的绘制效率。这里引入两个引理: 引理1 设有从(x1, y1)到(x2, y2)的直线,0<H=y2-y1<K=x2-x1。那么在该直线的位移码中,相邻两个1之间连续0的数目w为[(K-H)/H]或[K/H]。[A]表示对A取整。 引理2设有从(x1, y1)到(x2, y2)的直线,0<H=y2-y1<K=x2-x1。那么在该直线的位移码中,相邻两个0之间连续1的数目w为[H/(K-H)]或[K/(K-H)]。 证明略。 这里给出自适应多步位移码画线算法的伪代码: K = x2 – x1; H = y2 – y1; T = [(K – 1)/2] – H; Divide(H, K – H, N, Tchg); x = x1; y = y1; WHILE x<= x2 DO IF T<0 THEN y = y +1; xend = x+N; WHILE x<xend Do DrawPixel(x, y); x = x +1; END WHILE T = T+Tchg; ELSE T = T – H; DrawPixel(x, y); END IF x = x +1; END WHILE 从理论而言,该算法根据直线斜率自适应地选择一个步长, 以一次判断生成多个采样点的方式对直线进行绘制,从而大大提供了绘制效率。但是实际上,连续两个1(0)中间0(1)的个数虽然只在两个整数上选择,但是到底在哪一步是哪一个难以判断,所以实现起来并不容易,而且目前尚未有解决方法。
Bresenham 对称 两步 四步 加法 1 0.5 1 1 比较 2 1 1.5 1.3 加法和比较 3 1.5 2.5 2.3 乘法 2 3 4 6一 总述
二 算法介绍
1 对称直线生成算法
2 两步算法
3 四步画线算法
4 自适应多步位移码画线算法
三 算法效率
上表给出平均每步所进行的加法次数和比较次数,乘除次数是整算法总共的次数。DDA算法未在表中给出,它与Bresenham相似,但使用了多次浮点乘除运算和取整运算,效率明显低于其它算法。Bresenham与其它算法相比,加法步骤相当(对称算法除外),但比较次数较多。理论上而言,后面的算法效率较高。但是应该注意到多步算法,如两步和四步算法,在进行循环之前要做很多的初始化工作,而且乘除次数大于Bresenham算法。在进行较短直线绘制时,效率反而不如Bresenham算法快。
(一些算法的源程序较长,这里未给出。)








