如果你是刚接触as编程或者现在只接触flex开发,或许你会对BitmapData不太了解,也或许你会对它不屑一顾。
但作为一个资深的as开发者,无论是游戏开发还是web应用。
BitmapData是你不可不了解的一个基础核心。为什么说它是核心,因为在as开发中BitmapData是我们处理图像的基类。
BitmapData可以为我们做什么?很多哦开发者或者老开发人员也可能不太了解
。BitmapData的存在可以大大的优化你的图像处理能力。小至一个简单的效果,大至一个3d引擎。BitmapData可以说任何地方都能发挥其优越性。只要你能深入了解它,它将带给你对as的一个高层次提升。
by 吾系衰人
@2009.6.19
Email :Wxsr2008@126.com
blog:
http://www.blogbus.com/
qq:457691057
下边我结合一些例子给大家详细的解说下,BitmapData对于我们究竟用处有多大。
一。可能你用过flex的image组件或者你要写一个简单的图片自适应问题而苦恼,又或者你现在对于处理你的相册应用开销而烦恼,诸如此类的图片大小适应问题将在这节为你详细说说。
对于刚接触as编程的人,对于一个图片自适应问题可能很是苦恼,要么花费大量的判断去对一个加载进来的loader的大小进行判断处理,但你有没有考虑过用BitmapData来解决你的问题呢?
如果你认为自己是一个老程序员应该要第一时间考虑到BitmapData的解决这类问题。而不是花费大量时间去处理如sprite,movieclip等可是对象的大小。因为那样的处理大大的提高了你的开销,并不可取。
这类问题的一般难点在于,我们要处理的图像大小不一致, 怎么可以使得一个位置大小的图像去适应一个已知大小的容器呢?
处理这类问题我们主要用到BitmapData的draw的方法。
接下来的问题是首先我们需要然原图像能够整个的现实在我们要显示的容器上,其次,我们还需要等比例拉伸我们的图像使得他不会变形。
draw虽然可以帮我们解决这里问题,但怎么个思路呢?
这里您需要先了解BitmapData的一个特性,如果你有BitmapData的使用经验,你可能也会发觉知道BitmapData.draw()这个方法对于位于为负数坐标的像素是无法绘制的。
知道了问题所在之后我们再看看 BitmapData.draw()这个方法的一些参数说明
- public function draw(source:IBitmapDrawable, matrix:Matrix = null, colorTransform:ColorTransform = null, blendMode:String = null, clipRect:Rectangle = null, smoothing:Boolean = false):void
复制代码
参数
source:IBitmapDrawable — 要绘制到 BitmapData 对象的显示对象或 BitmapData 对象。 (DisplayObject 和 BitmapData 类实现 IBitmapDrawable 接口。)
matrix:Matrix (default = null) — 一个 Matrix 对象,用于缩放、旋转位图或转换位图的坐标。 如果不想将矩阵转换应用于图像,请将此参数设置为恒等矩阵(使用默认 new Matrix() 构造函数创建),或传递 null 值。
colorTransform:ColorTransform (default = null) — 一个 ColorTransform 对象,用于调整位图的颜色值。 如果没有提供任何对象,则不会转换位图图像的颜色。 如果必须传递此参数但又不想转换图像,请将此参数设置为使用默认 new ColorTransform() 构造函数创建的 ColorTransform 对象。
blendMode:String (default = null) — 来自 flash.display.BlendMode 类的一个字符串值,指定要应用于所生成位图的混合模式。 clipRect:Rectangle (default = null) — 一个 Rectangle 对象,定义要绘制的源对象的区域
。 如果不提供此值,则不会进行剪裁,并且将绘制整个源对象。
smoothing:Boolean (default = false) — 一个布尔值,用于确定因在 matrix 参数中指定缩放或旋转而对 BitmapData 对象进行缩放或旋转以后,是否对该对象进行平滑处理。 smoothing 参数只有在 source 参数是 BitmapData 对象时才适用。 在将 smoothing 设置为 false 的情况下,经过旋转或缩放的 BitmapData 图像可能会显得像素化或带有锯齿。 例如,下面两个图像的 source 参数使用同一个 BitmapData 对象,但对左侧的图像,smoothing 参数设置为 true,对右侧的图像,该参数设置为 false: 在将 smoothing 设置为 true 的情况下绘制位图要比在将 smoothing 设置为 false 的情况下执行相同操作更为缓慢。
引发 ArgumentError — source 参数不是 BitmapData 或 DisplayObject 对象。 SecurityError — source 对象及(就 Sprite 或 MovieClip 对象而论)其所有子对象与调用方不在同一个域中,或者不在调用方可通过调用 Security.allowDomain() 方法访问的 SWF 文件中。 ArgumentError — 源为空或不是有效的 IBitmapDrawable 对象。
通过对参数的了解我们其实可以发觉 draw()这个方法其实早就已经考虑到这种问题了,只是一般人不了解参数的使用望而却步而已。。 恩,我们可以使用参数 矩阵(matrix:Matrix) ,及 矩形(clipRect:Rectangle )这两个参数来帮助我们处理 这两个参数主要提供了像素的矩阵乘积及图像的范围限制 来为我们解决上述问题, 知道了解决问题的缺口,那么现在 我们要做的就是, 我们根据上边的分析可以定出我们的方法所需要的必要参数,
- public function draw(container:DisplayObjectContainer,target:DisplayObject,Number=150,height:Number=200):BitmapData { }
复制代码
我们在定义一个矩阵matrix为调用draw时必须注意一下几点,a.让图像的像素都调整到以(0,0)点开始。
b .我们还要考虑到可是对象的大小伸缩问题,因为draw是对可是对象的内存缓存的一个渲染。所以如果可是对象发生的大小伸缩,draw出来的图像是不会伸缩的,
c. 怎么让draw出来的图像适应我们的容器等
这3个问题都在定义矩阵时必须考虑好的。 要让绘制出来的图像适应容器的大小我们必须计算出一个比例,这个比例主要是用于对图像的等比例缩放用的, 要让两个大小不一样的对象适应我们首先得根据一个比例来缩小图像,而这个的依据就是图像的宽度或者高度那个最大,那个大我们就以那个为参照伸缩,
方法的实现过程大致如下
- private function getScale(Number,height:Number):Number {
- var size:Number; var scale:Number; var num:Number=this.width-this.height var num2:Number=width-height num2<0?size=size=height; num>0?scale=size/this.scale=size/this.height; return scale
- }
复制代码
通过这样的处理我们就可以得到一个缩放的比例, 然后下边就是绘制一张完整的图像,像之前说的那样我们在draw时还需要对将像素进行平移, 平移的依据我们可以根据getBounds这个放来来获取我们要绘制的对象的信息矩形, 在得到图像的信息后我们只要让 matrix的tx,ty减去这个像素的开始点就好~因为如果像素开始点是负数,那么我们 减去实际是在x轴跟y轴增加相应像素,这个处理完后我们还要处理掉可是对象原先的缩放问题 于是我们得到下边的过程
- public function draw(container:DisplayObjectContainer,target:DisplayObject,Number=150,height:Number=200):BitmapData { var rect:Rectangle=target.getBounds(target) var scale:Number=getScale(width,height); var vw:Number=(width-target.width*scale)/2; var vh:Number=(height-target.height*scale)/2; this.samllMapBMD=new BitmapData(width,height,true,0); var matrix:Matrix=new Matrix; matrix.tx-=rect.topLeft.x; matrix.ty-=rect.topLeft.y; matrix.scale(scale,scale); matrix.scale(target.scaleX,target.scaleY) matrix.tx+=vw; matrix.ty+=vh; var rect2: Rectangle=new Rectangle(0,0,width,height) var bitmapdata:Bitmapdata=new Bitmapdata(rect2.width,rect2.height,true,0) bitmapdata.draw(target,matrix,null,null,rect2) return bitmapdata
- }
复制代码
通过上边的讲解大家现在应该了解怎么去绘制一个自适应图像了~ 这个应用的方法扩展还可以让你去 实现一些诸如随机图片抢等难度相对应大点的应用,因为现在你的图像可以任意适应任何大小的容器,所以你只要预先根据加载对象的高宽特向随机出几个大小不一的容器就可以很方便的实现那样的效果了~
--------------------------------------------------------
2.扣图,
为什么我们要去扣图?怎么样去扣图?,
首先回答第一个问题,扣图的一般应用主要是为了处理一些png图片的大小问题,我们都知道bitmapdata的大小消耗是bitmapdata的高宽大小决定的,因为一张png图包含的透明部分而设计往往懒于将每张图片都做到最小,所以一般这部分应该使用程序实现的,我们可以编写一个air项目对图片惊醒批处理,也可以根据实际在程序运行期间实现对图像大小的优化。
主要方法我们用到getColorBoundsRect来获取非透明区域的大小getColorBoundsRect的参数可以方便的让我们赛选出非透明区域 处理过程如下: bitmapdata.getColorBoundsRect(0xFF000000,0x00000000,false) 可能你会以为为什么我要定义这样两个参数来排除区域,
首先,我们应该知道 png图是ARGB制式,也就是说出类红绿蓝3通道外还带一alpha的透明通道 一个16进制的通道值大致是0xAARRGGBB 除0x外每两个数代表一组,前两个代表通明通道,ff是最大值,00是最小值,其他3通道一样。
getColorBoundsRect参数的说明如下
mask:uint — 一个十六进制值,指定要考虑的 ARGB 颜色的位。 通过使用 &(按位 AND)运算符,将颜色值与此十六进制值合并。 color:uint — 一个十六进制值,指定要匹配(如果 findColor 设置为 true)或不 匹配(如果 findColor 设置为 false)的 ARGB 颜色。 findColor:Boolean (default = true) — 如果该值设置为 true,则返回图像中颜色值的范围。 如果该值设置为 false,则返回图像中不存
上边的0xFF000000代表的是透明度最大,0x00000000代表的是完全透明的 通过对 getColorBoundsRect参数的理解 我们知道 mask是一个颜色值合并参数,他会先对bitmapdata的所有像素进行合并,然后根据第二个匹配参数 也就是完全透明的的值进行匹配,最后根据findColor返回匹配的结果, 正果过程就是将bitmapdata的所有像素匹配完全透明值,返回非透明的实际矩形范围。 这样子我们就可以得到我们所需要的实际包图像范围了~ 再来就是扣图, 扣图我们用到BitMapData.threshold() 通过这个方法我们可以很方便排除我们不想要的颜色值来获取我们实际需要的图像,原理跟上边说的大致一样,大家可以参考阅读下就是了。
3.怎么简单的跟精确的判断是否 碰撞到png图的具体有效范围,
对于游戏开发者来说可能这个问题是经常会考虑一个问题,怎么判断我们是否点击了一张png图的具体非透明像素。 简单的方法我们可以用bitmapdata的。hitTest方法,但这个方法需要我们提供一个点击点,但在我们不知道该点的前提呢? 说这个问题前我在这里先说说 hitTest方法,的实现原理,其实原来很简单,就是根据一个点及其他参数去判断它的颜色,是否属于某张bitmapdata, 下边说说怎么简单的自己实现该方法的功能: 因为是简单的针对性实现所以我们只需要一个点就可以了
-
function hitTest(parent:DisplayObject,target:BitmapData,point:Point):Boolean {
- var rect: Rectangle=target.getBounds(parent)
- if(rect.containsPoint(point)){
- var alphaValue:uint = bmd.getPixel32(point.x,point.y) >> 24 & 0xFF;
- if(alphaValue==0x00000000){
- return false
- }
- return true
- }
- return false
- }
复制代码
之所以说这个例子我是想大家知道怎么去运用一些还没知道的细节过程以及让大家更深刻的了解bitmapdata的一些使用方法; 像刚才的例子, 我实现的原理是先获取我们要判断的对象的范围矩形,然后根据我们提供的哦那个点去判断该点是否在该矩形范围内。然后在对于在范围内的情况通过getPixel32方法获取该点下的像素 里边有个位操作,用于提取alpha通道的值,
var alphaValue:uint = bmd.getPixel32(point.x,point.y) >> 24 & 0xFF;
同样的只要你了解16进制的构造,
你也可以通过如下方法提取其他通道的颜色值
- var redValue:uint = bmd.getPixel32(point.x,point.y) >> 16& 0xFF;
- var greenValue:uint = bmd.getPixel32(point.x,point.y) >> 8 & 0xFF;
- var blueValphaValue:uint = bmd.getPixel32(point.x,point.y) >> 0 & 0xFF;
复制代码
对于RGB类型改方法同样有效。 最后当然就是判断这个透明度值看看是否透明,同理的你可以扩展它的这个判断,诸如对透明度的限定, 透明度之前也已经提过了;他颜色通道的取值范围都是-255到255 你可以用数字,也可以直接用16进制 )0xAA000000 你只要调整AA的范围即可了~这里也不多说了。
这里我们简单的实现怎么去碰撞一个图片的一个点 那么多张图片的呢?我们怎么去判断?
- public static function getChildUnderPoint(parent:DisplayObjectContainer,point:Point,items:Array=null):DisplayObject {
- var t:Number=getTimer() var recordArray:Array=[] var tmp:Array=[] var tmp2:Array=[] var index:int=0; var result:DisplayObject for(var i:int=0;i< items.length;i++) { var item:BaseSprite=items[i] if(item){ var rect:Rectangle=item.getBounds(parent) if(rect.containsPoint(point)){ var itemDepth:int=item.parent.getChildIndex(item) tmp.push({depth:itemDepth,item:item}) } } } tmp.sortOn('depth',Array.NUMERIC|Array.DESCENDING ) var alphas:Array=[] for(var j:int=0;j> 24 & 0xFF; if(alphaValue>40){ result=target break } } return result }
复制代码
在这里我就简单的说说实现过程,其他的大家自己去思考吧 首先我们要提供一个判断对象的容器,及我们要判段的列表内容。 然后通过判遍历判断那些对象在我们的鼠标下,当然你也可以用underPoint这个方法getObjectsUnderPoint这个方法去获取,但这个方法有一个太过广泛了,获取的类型也未必是我们需要的,所以我一般的处理方法好是遍历对象是否在鼠标点下即可,(当然你也可以根据需要结合使用,毕竟getObjectsUnderPoint是缓存的对象列表,那样可以为我们提高我们去提去对象列表时的效率 。) 然后我们之前说的 透明度判断方法即可准确的知道我们具体碰撞到那个点上了。 有一点大家要注意的就是我这里只是对同一容器下的对象进行判断,对于嵌套多个对象的或者跨层级堆栈的对各对象时我们可以用他们夫级深度乘以一个大的基数再加上该物体的自身深度的方式来处理。
-----------------------------------------------------------------
4。杂点,颗粒,效果 这些效果我们都可以通过自带的方法来实现,而且只要你巧妙结合,可以实现的效果可以说多之又多 这里我只是简单的提提一些方法就是了,剩下的大家就自己细考去吧
- merge(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, redMultiplier:uint, greenMultiplier:uint, blueMultiplier:uint, alphaMultiplier:uint):void
复制代码
对每个通道执行从源图像向目标图像的混合。
- BitmapData noise(randomSeed:int, low:uint = 0, high:uint = 255, channelOptions:uint = 7, grayScale:Boolean = false):void
复制代码
使用表示随机杂点的像素填充图像。
- BitmapData paletteMap(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, redArray:Array = null, greenArray:Array = null, blueArray:Array = null,
- alphaArray:Array = null):void
复制代码
重新映射一个具有最多四组调色板数据(每个通道一组)的图像中的颜色通道值。
- BitmapData perlinNoise(baseX:Number, baseY:Number, numOctaves:uint, randomSeed:int, stitch:Boolean, fractalNoise:Boolean, channelOptions:uint = 7, grayScale:Boolean = false, offsets:Array = null):void 生成 Perlin 杂点图像。 BitmapData pixelDissolve(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, randomSeed:int = 0, numPixels:int = 0, fillColor:uint = 0):int
复制代码
执行源图像到目标图像的像素溶解,或使用同一图像执行像素溶解。
这4个方法都可实现杂点,颗粒效果,想一些色彩斑斓的颗粒,杂点效果一般都是先通过perlinNoise方法也就是Perlin 杂点算法再通过getPixel32 () 或者getPixel()随机取点,然后通过 对bitmapdata的像素级操作实现的 还有就像倒影 ,色彩填充,这些效果只要你对bitmapdata足够了解,你都肯依很轻松的实现, 想倒影我们可以先过draw的方法绘制出原图的拷贝然后透过参数矩阵进行颠倒,再通过alpha或者滤镜就可以实现了。 还有图片级别的非透明区域图像填充,对是图像填充而不是像素填充,这个区别在于我们可以以一张位图作为填充对象去填充一张png位图,可鞥大家会说可是对象的绘图api里也提供了一个位图 填充方法graphics.beginBitmapFill 但他们的区别是这个api只对矢量区域填充而不支持位图的非透明区域填充 大致的原理我这里可以说说, 我们需要先 获取png图的最小有效矩形,然后通过threshold方法进行颜色替换, 5.像素混合,位图滚动,可视对象的缓存 像素混合我们一般要用到merge方法,如果是一般可是对象的话我们一般通过设置blendMode来设置其的混合效果 但大家得注意这些方法的消耗一般都比较高,但对一一些复杂的效果这两个方法倒是相当有用。 还有就是位图缓存 可是对象的 cacheAsBitmap为我们提供可一个位图缓存方法,正确使用的话我们程序的效率会得到整体提高,但我们要注意什么时候用什么时候不用,说这个问题就得先说说cacheAsBitmap过后的一个特点,就是每当你的眼神或者大小等发生变化时他都必须得重新绘制一边,也就是说大家使用它时一般只应该对于那些只涉及坐标移动等变化的时使用效率方能提高如果你的角度,颜色等也发生了变化,那么使用它只会徒增开销。 最后说说位图滚动,恩,基本上你要你的程序滚动时效率高我们都应该用scroll(x:int, y:int):这个方法, 而不是移动xy坐标,注意的是xy坐标的移动在你缓存过后也是使用scroll(x:int, y:int):方法,所谓这是为什么缓存后移动开销会那么小一的一原因。