bresenham直线算法

  思路很明了的一个算法,没有中点划线法那么绕(我需要小心的判断_2Fm>0意味着什么)。

  基本原理从某处摘得:设直线方程为yi+1=yi+k(xi+1-xi)+k。假设列坐标象素已经确定为xi,其行坐标为yi。那么下一个象素的列坐标为xi1,而行坐标要么为yi,要么递增1yi1。是否增1取决于误差项d的值。误差项d的初值d00x坐标每增加1d的值相应递增直线的斜率值k,即ddk。一旦  d≥1,就把它减去1,这样保证d01之间。当d0.5时,直线与垂线x=xi1交点最接近于当前象素(xiyi)的右上方象素(xi1yi1);而当d<0.5时,更接近于右方象素(xi1yi)。为方便计算,令ed0.5e的初值为-0.5,增量为k。当e0时,取当前象素(xiyi)的右上方象素(xi1yi1);而当e<0时,取(xiyi)右方象素(xi1yi)。

  上面这段叙述很见功底,是有中国特色的教授式描述,不要图就能明白。原文的图示也很简单:

下面是我画的算法流程图(对应-1<k<0情景)

<你看到图中的y--可能好奇:为什么不是教科书上的y++?因为本文使用的y轴是朝下的,确切说屏幕的常规y轴本来就该朝下>

把流程图变成c代码,依旧是在linux的framebuffer下绘图:

View Code
 1 void drawLine_brshm_asis(int x0,int y0,int x1,int y1){
 2     int x0_x1_int=x1-x0;
 3     int y0_y1_int=y1-y0;
 4     if((x0_x1_int>0?x0_x1_int:-x0_x1_int)>(y0_y1_int>0?y0_y1_int:-y0_y1_int)){
 5         if(x0>x1){
 6             EXCHANGE_INT(x0,x1);
 7             EXCHANGE_INT(y0,y1);
 8         }
 9         float k=(float)y0_y1_int/(float)x0_x1_int;
10         int y=y0;
11         float d=0;
12         if(y0>y1){
13             k=-k;//k<0,取正好思考些
14             for(int x=x0;x<=x1;x++){
15                 if(d>0.5){
16                     *(pt_memBuffer+(x<<2)+(y-1)*bytes_w)=0xff;//drawPixel(x,y-1)
17                     if(d>1){
18                         d--;
19                         y--;
20                     }
21                 }
22                 else{
23                     *(pt_memBuffer+(x<<2)+y*bytes_w)=0xff;//drawPixel(x,y)
24                 }
25                 d+=k;
26             }
27         }
28     }
29 }

pt_buffer是显存指针。一句*(pt_buffer+(x<<2)+y*bytes_w)就相当于函数drawPixel(x,y)。并且,我只写了-1<k<0的算法分支,剩下的懒得写了。

代码逻辑清楚,但速度慢,调用1000*100此耗时100ms左右。下面是优化后的代码,所谓优化,就是

1,通过乘2消去含0.5的浮点数项。此时d变成了_2d,即2*d。

2,通过乘dx消去含k的浮点数项,此时_2d变成了_2ddx,即2d*dx。消去k的同时也避免了算法开始计算k的开销。

3,d的取值范围本来是0~_2dx,将其调整为-dx~dx,这样(y>dx?)项会变成(y>0?),判断起来会快一些。

View Code
 1 void drawLine_brshm(int x0,int y0,int x1,int y1){
 2     int x0_x1_int=x1-x0;
 3     int y0_y1_int=y1-y0;
 4     
 5     if((x0_x1_int>0?x0_x1_int:-x0_x1_int)>(y0_y1_int>0?y0_y1_int:-y0_y1_int)){
 6         if(x0>x1){
 7             EXCHANGE_INT(x0,x1);
 8             EXCHANGE_INT(y0,y1);
 9         }
10         int _2dx=x0_x1_int<<1;
11         int _2dy=y0_y1_int+y0_y1_int;//may be negative
12         int _2ddx=-x0_x1_int;
13         char*pt_write=pt_memBuffer+(x0<<2)+y0*bytes_w;
14         if(y0>y1){
15             _2dy=-_2dy;//k=-k
16             for(int x=x0;x<=x1;x++){
17                 if(_2ddx>0){
18                     *(pt_write-bytes_w)=0xff;
19                     if(_2ddx>x0_x1_int){
20                         _2ddx-=_2dx;
21                         pt_write-=bytes_w;//affected by y--
22                     }
23                 }
24                 else{
25                     *pt_write=0xff;
26                 }
27                 _2ddx+=_2dy;
28                 pt_write+=4;
29             }
30         }
31     }
32 }

 耗时降到65ms左右。

以上的编译优化等级都是-O2。不经意测了DDA算法的定点数版本,几乎赶上bresenham了。不知道是定点数太快了,还是我的bresenham实现的太慢。

posted on 2013-03-11 18:24  weiweishuo  阅读(1887)  评论(0编辑  收藏  举报

导航