目录
种子填充思想
所谓种子填充,是指以待填充一内点为基础向四周进行扩展,对满足条件的像素点进行颜色绘制的一种思想。从像素点是否需要绘制的判断方式出发,又分为边界表示和内点表示两种方法;从待填充像素点向四周扩展的方式出发,又分为四连通和八连通两种方式。
边界表示的种子填充
所谓边界表示,就是指规定边界颜色,若当前像素点颜色是边界颜色,那么我们认为到达图形边界,对该像素点我们不进行绘制,并且也不会从这个点出发进行像素点的扩展;反之,若当前像素点颜色不是边界颜色,那么无论它是什么颜色的,我们都认为这是属于待填充图形的内点,需要进行绘制并且向外扩展。
内点表示的种子填充
所谓内点表示,就是规定待填充图形内部像素点的颜色特征,若当前像素点颜色是内点颜色,那么我们认为该点在图形内部,进行颜色绘制并且向四周扩展;反之,若当前颜色不是内点颜色,那么无论它是什么颜色,我们都认为当前已经到达了图形边界,那么对该像素点不行绘制处理并且也不能向外扩展。
四连通扩展方式
假设当前判断像素点(xpos, ypos)应该绘制,那么我们在绘制这个点的颜色后,应当对这个点四周的点是否也应当进行绘制,四连通方式就是对这个点的左、上、右、下四的像素点逐一进行类似操作(是否绘制以及扩展操作),进而递归式的扩展到整个区域。
八连通扩展方式
假设当前判断像素点(xpos, ypos)应该绘制,那么我们在绘制这个点的颜色后,应当对这个点四周的点是否也应当进行绘制,八连通方式就是对这个点的左、左上、上、右上、右、右下、下、左下八的像素点逐一进行类似操作(是否绘制以及扩展操作),进而递归式的扩展到整个区域。
四连通VS八连通
四连通和八联通作为两种像素向四周扩展的方式,在绘制效果上存在明显的不同。八连通区域的边界是四联通式的,四连通区域的边界是八连通式的。例如对于下图所示的3*3区域来讲,八连通边界(下图右)比四联通区域(下图左)多了四个像素位置。
这对图形的填充有什么影响呢?具体来讲,假设存在如下图所示的多边形,我们分别考量该图形中一点被选中为初始点后的四联通/八连通填充方式的结果:
对于四联通方式的填充来讲填充结果正常(如下图左所示),但是对于八连通方式的填充来讲,在绘制进行到斜边边缘的像素点时,由于像素点会向左下方((x,y)->(x-1, y-1))位置进行扩展,填充就会扩展到图示多边形外侧(如下图右所示)。
我们可以发现,对于水平线或者垂直线而言,八连通填充方式不会存在填充问题,但对于线宽为1的斜线而言,就存在可能会填充预期之外的部分。为避免这种填充上的问题,我们可以将包围线设置为线宽>1即可。
代码实现
在上述原理介绍部分,我们采用递归的方式进行理解,但是在实际的代码实现中,为例避免”要绘制的像素点过多->递归层数过多->爆栈“情况的发生,我采用队列的方式进行实现。
四连通--边界表示
void BoundFill4(int x, int y, unsigned long boundcolor, unsigned long fillcolor)
{
CClientDC dc(mView);
std::queue<std::pair< int, int> > qpt;
qpt.push(std::pair<int, int>(x, y));
while (!qpt.empty())
{
std::pair<int, int> pnow;
pnow = qpt.front();
qpt.pop();
int color = dc.GetPixel(pnow.first, pnow.second);
if ((color != boundcolor) && (color != fillcolor))
{
dc.SetPixel(pnow.first, pnow.second, fillcolor);
qpt.push(std::pair<int, int>(pnow.first - 1, pnow.second));//left
qpt.push(std::pair<int, int>(pnow.first, pnow.second + 1));//top
qpt.push(std::pair<int, int>(pnow.first + 1, pnow.second));//right
qpt.push(std::pair<int, int>(pnow.first, pnow.second - 1));//bottom
}
}
}
四连通--内点表示
void FloodFill4(int x, int y, unsigned long innercolor, unsigned long fillcolor)
{
CClientDC dc(mView);
std::queue<std::pair< int, int> > qpt;
qpt.push(std::pair<int, int>(x, y));
while (!qpt.empty())
{
std::pair<int, int> pnow;
pnow = qpt.front();
qpt.pop();
int color = dc.GetPixel(pnow.first, pnow.second);
if (color == innercolor && color != fillcolor)
{
dcSetPixel(pnow.first, pnow.second, fillcolor);
qpt.push(std::pair<int, int>(pnow.first - 1, pnow.second));//left
qpt.push(std::pair<int, int>(pnow.first, pnow.second + 1));//top
qpt.push(std::pair<int, int>(pnow.first + 1, pnow.second));//right
qpt.push(std::pair<int, int>(pnow.first, pnow.second - 1));//bottom
}
}
}
八连通--边界表示
void BoundFill8(int x, int y, unsigned long boundcolor, unsigned long fillcolor)
{
CClientDC dc(mView);
std::queue<std::pair< int, int> > qpt;
qpt.push(std::pair<int, int>(x, y));
while (!qpt.empty())
{
std::pair<int, int> pnow;
pnow = qpt.front();
qpt.pop();
int color = dc.GetPixel(pnow.first, pnow.second);
if ((color != boundcolor) && (color != fillcolor))
{
dc.SetPixel(pnow.first, pnow.second, fillcolor);
qpt.push(std::pair<int, int>(pnow.first - 1, pnow.second));//left
qpt.push(std::pair<int, int>(pnow.first, pnow.second + 1));//top
qpt.push(std::pair<int, int>(pnow.first + 1, pnow.second));//right
qpt.push(std::pair<int, int>(pnow.first, pnow.second - 1));//bottom
qpt.push(std::pair<int, int>(pnow.first - 1, pnow.second + 1));//left+top
qpt.push(std::pair<int, int>(pnow.first + 1, pnow.second + 1));//right+top
qpt.push(std::pair<int, int>(pnow.first + 1, pnow.second - 1));//right+bottom
qpt.push(std::pair<int, int>(pnow.first - 1, pnow.second - 1));//bottom=left
}
}
#ifdef USEMEMDC
SwapBackBuffer();
#endif
}
八连通--内点表示
void FloodFill8(int x, int y, unsigned long innercolor, unsigned long fillcolor)
{
CClientDC dc(mView);
std::queue<std::pair< int, int> > qpt;
qpt.push(std::pair<int, int>(x, y));
while (!qpt.empty())
{
std::pair<int, int> pnow;
pnow = qpt.front();
qpt.pop();
int color = dc.GetPixel(pnow.first, pnow.second);
if (color == innercolor && color != fillcolor)
{
dc.SetPixel(pnow.first, pnow.second, fillcolor);
qpt.push(std::pair<int, int>(pnow.first - 1, pnow.second));//left
qpt.push(std::pair<int, int>(pnow.first, pnow.second + 1));//top
qpt.push(std::pair<int, int>(pnow.first + 1, pnow.second));//right
qpt.push(std::pair<int, int>(pnow.first, pnow.second - 1));//bottom
qpt.push(std::pair<int, int>(pnow.first - 1, pnow.second + 1));//left+top
qpt.push(std::pair<int, int>(pnow.first + 1, pnow.second + 1));//right+top
qpt.push(std::pair<int, int>(pnow.first + 1, pnow.second - 1));//right+bottom
qpt.push(std::pair<int, int>(pnow.first - 1, pnow.second - 1));//bottom=left
}
}
}