区域填充的递归算法原理与程序都很简单,但由于多次递归,费时,费内存,效率低。特别是当像素个数变多时,由于计算机是用栈实现的递归,递归层数太大程序会崩溃掉。
需要填充的范围比较大:
填充速度很慢,几秒中后崩溃了。
为了减少递归次数,提高效率,可以采用扫描线算法。
算法思想:当给定种子点(x,y)时,首先填充种子点所在扫描线上位于给定区域的一个区段,然后确定与这一区段相连通的上下两天扫描线上位于给定区域内的区段,并依次保存下来。反复这个过程反复进行这个过程,直到保存的这个区段都填充完毕。
可以用四个步骤实现:
1、初始化,堆栈置空,将种子点(x,y)入栈
2、出栈,若栈空则结束,否则取栈顶元素(x,y),以y作为当前扫描线。
3、填充并确定种子点所在区域,从种子点(x,y)出发,沿当前扫描线向左右两个方向填充,直到边界。分别标记区段的左右端点坐标为xl和xr。
4、确定新的种子点,在区间[xl,xr]中检查与当前扫描线y上、下相邻的两条扫描线上的像素,若存在非边界、未填充的像素,则把每一区间的最右像素作为种子点压入堆栈。返回2、步。
运行结果:
种子:
//种子
typedef struct
{
int x,y;
}Seed;
源代码:
void CDrawView::ScanLineFill4(int x,int y,COLORREF oldcolor,COLORREF newcolor)
{
//内点表示的区域填充扫描线算法
CClientDC dc(this);
stack<Seed> s;
bool spanNeedFill;
int xl,xr;
Seed pt; //种子
pt.x=x;
pt.y=y;
s.push(pt); //种子入栈
while(!s.empty()) //只要栈不空
{
pt=s.top(); //出栈
s.pop();
x=pt.x;
y=pt.y;
while(dc.GetPixel(x,y)==oldcolor) //向右填充
{
dc.SetPixel(x,y,newcolor);
x++;
}
xr=x-1; //标记右端点
x=pt.x-1;
while(dc.GetPixel(x,y)==oldcolor) //像左填充
{
dc.SetPixel(x,y,newcolor);
x--;
}
xl=x+1; //标记左端点
x=xl; //扫描上一条扫描线
y=y+1;
while(x<=xr)
{
spanNeedFill=false; //初始化
while(dc.GetPixel(x,y)==oldcolor) //向右搜索
{
x++;
spanNeedFill=true;
}
if(spanNeedFill) //如果没有填充过这个区间
{
pt.x=x-1;
pt.y=y;
s.push(pt); //把最右端点作为种子点压入栈
spanNeedFill=false;
}
while(dc.GetPixel(x,y)!=oldcolor&&x<=xr) //还有区间没有搜素
x++;
}
x=xl; //扫描下一条扫描线
y=y-2;
while(x<=xr)
{
spanNeedFill=false;
while(dc.GetPixel(x,y)==oldcolor)
{
x++;
spanNeedFill=true;
}
if(spanNeedFill)
{
pt.x=x-1;
pt.y=y;
s.push(pt);
spanNeedFill=false;
}
while(dc.GetPixel(x,y)!=oldcolor&&x<=xr)
x++;
}
}
}