总目录:http://blog.csdn.net/iloveas2014/article/details/38304477
4.5.2 矩阵卷积的ActionScript实现
下面我们就尝试着在ActionScript里实现它,哇,那岂不是要操作像素的吗?请放心,跟ColorMatrixFilter一样,ActionScript也帮我们实现了卷积的滤镜——ConvolutionFilter:
ConvolutionFilter(matrixX:Number = 0, matrixY:Number = 0, matrix:Array = null, divisor:Number = 1.0, bias:Number = 0.0, preserveAlpha:Boolean = true, clamp:Boolean = true, color:uint = 0, alpha:Number = 0.0)
其中,前3个参数与矩阵相对应,因为matrix是一个一维数组,如果直接传9个1,那它可以是个3*3矩阵,也可以是9*1或者1*9,为此我们需要3个参数,分别代表行数,列数及矩阵的具体数值。
而第4个参数divisor则对应上面所说的因子了,在绝对平均分配的那个矩阵里,divisor=9,而修正为按圆扩展的矩阵里,divisor=11。
divisor的值不一定要跟矩阵内元素的总和相等,当divisor小于矩阵元素之和,颜色和alpha的成分就会增多,色彩会变明亮,或者呈现淡入的状态,反之变暗,或透明度淡出,而对于相等的情况,通常情况下也会有缓慢的淡出,因为计算结果的小数部分会被丢弃掉。
考虑到divisor的可变性,Adobe没有把这个因子写死在flashPlayer的底层上,而供我们自行设置。
其它参数我将结合实例进行讲解。
本书制作的效果的都尽可能地不使用现成的素材(微软雅黑字体除外),所以此处我们依然用一个TextField进行测试。
新建一个ActionScript项目,名为ConvolutionFilterTest,代码如下:
package{ [SWF(width = "800", height = "600")] public class ConvolutionFilterTest extends Sprite{ private var _main_txt:TextField; public function ConvolutionFilterTest(){ init(); } private function init():void{ initMainTxt(); } private function initMainTxt():void{ _main_txt = new TextField(); _main_txt.autoSize = TextFieldAutoSize.LEFT; _main_txt.selectable = false; _main_txt.defaultTextFormat = new TextFormat("Microsoft YaHei", 80, null, true); _main_txt.text = "Flash艺术编程"; _main_txt.filters = [new ConvolutionFilter(3, 3, [1, 1, 1, 1, 1, 1, 1, 1, 1], 9)]; addChild(_main_txt); } } }
运行效果如图 4.49所示,没什么特别嘛,跟设置滤镜前的状态没啥两样。
图 4.49 测试卷积滤镜
也是可以理解的,毕竟只是个纯色文本,再怎么交换颜色都还是黑色,而且只跟周边的1像素进行卷积,效果不明显也在情理之中。
既然如此,我们就不妨把卷积矩阵做大一点,扩充它的影响范围。
从3*3扩展到5*5(之所以不考虑4*4,是因为4*4没办法对齐到中心),矩阵数组的长度从9飙升到25。如果再要测试7*7,这么一个个手动敲,工作量可不少,而且容易出错。所以,在不考虑运行效率的情况下,我更倾向于书写一个生成指定尺寸矩阵数组的方法:
private function getConFilter(matrixSize:int, bias:Number = 0, preserveAlpha:Boolean = true, clamp:Boolean = true, color:uint = 0, alpha:Number = 0):ConvolutionFilter{ var amount:int = matrixSize * matrixSize; var arr:Array = []; for(var i:int = 0; i < amount; i ++){ arr[i] = 1; } return new ConvolutionFilter(matrixSize, matrixSize, arr, amount, bias, preserveAlpha, clamp, color, alpha); }
有了这样的一个方法,我们就可以轻松创建任意尺寸的方阵(行数=列数的矩阵叫方阵)了,如果在乎运行效率,就可以运用我之前说的代码片断思想去生成不需要运行时计算的代码,让开发运行两不误。
接下来把滤镜设置一句修改为:
_main_txt.filters = [getConFilter(5)];
现在我们只需要调整getConFilter内的参数就可以随意设置滤镜的作用范围了(这当然还不包含小数的情况)。
但运行后发现也还没有任何效果,何故?
此时我们就应该静下心来好好分析了。首先分析RGB,它们的通道值全等于0,而透明色,它由于全透明,所以无法携带RGB通道,通道值也等于0,于是就无法交换了。所以整个文本里,只有alpha至少通道包含了0和1这两个值。那么,我们不妨看看该滤镜是否包含了与alpha通道相关的参数。
扫视一番之后,我们发现两个这样的参数:preserveAlpha和alpha,其中alpha很显然是一个不透明度值,它看起来与通道设置不太相关,那我们看看preserveAlpha,它意为是否要保留着alpha通道,使其不发生变化,默认为true,而我们只有alpha通道可以实施变换,因此需要把该参数设置成false。
设置以后,效果如图 4.50所示。可以看到,边缘部分变模糊了,如果不断调大滤镜,或者重复使用,结果将会像烟雾一样缓缓消散。
图 4.50 设置preserveAlpha为false
找到问题的症结所在以后,我们不妨把矩阵尺寸调整回3*3,便得到如图 4.51的效果,虽然整体字型没有发生太大的变化,但是我们看到边缘变模糊了,这对于设备文本来说非常有用,用了它,边缘的锯齿大部分可以被消除,当然了,此法在消除锯齿方面也不是万能,比如小字号字体,轮廓较复杂的文字等,这些我们在后续章节再深入讨论。
图 4.51 将矩阵尺寸设置为3*3
如果说之前没看到滤镜效果是RGB通道之间无差异所引起的,如果说全透明色的RGB值都等于0,那么我把文本改成别的颜色,色彩就应该能发生交换了。如图 4.52,我把文本颜色改为纯红,并且把滤镜的尺寸调为5*5,文本边缘被描上了一条暗红色的边线(为什么是暗红,这跟alpha=0时的RGB数值有关,请读者自己思考)。
图 4.52 设置文本颜色为纯红
下面把模糊调大一点,比如20再看看效果(图 4.53)。
图 4.53 设置矩阵尺寸为20*20
模糊出来的结果附带了很多渣渣,而且出现了很多纵横交错的线条……似乎就是方形矩阵的副作用了。所以我们不妨尝试一下那种根据距离进行色彩分配的矩阵。
不过在进入之前,我想先让大家观察一下,是不是有种很熟悉的感觉?是不是跟之前模拟出来的某种渣渣有那么一些相似之处?没错,Flash的简单滤镜也有这样的问题!那到底Flash滤镜里用到的模糊是不是也基于这种模式呢?我们不妨换成BlurFilter看看:
_main_txt.filters = [new BlurFilter(20, 20)];
效果如图 4.54所示,渣渣还是掉了一地,而且调渣程度比ConvolutionFilter还高,再者,方块的感觉也出来了,就是颜色深了一点。所以,我们不妨尝试通过修改某些参数让ConvolutionFilter的效果与这个BlurFilter达到比较相似的效果。
图 4.54 测试BlurFilter的效果
首先BlurFilter的掉渣程度比较严重,所以我们不妨把模糊值改到10。因为BlurFilter的模糊值指的是模糊半径,而ConvolutionFilter的20*20矩阵中,将当前点置于变换矩阵的中心后,它也只与周边的10像素范围内的矩形区域发生关系。
BlurFilter调整为10后,效果如图 4.55所示。此时掉渣程度及其轮廓都已经与20*20的卷积滤镜相当接近了,但颜色却比卷积滤镜深了好多。
图 4.55 将BlurFilter的模糊值设置为10
既然模糊已经定型,那么我们就只能在ConvolutionFilter的颜色上做调整了。因为我们知道这个是纯黑的图像,RGB不可能变大,所以颜色淡肯定是alpha降低的结果,因此,我们要让计算的结果值变大,让文本变得更不透明一些。
逐个设置矩阵元素的做法有点不太科学,所以我们通过调整divisor参数对色彩进行整体调整,看起来乘以0.5,让alpha刚好放大一倍可以得到不错的结果:
return new ConvolutionFilter(matrixSize, matrixSize, arr, amount * 0.5, bias, preserveAlpha, clamp, color, alpha);
得到如图 4.56的效果,跟BlurFilter已经没什么两样了。
图 4.56 设置divisor等于矩阵元素总和的一半
由此可见,简单滤镜在实现模糊的过程中,用的就是ConvolutionFilter的矩阵卷积算法。至于这里面的乘以0.5,笔者也不会过于武断,单凭这一次的实验结果就确定了下来,因此,在接下来的时间里,笔者还继续对多种模糊值(包括不等的blurX和blurY)进行试验,并查阅与模糊算法相关的资料,才最终得出乘以0.5的结论。
这种模糊矩阵在图形学上也有一个专有名词:均值滤波法,因为其本质就是取周围像素的平均值作为新值应用到当前像素上。