扫描线种子填充算法不再采用递归的方式处理“4-联通”和“8-联通”的相邻点,而是通过沿水平扫描线填充像素段,一段一段地来处理“4-联通”和“8-联通”的相邻点。这样算法处理过程中就只需要将每个水平像素段的起始点位置压入一个特殊的栈,而不需要象递归算法那样将当前位置周围尚未处理的所有相邻点都压入堆栈,从而可以节省堆栈空间。应该说,扫描线填充算法只是一种避免递归,提高效率的思想。
基本过程
当给定种子点(x, y)时,首先分别向左和向右两个方向填充种子点所在扫描线上的位于给定区域的一个区段,同时记下这个区段的范围[xLeft, xRight],然后确定与这一区段相连通的上、下两条扫描线上位于给定区域内的区段,并依次保存下来。反复这个过程,直到填充结束。
步骤实现
1) 初始化一个空的栈用于存放种子点,将种子像素(x,y)入栈
2) 当栈为非空时,重复执行以下步骤
a. 栈顶像素出栈
b. 沿扫描线对出栈像素的左右像素进行填充,直到遇到边界像素为止
c. 将上述区间内最左最右像素记为xLeft和xRight
d. 在区间[xLeft,xRight]内检查与当前扫描线相邻的上下两条扫描线是否全为边界像素或已填充的像素,若为非边界和未填充,则把每一区间的最右像素xRight作为种子像素压入堆栈,重复步骤(2)
步骤示例
代码实现(基于VC 6.0)
1 /* 扫描线种子生成算法 */ 2 int SetRP(int x,int y,COLORREF color,COLORREF mColor,CDrawDC *pDC){ 3 while(pDC->GetPixel(CPoint(x,y))== mColor){ 4 pDC->SetPixel(ROUND(x),ROUND(y),color); 5 x++; 6 } 7 return x-1; 8 } 9 int SetLP(int x,int y,COLORREF color,COLORREF mColor,CDrawDC *pDC){ 10 while(pDC->GetPixel(CPoint(x-1,y))==mColor){ 11 pDC->SetPixel(ROUND(--x),ROUND(y),color); 12 } 13 return x; 14 } 15 void NewLineSeed(std::stack<CPoint> *stk,int lx,int rx,int y,COLORREF color,COLORREF mColor,CDrawDC *pDC){ 16 for(int x=lx+1,e=rx+1;x<e;x++){ 17 if(pDC->GetPixel(CPoint(x,y))!=mColor){ 18 if(pDC->GetPixel(CPoint(x-1,y))==mColor) 19 stk->push(CPoint(x-1,y)); 20 } 21 } 22 if(pDC->GetPixel(CPoint(x-1,y))==mColor) 23 stk->push(CPoint(x-1,y)); 24 } 25 void CDrawDC::ScanLineSeedFill(int x, int y, COLORREF color) 26 { 27 int pRight,pLeft; 28 std::stack<CPoint> stk; 29 mColor = GetPixel(CPoint(x,y)); 30 31 stk.push(CPoint(x,y)); 32 while(!stk.empty()){ 33 CPoint p=stk.top(); 34 stk.pop(); 35 36 pRight = SetRP(p.x,p.y,color,mColor,this); 37 pLeft = SetLP(p.x,p.y,color,mColor,this); 38 39 NewLineSeed(&stk,pLeft,pRight,p.y+1,color,mColor,this); 40 NewLineSeed(&stk,pLeft,pRight,p.y-1,color,mColor,this); 41 } 42 }