Download presentation
Presentation is loading. Please wait.
Published byNoel Foster Modified 5年之前
1
第三章 基 本 图 形 的 生 成 本 章 主 要 内 容 直线段的扫描转换算法 圆弧的扫描转换算法 多边形的扫描转换与区域填充 字符 裁剪
反走样 消隐
2
3.1 直线段的扫描转换算法 直线的扫描转换 三个常用算法 确定最佳逼近于该直线的一组像素 按扫描线顺序,对这些像素进行写操作
数值微分(DDA)算法 中点画线法 Bresenham画线算法
3
3.1.1 数值微分(DDA)法 基本方法(不是DDA方法) 已知过端点 的直线段L:y = kx+b 则有: x=x+xInc;
而像素 (x, round(y)) 作为当前点的坐标。 评价:①这种方法直观易懂,②但效率太低,因为画每一个点需要一次浮点乘法和一次舍入运算。
4
3.1.1 数值微分(DDA)法 数值微分(DDA)算法---增 量 算 法 分 析: 1:如果 | k | ≤1,则
( X i+1 ,Yi + 1) (X i , Yi) 栅格交点表示像素点位置 。 分 析: 1:如果 | k | ≤1,则 yi+1 = kxi+1 + b = kxi kx + b = yi kx 当x =1;有:yi+1 = yi+k ……式3-1 当x =-1;有:yi+1 = yi-k ……式3-2 乘法运算
5
3.1.1 数值微分(DDA)法 2:如果 | k| >1,有 当y =1;有:xi+1 = xi + 1/k ……式3-3
增量算法:在一个迭代算法中,如果每一步的x、y值是用前一步的值加上一个增量来获得,则称为增量算法。如:式3-1、式3-2、式3-3、式3-4那样; DDA算法便是一个增量算法。尊重历史,继往开来 乘法运算
6
3.1.1 数值微分(DDA)法 #define round(x) ((int)(x+0.5))
取整运算 #define round(x) ((int)(x+0.5)) void DDALine(int x0,int y0,int x1,int y1,int color) int dx = x1- x0, dy = y1- y0, steps, k; float xin,yin,x = x0 ,y = y0 ; if(abs(dx)>abs(dy)) steps = abs(dx); else steps = abs(dy); xin = dx/(float)steps; yin = dy/(float)steps; setpixel (round(x), round(y), color); for (k=0;k<steps;k++) x + = xin; y + = yin; 浮点运算
7
3.1.1 数值微分(DDA)法 关于DDA的评价: 改进措施: DDA方法计算像素位置比直接采用方程式更快;
利用了光栅特性消除了3-1式中的乘法; 浮点增量的连续迭代加中取整误差的积累,导致长线段所计算的像素位置偏离实际线段; 程序DDAline中的取整函数和浮点运算仍然十分耗时; 改进措施: 将增量k、1/k分解成整数和小数部分,使所有计算都改进成整数DDA算法的性能;
8
3.1.2 中点画线法 采用增量思想的DDA算法,每计算一个象素,只需计算一个加法,是否最优? 如非最优,如何改进?
目标:进一步将一个加法改为一个整数加法。 新思路-> DDA算法采用两点式,可否采用其他的直线表示方式?
9
3.1.2 中点画线法 基本思想 当前象素点为(xp, yp) 。下一个象素点为P1 或P2 。
设M=(xp+1, yp+0.5),为p1与p2 之中点,Q为理想直线与x=xp+1 垂线的交点。将Q与M的y坐标进 行比较。 当M在Q的下方,则P2 应为 下一个象素点; M在Q的上方,应取P1为下一点。
10
3.1.2 中点画线法 构造判别式:d=F(M)=F(xp+1,yp+0.5) =a(xp+1)+b(yp+0.5)+c
其中a=y0-y1, b=x1-x0, c=x0y1-x1y0 当d<0,M在L(Q点)下方,取右上方P2为下一个象素; 当d>0,M在L(Q点)上方,取右方P1为下一个象素; 当d=0,选P1或P2均可,约定取P1为下一个象素;
11
3.1.2 中点画线法 这样做,每一个像素的计算量是4个加法,两个乘法。 如果也采用增量算法呢?
d 是xp, yp的线性函数,因此可采用增量计算,提高运算效率。
12
3.1.2 中点画线法 若当前象素处于d0情况,则取正右方象素P1 (xp+1, yp), 要判下一个象素位置,应计算
d1=F(xp+2, yp+0.5)=a(xp+2)+b(yp+0.5)=d+a; 增量为a 若d<0时,则取右上方象素P2 (xp+1, yp+1)。要判断再下一象素,则要计算 d2= F(xp+2, yp+1.5)=a(xp+2)+b(yp+1.5)+c=d+a+b ;增量为a+b
13
3.1.2 中点画线法 至此,至少新算法可以和DDA算法一样好。 能否再做改进?能否实现整数运算? 画线从(x0, y0)开始,d的初值
d0=F(x0+1, y0+0.5)=F(x0, y0)+a+0.5b =a+0.5b。 可以用2d代替d来摆脱小数,提高效率。 令 d0=2a+b, d1=2a, d2=2a+2b,我们有如下算法 。
14
3.1.2 中点画线法 void Midpoint Line (int x0,int y0,int x1, int y1,int color) { int a, b, d1, d2, d, x, y; a=y0-y1, b=x1-x0, d=2*a+b; d1=2*a, d2=2* (a+b); x=x0, y=y0; drawpixel(x, y, color); while (x<x1) { if (d<0) {x++, y++, d+=d2; } else {x++, d+=d1;} drawpixel (x, y, color); } /* while */ } /* mid PointLine */
15
3.1.2 中点画线法 例:用中点画线法 i xi yi d 1 0 0 1 2 1 0 -3 3 2 1 3 4 3 1 -1
16
3.1.3 Bresenham算法 基本思想 DDA算法采用点斜式,中点法采用隐式表示。 中点法可以有整数算法。
其他表示可以推出整数算法吗?
17
3.1.3 Bresenham算法 d2 d1 Yk+3 Yk+2 Yk+1 Yk Yk+1 Y Yk Xk Xk+1 Xk+2 Xk+3
18
3.1.3 Bresenham算法 决策参数: 可见,如果 pk<0,即d1<d2,yk+1=yk,否则 yk+1=yk+1
19
3.1.3 Bresenham算法 例:画直线从(20,10)到(30,18),斜率为0.8. △x=10, △y=8;初始决策参数为p0=2△y-△x=6. k pk (xk+1,yk+1) 6 (21,11) 5 (26,15) 1 2 (22,12) (27,16) -2 (23,12) 7 (28,16) 3 14 (24,13) 8 (29,17) 4 10 (25,14) 9 (30,18)
20
3.1.3 Bresenham算法
21
3.1.3 Bresenham算法
22
3.2圆弧扫描转换算法 圆弧的扫描转换 四个常用算法 确定最佳逼近于该圆弧的一组象素 按扫描线顺序,对这些象素进行写操作 中点画圆法
Bresenham画圆算法 生成圆弧的正负法 圆的内接正多边形逼近法
23
圆弧扫描算法 下面仅以圆心在原点、半径R为整数的圆为例,讨论圆的生成算法。 假设圆的方程为: X2 + Y2 = R2
24
3.2 圆弧扫描算法 X2 + Y2 = R2 Y = Sqrt(R2 - X2) 在一定范围内,每给定一 X值,可得一Y值。
缺点:浮点运算,开方, 取整,不均匀。 y x
25
中点画圆法 利用圆的对称性,只须讨论1/8圆。第二个8分圆
P为当前点亮象素,那么,下一个点亮的象素可能是P1(Xp+1,Yp)或P2(Xp +1,Yp -1)。 P(Xp ,Yp ) P1 M P2
26
中点画圆法 构造函数:F(X,Y)=X2 + Y2 - R2 ;则 F(X,Y)= 0 (X,Y)在圆上;
设M为P1、P2间的中点,M=(Xp+1,Yp-0.5) P1 M P2
27
F(M)< 0 ->M在圆内-> 取P1 F(M)>= 0 ->M在圆外-> 取P2
中点画圆法 有如下结论: F(M)< 0 ->M在圆内-> 取P1 F(M)>= 0 ->M在圆外-> 取P2 为此,可采用如下判别式: P1 M P2
28
中点画圆法 d = F(M) = F(xp + 1, yp - 0.5) =(xp + 1)2 + (yp - 0.5) 2 - R2
若d<0, 则P1 为下一个象素,那么再下一个象素的判别式为: d2 = F(xp + 2, yp - 0.5) = (xp + 2)2 + (yp - 0.5) 2 - R2 = d + 2xp +3 即d 的增量为 2xp +3. P1 M P2
29
中点画圆法 若d>=0, 则P2 为下一个象素,那么再下一个象素的判别式为: d1 = F(xp + 2, yp - 1.5)
= (xp + 2)2 + (yp - 1.5) 2 - R2 = d + (2xp + 3)+(-2 yp + 2) 即d 的增量为 2 (xp - yp) +5. d的初值: d0 = F(1, R-0.5) = 1 + (R-0.5)2 - R2 = R P1 M P2
30
中点画圆法 MidpointCircle(int r, int color) { int x,y; float d;
x=0; y=r; d=1.25-r; drawpixel(x,y,color); while(x<y){ if(d<0){ d+ = 2*x+3; x++ } else{d+ = 2*(x-y) + 5; x++;y--; } }
31
中点画圆法 为了进一步提高算法的效率,可以将上面的算法中的浮点数改写成整数,将乘法运算改成加法运算,即仅用整数实现中点画圆法。
使用e=d-0.25代替d e0=1-R
32
Bresenham画圆算法 现在从A点开始向右下方逐点来寻找弧AB要用的点。如图中点Pi-1是已选中的一个表示圆弧上的点,根据弧AB的走向,下一个点应该从Hi或者Li中选择。显然应选离AB最近的点作为显示弧AB的点。 假设圆的半径为R,显然,当xhi2 + yhi2 -R2 ≥ R2 - (xli2 + yli2)时,应该取Li。否则取Hi。 令di = xhi2 + yhi2 + xli2 + yli2 - 2R2 显然,当di ≥0 时应该取Li。否则取Hi。
33
Bresenham画圆算法 剩下的问题是如何快速的计算di。设图中Pi-1的坐标为(xi-1,yi-1),则Hi和Li的坐标为(xi,yi-1)和(xi,yi-1-1 ) di = xi2 + yi-12 + xi2 + (yi-1-1)2 - 2R2 =2xi2 + 2yi yi-1 - 2R2 Hi+1和Li+1的坐标为(xi+1,yi)和(xi+1,yi-1 ) di+1 = (xi + 1)2 + yi2 + (xi + 1)2 + (yi - 1)2 - 2R2 =2xi2 + 4xi + 2yi2 - 2yi - 2R2 + 3
34
Bresenham画圆算法 当di<0时->取Hi -> yi=yi-1,则 di+1 = di + 4xi-1 + 7
当di ≥ 0时->取Li -> yi=yi-1-1,则 di+1 = di + 4(xi-1-yi-1) + 11 易知 x0=0,y0=R,x1=x0+1 因此 d0=12 + y (y0 - 1)2 - 2R2 = 3 - 2y0 = 3 - 2R
35
3.3 多边形的扫描转换与区域填充 多边形有两种重要的表示方法:顶点表示和点阵表示。 多边形的扫描转换:把多边形的顶点表示转换为点阵表示。
区域可采用内点表示和边界表示两种表示形式。 区域填充:指先将区域的一点赋予指定的颜色,然后将该颜色扩展到整个区域的过程。
36
多边形分为凸多边形、凹多边形、含内环的多边形。
37
3.3.1多边形的扫描转换 2.3.1.1 扫描线算法 基本思想: 对于一条扫描线填充过程可以分为四个步骤:
按扫描线顺序,计算扫描线与多边形的相交区间,再用要求的颜色显示这些区间的象素,即完成填充工作。 对于一条扫描线填充过程可以分为四个步骤: 求交 排序 配对 填色
38
一个多边形与若干扫描线
39
数据结构 活性边表(AET):把与当前扫描线相交的边称为活性边,并把它们按与扫描线交点x坐标递增的顺序存放在一个链表中 结点内容
△x:从当前扫描线到下一条扫描线间x的增量 ymax:该边所交的最高扫描线号ymax
40
新边表(NET): 存放在该扫描线第一次出现的边。若某边的较低端点为ymin,则该边就放在扫描线ymin的新边表中
41
若y=yi,x=x i;则当y = y i+1时,
假定当前扫描线与多边形某一条边的交点的x坐标为x,则下一条扫描线与该边的交点不要重计算,只要加一个增量△x。 设该边的直线方程为:ax+by+c=0; 若y=yi,x=x i;则当y = y i+1时, 其中 为常数
42
扫描线与多边形的顶点或边界相交时,必须正确的交点的取舍。只需检查顶点的两条边的另外两个端点的y值。按这两个y值中大于交点y值的个数是0,1,2来决定。
43
算法过程 void polyfill (polygon, color) int color; 多边形 polygon;
{ for (各条扫描线i ) { 初始化新边表头指针NET [i]; 把y min = i 的边放进边表NET [i]; } y = 最低扫描线号; 初始化活性边表AET为空; for (各条扫描线i )
44
把新边表NET [i] 中的边结点用插入排序法插入AET表,使之按x坐标递增顺序排列;
遍历AET表,把配对交点区间(左闭右开)上的象素(x, y),用drawpixel (x, y, color) 改写象素颜色值; 遍历AET表,把y max= i 的结点从AET表中删除,并把y max > i 结点的x值递增x; 若允许多边形的边自相交,则用冒泡排序法对AET表重新排序; } } /* polyfill */
45
3.3.1.2边界标志算法 基本思想: 帧缓冲器中对多边形的每条边进行直线扫描转换,亦即对多边形边界所经过的象素打上标志。
然后再采用和扫描线算法类似的方法将位于多边形内的各个区段着上所需颜色。 使用一个布尔量inside来指示当前点是否在多边形内的状态。
46
算法过程 void edgemark_fill(polydef, color) 多边形定义 polydef; int color;
inside = FALSE; for (每条与多边形polydef相交的扫描线y ) for (扫描线上每个象素x ) { if(象素 x 被打上边标志) inside = ! (inside); if(inside!= FALSE) drawpixel (x, y, color); else drawpixel (x, y, background); }
47
用软件实现时,扫描线算法与边界标志算法的执行速度几乎相同,
但由于边界标志算法不必建立维护边表以及对它进行排序,所以边界标志算法更适合硬件实现,这时它的执行速度比有序边表算法快一至两个数量级。
48
3.3.2区域填充算法 区域指已经表示成点阵形式的填充图形,它是象素的集合。 区域可采用内点表示和边界表示两种表示形式。
区域可分为4向连通区域和8向连通区域。 区域填充指先将区域的一点赋予指定的颜色,然后将该颜色扩展到整个区域的过程。区域填充算法要求区域是连通的
49
4向连通区域和8向连通区域 四个方向运动 八个方向运动 四连通区域 八连通区域
50
3.3.2.1区域填充的递归算法 内点表示的4连通区域的递归填充算法:
void FloodFill4(int x,int y,int oldcolor,int newcolor) { if(getpixel(x,y)==oldcolor) //属于区域内点oldcolor { drawpixel(x,y,newcolor); FloodFill4(x,y+1,oldcolor,newcolor); FloodFill4(x,y-1,oldcolor,newcolor); FloodFill4(x-1,y,oldcolor,newcolor); FloodFill4(x+1,y,oldcolor,newcolor); }
51
边界表示的4连通区域的递归填充算法: void BoundaryFill4(int x,int y,int boundarycolor,int newcolor) { int color; if(color!=newcolor && color!=boundarycolor) { drawpixel(x,y,newcolor); BoundaryFill4 (x,y+1, boundarycolor,newcolor); BoundaryFill4 (x,y-1, boundarycolor,newcolor); BoundaryFill4 (x-1,y, boundarycolor,newcolor); BoundaryFill4 (x+1,y, boundarycolor,newcolor); }
52
3.3.2.2区域填充的扫描线算法 算法步骤: 首先填充种子点所在扫描线上的位于给定区域的一个区段
然后确定与这一区段相连通的上、下两条扫描线上位于给定区域内的区段,并依次保存下来。 反复这个过程,直到填充结束。
53
(1)初始化:堆栈置空。将种子点(x,y)入栈。
(2)出栈:若栈空则结束。否则取栈顶元素(x,y),以y作为当前扫描线。 (3)填充并确定种子点所在区段:从种子点(x,y)出发,沿当前扫描线向左、右两个方向填充,直到边界。分别标记区段的左、右端点坐标为xl和xr。 (4)并确定新的种子点:在区间[xl,xr]中检查与当前扫描线y上、下相邻的两条扫描线上的象素。若存在非边界、未填充的象素,则把每一区间的最右象素作为种子点压入堆栈,返回第(2)步。 上述算法对于每一个待填充区段,只需压栈一次;因此,扫描线填充算法提高了区域填充的效率。
Similar presentations