图形的扫描转换(图形的光栅化)
实现在屏幕上显示图形,可通过寻找屏幕上的一组像素点集,并将该组像素点集用指定的颜色显示,以此来最佳逼近图形的形状的方法
直线的生成
比较常用的直线扫描转换算法有三种:数值微分法(DDA);中点画线算法;Bresenham算法。
数值微分法的特点是增量算法,直观、易实现。但是算法中有除法运算和浮点数,不利于用硬件实现;当图形中有大量的直线时,利用数值微分法会占用较多的内存,对运算速度会有一些影响。
中点画线算法在整个运算中都是整数运算,没有出现小数,因此占有的内存相对较少,也便于硬件实现。
Bresenham画线算法是计算机图形学领域使用最广泛的直线生成算法。在计算直线最佳逼近点的过程中,全部是整数运算,因此可以大幅提升计算速度。
数值微分法(DDA)
void DDA_Line(CDC *&pDc,CPoint &startPoint,CPoint &endPoint,COLORREF crColor)
{
if (endPoint.x !=startPoint.x && endPoint.y !=startPoint.y ) //非特殊位置直线
{
double x,y;
double k;
k=(endPoint.y -startPoint.y)*1.0/(endPoint.x -startPoint.x);
x=startPoint.x;
y=startPoint.y;
pDc->SetPixel((int)x,(int)y,crColor);
if (abs(k)<=1.0)
{
//X增量
for (int i=0;i<abs(endPoint.x -startPoint.x);i++)
{
if (endPoint.x >startPoint.x)
{
x+=1;
y+=k;
}
else
{
x-=1;
y-=k;
}
pDc->SetPixel((int)x,(int)(y+0.5),crColor);
}
}
else if (abs(k)>1.0)
{
for (int i=0;i<abs(endPoint.y -startPoint.y);i++)
{
if (endPoint.y >startPoint.y)
{
y+=1;
x+=1/k;
}
else
{
y-=1;
x-=1/k;
}
pDc->SetPixel((int)(x+0.5),(int)y,crColor);
}
}
}
else if (startPoint.x== endPoint.x) //垂直线
{
if (endPoint.y >startPoint.y)
{
for (int i=startPoint.y;i<=endPoint.y ;i++)
{
pDc->SetPixel(startPoint.x,i,crColor);
}
}
else
{
for (int i=startPoint.y;i>=endPoint.y ;i--)
{
pDc->SetPixel(startPoint.x,i,crColor);
}
}
}
else if (startPoint.y== endPoint.y)//画水平线
{
if (endPoint.x >startPoint.x)
{
for (int i=startPoint.x;i<=endPoint.x ;i++)
{
pDc->SetPixel(i,startPoint.y,crColor);
}
}
else
{
for (int i=startPoint.x;i>=endPoint.x ;i--)
{
pDc->SetPixel(i,startPoint.y,crColor);
}
}
}
}
中点画线算法
void MIDPOINT_Line(CDC *&pDc,CPoint &startPoint,CPoint &endPoint,COLORREF crColor)
{
if (endPoint.x !=startPoint.x && endPoint.y !=startPoint.y ) //非特殊位置直线
{
int kFlag=0; //0:k<=1 1:k>1
int sFlag=1; //斜率正负的标识
if (endPoint.x <startPoint.x ) //使起点X小于终点X
{
CPoint pt=startPoint;
startPoint = endPoint;
endPoint=pt;
}
if (abs(endPoint.y -startPoint.y)>abs(endPoint.x -startPoint.x)) //斜率绝对值大于1
{
kFlag=1;
}
if (startPoint.y>endPoint.y) //斜率小于0
{
sFlag=-1;
}
int a,b,tA,tAB,d,x,y;
if (sFlag==-1)
{
endPoint.y=startPoint.y+(startPoint.y-endPoint.y);
}
a=startPoint.y-endPoint.y;
b=endPoint.x -startPoint.x;
tA=2*a;
tAB=2*(a+b);
d=2*a+b;
x=startPoint.x;
y=startPoint.y;
pDc->SetPixel(x,y,crColor);
if (kFlag==0) //斜率<=1
{
for (int i=0;i<(endPoint.x-startPoint.x);i++)
{
if (d>=0)
{
pDc->SetPixel(x+1,y,crColor);
x+=1;
d+=tA;
}
else
{
pDc->SetPixel(x+1,y+sFlag,crColor);
x+=1;
y+=sFlag;
d+=tAB;
}
}
}
else
{
if (kFlag==1)
{
tA=2*b;
d=2*b+a;
}
for (int i=0;i<abs(endPoint.y-startPoint.y);i++)
{
if (d>=0)
{
pDc->SetPixel(x+1,y+sFlag,crColor);
y+=sFlag;
x+=1;
d+=tAB;
}
else
{
pDc->SetPixel(x,y+sFlag,crColor);
y+=sFlag;
d+=tA;
}
}
}
}
else if (startPoint.x== endPoint.x) //垂直线
{
if (endPoint.y >startPoint.y)
{
for (int i=startPoint.y;i<=endPoint.y ;i++)
{
pDc->SetPixel(startPoint.x,i,crColor);
}
}
else
{
for (int i=startPoint.y;i>=endPoint.y ;i--)
{
pDc->SetPixel(startPoint.x,i,crColor);
}
}
}
else if (startPoint.y== endPoint.y)//画水平线
{
if (endPoint.x >startPoint.x)
{
for (int i=startPoint.x;i<=endPoint.x ;i++)
{
pDc->SetPixel(i,startPoint.y,crColor);
}
}
else
{
for (int i=startPoint.x;i>=endPoint.x ;i--)
{
pDc->SetPixel(i,startPoint.y,crColor);
}
}
}
}
Bresenham算法
void BRESENHAM_Line(CDC *&pDc,CPoint &startPoint,CPoint &endPoint,COLORREF crColor)
{
if (endPoint.x !=startPoint.x && endPoint.y !=startPoint.y ) //非特殊位置直线
{
int kFlag=0; //0:k<=1 1:k>1
int sFlag=1; //斜率正负的标识
if (endPoint.x <startPoint.x ) //使起点X小于终点X
{
CPoint pt=startPoint;
startPoint = endPoint;
endPoint=pt;
}
if (abs(endPoint.y -startPoint.y)>abs(endPoint.x -startPoint.x)) //斜率绝对值大于1
{
kFlag=1;
}
if (startPoint.y>endPoint.y) //斜率小于0
{
sFlag=-1;
}
int x,y;
int dx,dy,p;
if (sFlag==-1)
{
endPoint.y=startPoint.y+(startPoint.y-endPoint.y);
}
dy=endPoint.y-startPoint.y;
dx=endPoint.x -startPoint.x;
x=startPoint.x;
y=startPoint.y;
pDc->SetPixel(x,y,crColor);
if (kFlag==0) //斜率<=1
{
p=2*dy-dx;
for (int i=0;i<(endPoint.x-startPoint.x);i++)
{
if (p<=0)
{
pDc->SetPixel(x+1,y,crColor);
x+=1;
p+=2*dy;
}
else
{
pDc->SetPixel(x+1,y+sFlag,crColor);
x+=1;
y+=sFlag;
p+=2*dy-2*dx;
}
}
}
else
{
p=2*dx-dy;
for (int i=0;i<abs(endPoint.y-startPoint.y);i++)
{
if (p<=0)
{
pDc->SetPixel(x,y+sFlag,crColor);
y+=sFlag;
p+=2*dx;
}
else
{
pDc->SetPixel(x,y+sFlag,crColor);
y+=sFlag;
x+=1;
p+=2*dx-2*dy;
}
}
}
}
else if (startPoint.x== endPoint.x) //垂直线
{
if (endPoint.y >startPoint.y)
{
for (int i=startPoint.y;i<=endPoint.y ;i++)
{
pDc->SetPixel(startPoint.x,i,crColor);
}
}
else
{
for (int i=startPoint.y;i>=endPoint.y ;i--)
{
pDc->SetPixel(startPoint.x,i,crColor);
}
}
}
else if (startPoint.y== endPoint.y)//画水平线
{
if (endPoint.x >startPoint.x)
{
for (int i=startPoint.x;i<=endPoint.x ;i++)
{
pDc->SetPixel(i,startPoint.y,crColor);
}
}
else
{
for (int i=startPoint.x;i>=endPoint.x ;i--)
{
pDc->SetPixel(i,startPoint.y,crColor);
}
}
}
}
运行结果: