zoukankan      html  css  js  c++  java
  • 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实现的太慢。

  • 相关阅读:
    3. 文件存储格式
    2. Reduce 输出压缩
    Nginx+Tomcat搭建集群环境
    redis缓存失效及解决方案
    几种开源NOSQL数据库
    使用Maven开发用户模块的CRUD(增删改查)
    Spring Boot 面试题整理
    oracle 11g&12c(pdb&cdb)系统巡检
    Redhat 6.8部署oracle12.2.0.1.0
    sqlserver创建程序集
  • 原文地址:https://www.cnblogs.com/weiweishuo/p/2954443.html
Copyright © 2011-2022 走看看