最近写了一个画板小程序,为了实现画板的填充功能,研究了一下图形学填充算法。我在网上查找了大量的资料,最终找到了一个效率很高的填充算法。可以在minicode看到我的画板程序,测试下填充功能的速度。
下面我将讲述我的算法实现过程。
网上有很多讲解种子填充算法的,大家一搜就知道有4联通的方式和8联通的方式。但是4联通和8联通的算法效率太低了,根本无法用来实现画板的填充功能。 4联通的算法,我简单描述一下:
8联通算法与4联通算法同理。 至于它们的代码,比较简单,我就偷懒不写出来了。
这个算法是我在一篇文章中看到的,我不知道这个算法是谁想出来的,但是它的效率经过我的实践确实很高,满足我的要求。 填充算法的目标是用新的填充颜色填充所有与起始像素相连的同颜色的像素。假设有如图3x4的像素块,填充起始点在(1,3)。创建一个栈,将起始点入栈。
var pixelStack = [[1, 3]];
接下来执行弹栈操作,栈顶元素为(1, 3),也就是起始点。我们从这个点开始向上走,直到走到边界或者与起始点颜色不同的像素点时停止。
然后再回头向下开始,填充颜色,首先将(1, 0)点填充为红色并设置两个布尔变量reachLeft和reachRight为false。
var reachLeft = false;
var reachRight = false;
这时判断其左边的像素点是否与起始点颜色相同,如果相同,将左侧像素点入栈并将reachLeft设置为true。因为此时右边为黑色,与起始点颜色不一样,所以不需要将右边的像素入栈。
reachLeft = true;
继续向下走,将下面一个像素染成红色。
此时因为reachLeft为true,reachRight为false,所以左侧不需要判断,只需要判右侧是否满足填充像素的要求,可以看到右侧像素点与起始点像素颜色相同,所以将其入栈,并将reachRight设置为true。
reachRight = true;
继续,此时左侧为黑色方块与起始点像素色颜色不同,我们将reachLeft重置为false。
reachLeft = false;
继续将下一个像素点染色。
此时,其左侧像素(0, 3)与起始颜色相同,并且reachLeft为false,所以将(0, 3)入栈。
继续染色,直到到达边界或者下一个像素点颜色与起始点颜色不同。
这样一轮染色就完成了,我们需要继续弹栈直至清空栈内元素,所以接下来从(0, 3)开始,重复上述过程。
下图是接下来所有的染色过程。
一个更复杂的例子:
可以在我的画板程序中的colorFill.ts文件中找到我的实现代码。
画板:https://github.com/pengfeiw/react-paint 。
(完)