思路很明了的一个算法,没有中点划线法那么绕(我需要小心的判断_2Fm>0意味着什么)。
基本原理从某处摘得:设直线方程为yi+1=yi+k(xi+1-xi)+k。假设列坐标象素已经确定为xi,其行坐标为yi。那么下一个象素的列坐标为xi+1,而行坐标要么为yi,要么递增1为yi+1。是否增1取决于误差项d的值。误差项d的初值d0=0,x坐标每增加1,d的值相应递增直线的斜率值k,即d=d+k。一旦 d≥1,就把它减去1,这样保证d在0、1之间。当d≥0.5时,直线与垂线x=xi+1交点最接近于当前象素(xi,yi)的右上方象素(xi+1,yi+1);而当d<0.5时,更接近于右方象素(xi+1,yi)。为方便计算,令e=d-0.5,e的初值为-0.5,增量为k。当e≥0时,取当前象素(xi,yi)的右上方象素(xi+1,yi+1);而当e<0时,取(xi,yi)右方象素(xi+1,yi)。
上面这段叙述很见功底,是有中国特色的教授式描述,不要图就能明白。原文的图示也很简单:
下面是我画的算法流程图(对应-1<k<0情景)
<你看到图中的y--可能好奇:为什么不是教科书上的y++?因为本文使用的y轴是朝下的,确切说屏幕的常规y轴本来就该朝下>
把流程图变成c代码,依旧是在linux的framebuffer下绘图:
View Code1 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 Code1 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实现的太慢。
来源:https://www.cnblogs.com/weiweishuo/archive/2013/03/11/2954443.html