http://bbs.9ria.com/thread-73321-1-1.html
BitmapData 基础部分2
这篇文章是我对flash.display.BitmapData对象的第二篇总结.第一篇地址.
首先让我们来了解一下颜色
操作颜色
所有的显示对象都有一个transform 属性,这个属性允许我们对该显示对象应用各种各样的变换-例如变形和颜色转换等.下面有个转换公式:
var ct:ColorTransform = new ColorTransform(rM, gM, bM, aM, rO, gO, bO, aO);
myDisplayObject.transform.colorTransform = ct;
每个颜色的通道都被转换了:
new red = (rM * old red) + rO
new green = (gM * old green) + gO
new blue = (bM * old blue) + bO
new alpha = (aM * old alpha) + aO
你可能注意到了一旦ColorTransform被设置就不能通过设置null来移除它(你会得到的一个运行时错误);如果要移除它,只需要将一个新的默认值的ColorTransform设置上就可以了.
myDisplayObject.transform.colorTransform = new ColorTransform();
还有,为表现出我们想要的效果我们不得不为其设置一个全新的ColorTransform 对象---设置显示对象ColorTransform 的个别的属性不会产生任何效果.
//won't work:
myDisplayObject.transform.colorTransform.redOffset = 25;
//will work:
//get existing transform
var ct:ColorTransform = myDisplayObject.transform.colorTransform;
//amend transform
ct.redOffset = 25;
//apply transform
myDisplayObject.transform.colorTransform = ct;
这个colour transforming 可以用于任何显示对象(包括Bitmap),所以它是一个非常有用的工具.但是本篇的内容主要为BitmaData;它不是一个显示对象并且对BitmapData对象使用colour transform 时操作会有一点点不同.我认为显示对象的transform 就如同在该对象上覆盖一个图层然后对该图层染色,BitmapData 对象则是直接在该对象上面操作.
myBitmapDataObject.colorTransform(new Rectangle(...), new ColorTransform(...));
Rectangle 对象定义了图像要transform 的区域.
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
import flash.geom.Rectangle;
[SWF(backgroundColor="#CCCCCC", frameRate="30", width="200", height="200")]
public class BitmapDataExample3 extends Sprite
{
private var bm:Bitmap;
private var count:Number = 0;
public function BitmapDataExample3()
{
//do some general housekeeping
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
//create a new BitmapData object filled with random noise
var bmd:BitmapData = new BitmapData(100, 100, false, 0x000000);
bmd.noise(Math.random()*int.MAX_VALUE);
//create bitmap and pass in BitmapData object
bm = new Bitmap(bmd);
//add bitmap to stage
bm.x = 50;
bm.y = 50;
addChild(bm);
//add loop
addEventListener(Event.ENTER_FRAME, loop);
//add listener
stage.addEventListener(MouseEvent.CLICK, reset);
}
//run every frame
private function loop(event:Event):void
{
//define rect to be right half of image
var rect:Rectangle = new Rectangle(50, 0, 50, 100);
//define transform
var ct:ColorTransform = new ColorTransform(1, 1, 1, 1, count);
//apply transform to image
bm.bitmapData.colorTransform(rect, ct);
//update counter
count += 0.1;
}
//called when user clicks anywhere
private function reset(event:MouseEvent):void
{
//remove old bitmapData object
bm.bitmapData.dispose();
//insert new bitmapData object
bm.bitmapData = new BitmapData(100, 100, false, 0x000000);
//fill with noise
bm.bitmapData.noise(Math.random()*int.MAX_VALUE);
//reset counter
count = 0;
}
}
}
复制代码
{BitmapDataExample3}
拷贝颜色
Threshold(...)的作用和part1中的copyPixels很相似,除此之外,根据指定的阈值测试图像中的每个像素值,并将通过测试的像素设置为新的颜色值,如下所示:
var src:BitmapData = otherBitmapData;
var srcRect:Rectangle = otherBitmapData.rect;
var destPoint:Point = new Point(0, 0);
var operation:String = ">=";
var threshold:uint = 0x00800000;
var newColour:uint = 0xFFFFFF00;
var mask:uint = 0x00FF0000;
var copy:Boolean = false;
myBitmapData.threshold(src, srcRect, destPoint, operation, threshold, newColour, mask, copy);
Scr,srcRect和destPoint的作用等同于copyPixels对应参数的作用.另一个参数需要在这里介绍一下;operation和mask 用来判断每个被检测像素点是否符合条件.如果检测通过,则目标像素点被设置为新颜色.如果没有通过检测,则目标像素直接拷贝原始像素.
检测过程其实是一个返回boolean 值(true为通过检测)的操作,它工作原理如下(它的操作符是:“<”, “<=”, “>”, “>=”, “==” 或“!=”):
(pixelValue & mask) operation (threshold & mask)
(译者注peration 为操作符)
在本例中 operation 是“>=”,threshold 是0×00800000,newColour 是0xFFFFFF00 ,mask 是0x00FF0000 所以检测公式就是:
(pixelValue & 0x00FF0000) >= (0×00800000 & 0x00FF0000)
我们可以将其简化为:
(pixelValue & 0xFF0000) >= 0×800000
所以如果pixelValue 与(译者注:逻辑与操作)0xFF0000(ie:红色通道)大于或等于0×800000则使用new的颜色值(0xFFFFFF00 – 黄色) 替代原来的颜色.反之则不做任何操作.如果你觉这一部分就像是在听Klingon 的歌剧(译者注:Klingon 是一种外星人.意指 听天书),你应该去补习一下位操作的相关知识.
{BitmapDataExample4}
Histogram(...)方法用检索图像的颜色通道信息.它需要输入4个分别代表(R, G, B 和A) Vector对象并且返回一个Vector对象.每个Vector都包含了一个介于0-256的数字.
例如:item 47的 红色向量值是100,这就意味着图像中有100个红色通道值为47的像素点.
我们可以通过使用一个矩形参数来定义图像中的的特殊采样区,或者是设置其为null来采样整个图像.
Here’s a stock example that draws a graph of the data returned from histogram(…):
下面是一个通过绘制histogram(…)返回数据的例子:
{BitmapDataExample5}
noise(…)方法的作用在于随机填充,所以图像应该(理想地)会完全的一样(译者注:杂点函数是一个映射函数,不是真正的随机数生成函数,所以它每次都会根据相同的随机种子创建相同的结果),但是我猜测它可能有一个特定值---鼠标点击(产生新的噪点)并没有产生相同的噪点.如果你知道为什么请告诉我---我可能以后会在研究一下.
merge(…) 是另一个和copyPixels很类似的方法---不同之处在于它会融合源图像和目标图像,而不是完全的覆盖源图像.如下所示:
var src:BitmapData = otherBitmapData;
var srcRect:Rectangle = otherBitmapData.rect;
var destPoint:Point = new Point(0, 0);
var r:uint = 128;
var g:uint = 128;
var b:uint = 128;
var a:uint = 128;
myBitmapData.merge(src, srcRect, destPoint, r, g, b, a);
前三个参数和copyPixels 中的参数意义相同,后四个参数决定目标图像和源图像的4个通道值的改变量.每个参数的有效值介于 0(100%提取目标图像)-255 (100%提取源图像).在这里我们将参数全部设置为128,这就意味着从源图像和目标图像的融合度对等.
另一个复制功能的变种方法paletteMap(...):
var src:BitmapData = otherBitmapData;
var srcRect:Rectangle = otherBitmapData.rect;
var destPoint:Point = new Point(0, 0);
var reds:Array = [...];
var greens:Array = [...];
var blues:Array = [...];
var alphas:Array = [...];
myBitmapData.paletteMap(src, srcRect, destPoint, reds, greens, blues, alphas);
相信你已经对前三个参数很熟悉了.剩下的数组应该是分别包含ARGB 的信息(或者他们可以是null).本质上来说,这个方法会根据参数传入的数组将图像的每个像素重新映射成新的颜色.
对于每个像素点,每个新的颜色是这样计算的: a) 源颜色被分成4个通道值介于0-255的整数. B)这四个整数会被当做刚刚的4个数组的索引.c)这四个数组的值被加在一起然后将目标像素的值设置为该值.--如果某个数组为null,则用源图像(这个通道的)的数字代替.我将通过设置特殊值来阐述这一过程:
我们假定源像素的颜色为0xFFFF8000.将其分解成ARGB四通道值:0xFF, 0xFF, 0×80 和 0×00.--或者是10进制:255, 255, 128 和 0.目标像素值和四个数组中值的和相关的:alphas[255] + reds[255] + greens[128] + blues[0].
想着就头疼---我在flash.display.BitmapData的官方文档中找到一个只使用了个2数组和几种颜色的例子.
Filters(滤镜)
应用一个滤镜是非常简单的事儿:
myBitmapData.applyFilter(src, srcRect, destPoint, filter);
对于现在的你,参数名已经可以起到自我解释说明了.
和colorTransform一样,对BitmapData 使用滤镜都会产生持久的效果.此外滤镜的效果范围可能会超出我们定义的区域(虽然没有超出完整的图像)- e.g: 将一个blur x,y为2的滤镜应用于50×50的区域,由于2像素的边缘效果,该滤镜将会在所有方向上延长2像素,即产生54x54的效果区域.这是滤镜自身的一种行为.
应用滤镜的乐趣在于重复嵌套应用滤镜.这里应用了一个滤镜然后绘制一个色块.伴随着时间的不停推移每个色块慢慢变模糊.
{BitmapDataExample6}
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.filters.BitmapFilter;
import flash.filters.BlurFilter;
import flash.geom.Point;
import flash.geom.Rectangle;
[SWF(backgroundColor="#CCCCCC", frameRate="30", width="200", height="200")]
public class BitmapDataExample6 extends Sprite
{
private var bm:Bitmap;
private var filter:BitmapFilter;
public function BitmapDataExample6()
{
//do some general housekeeping
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
//create filter
filter = new BlurFilter(2, 2, 1);
//create a new BitmapData object
var bmd:BitmapData = new BitmapData(100, 100, false, 0x000000);
//create bitmap and pass in BitmapData object
bm = new Bitmap(bmd);
//add bitmap to stage
bm.x = 50;
bm.y = 50;
addChild(bm);
//add loop
addEventListener(Event.ENTER_FRAME, loop);
}
//called every frame
private function loop(event:Event):void
{
//apply a blur
bm.bitmapData.applyFilter(bm.bitmapData, bm.bitmapData.rect, new Point(0, 0), filter);
//draw new block randomly
var rect:Rectangle = new Rectangle(Math.random()*90, Math.random()*90, 10, 10);
bm.bitmapData.fillRect(rect, Math.random()*0xFFFFFF);
}
}
}
任何派生自BitmapFilter的类都可以被使用:BevelFilter, BlurFilter, ColorMatrixFilter, ConvolutionFilter, DisplacementMapFilter, DropShadowFilter, GlowFilter, GradientBevelFilter, GradientGlowFilter 和 ShaderFilter.
很多时候,重复的将一个滤镜应用于同一个BitmapData 最终将会产生一个单色的色块--在上面的例子中,如果我们没有新的方块绘制你会发现BlurFilter会将所有东西都变成黑色(0x0000000).其他的如ConvolutionFilter 滤镜会导致褪变成白色.可以通过同时应用一个colorTransform 对象来避免这一现象---这样就可以平衡滤镜的黑白效果了.
这个例子除了调用applyFilter 时不太一样,其他地方和前面的例子一样:
bm.bitmapData.colorTransform(bm.bitmapData.rect, new ColorTransform(1.012, 1.012, 1.012, 1.012, 0, 0, 0, 0));
这里增加了1.2%的亮度刚刚好可以避免方块的消失.(译者注:参数为作者手动暴力测试出来的)
{BitmapDataExample7}
我强烈建议对其他滤镜进行测试---效果的范围可能是无限制的.使用cpu趋向于密集型的是nvolutionFilter, GradientBevelFilter 和 GradientGlowFilter---其他的都是瞬时的.ShaderFilter 使用PixelBender来编码,所以其快慢取决于其工作量.
源代码