位图(Bitmaps)
与滤镜相同,可以用整本书来介绍 Bitmap 和 BitmapData 类,看起来也不错,但是这并不是本书的目的。我们将通过一些简单的例子,用来指出 AS 2 与 AS 3 中位图处理的变化。
在 AS 2 中,通过调用 BitmapData()函数,新建一个 BitmapData 对象使用如下参数:
new BitmapData (width:Number, height:Number, transparent:Boolean, fillColor:Number)
你也许猜到了, BitmapData 类同样也是嵌入在一个包中,完整的使用名称如下 flash.display.BitmapData。所以需要导入包,对于 width 和 height 参数则非常显而易见, transparent 参数表示创建的图像是否包涵一个 alpha 通道,选择 true 或 false ,fillColor 是创建图像的初始颜色,如果 transparent 为 true 的话,那么位图就用 32 位色表示,0xAARRGGBB,如果为 false 的话,就可以使用 24 位安全色表示。
在创建 BitmapData 对象时,也许很想能看到它的样子。在 AS 2 中,使用 attachBitmap 命令在影片剪辑中添加一个位图。大家也许会想,现在是否可以使用 addChild 在显示对象中添加一个位图,但事实上并没有这么简单。问题在于 addChild 只对继承自 DisplayObject 类的对象起作用,如 Sprite 影片,影片剪辑和文本框。然而,如果我们研究一下类的结构,就会发现 BitmapData ,没有继承自 DisplayObject,所有不能直接添加对象。这就是为什么要有 Bitmap 类的原因, Bitmap 类几乎始终都有一个函数作为 BitmapData 实例的容器,可以这样创建:
var myBitmapData:BitmapData = new BitmapData(100, 100, false, 0xff0000); var myBitmap:Bitmap = new Bitmap(myBitmapData);
现在就可以将对象加入到显示列表了:
addChild(myBitmap);
使其可见后,Bitmap 实例也可以改变位置,进行缩放,增加滤镜等等。 测试这个例子,只需要在第二章给出的类框架的 init 方法加入这三行就可以了,不要忘记导入 flash.display.Bitmap 和 flash.display.BitmapData,运行后就会看到一个红色的正方形。乍看上去,与使用绘图 API 所画的图形没什么不同,但是要知道这并不是矢量图绘制法:填充一个红色的正方形。这是张位图图像,在位图中每一个像素都要分别指定而且是可变的。事实上,每一个像素值都可以使用 getPixel,getPixel32 和 setPixel,setPixel32 进行读取和设置。两个版本的不同之处在于 getPixel 和 setPixel 使用24位色彩值忽略了 alpha 通道,而 “32”版的则使用32位色彩值其中包括了透明度信息。让我们来做个例子,制作一个简单的喷漆工具,就像所有位图喷漆程序一样。 这里是文档类,SprayPaint.as:
package { import flash.display.Sprite; import flash.display.Bitmap; import flash.display.BitmapData; import flash.events.MouseEvent; import flash.events.Event; import flash.filters.BlurFilter; public class SprayPaint extends Sprite { private var canvas:BitmapData; private var color:uint; private var size:Number = 50; private var density:Number = 50; public function SprayPaint() { init(); } private function init():void { canvas = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0x00000000); var bmp:Bitmap = new Bitmap(canvas); addChild(bmp); stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); } private function onMouseDown(event:MouseEvent):void { color = Math.random() * 0xffffff + 0xff000000; addEventListener(Event.ENTER_FRAME, onEnterFrame); } private function onMouseUp(event:MouseEvent):void { removeEventListener(Event.ENTER_FRAME, onEnterFrame); } private function onEnterFrame(event:Event):void { for (var i:int = 0; i < density; i++) { var angle:Number = Math.random() * Math.PI * 2; var radius:Number = Math.random() * size; var xpos:Number = mouseX + Math.cos(angle) * radius; var ypos:Number = mouseY + Math.sin(angle) * radius; canvas.setPixel32(xpos, ypos, color); } } } }
这也许是目前为止最复杂的代码,但除了 BitmapData 的内容外,其它的知识前面都讲过,只不过又使用了一遍而已。一步步来看,首先,创建了一些类变量,包括 canvas 变量,用于存放 BitmapData 的实例。创建的实例尺寸等于舞台的尺寸,并使用透明的背景色。然后使用 canvas 创建一个位图,并加入到显示列表。 鼠标事件处理函数中选择了一个随机的颜色,并且带有添加和删除 enterFrame 事件处理函数的功能。我们来回忆一下三角学,首先,从 0 到 Math.PI * 2 中计算出一个随机的角度,不要忘记使用弧度制表示,相当于随机的360度。然后,计算出一个随机的半径后,再使用三角函数将半径和角度转换为 x,y 值。最后使用 setPixel32 以鼠标位置加上随机的 x,y 值的像素点设置为喷漆色,每一次开始喷漆时随机决定颜色。在这个例子中有一个 for 循环,每一帧都会进行循环,每次循环多少次由 density 的值决定。 color 的值为24位的色彩值,然后加上 0xFF000000,为的是设置 alpha 通道为完全不透明,如果没有加上这个值,那么所有的颜色就都为透明的。如果用 0xFFFFFFFF 乘以 Math.random(),那么颜色的透明度是随机的,也许是你想要的,但不是我想要的。通过改变 density 和 size 的值再测试一下,看看会有些什么不同的效果。大家也许已经想到如何让用户来控制改变这些参数了。
刚刚看到这个程序时,你也许会想,“真是小题大作,完全可以用绘图 API 或通过加载小影片剪辑并改变颜色来实现”。是的,完成可以这么做,但是如果使用绘图 API 绘出成千上万的独立图像后,会发习画得越多,速度越慢。画过几百个图形后,慢下来的速度会变得非常明显,这个程序也就费掉了,使用加载影片剪辑的方式也是如此。但是,使用位图就完全不同了,我们可以使用这个程序喷上一天,都不影响程序的速度或效率。 如果想看到更酷的效果,就把下面一行代码加在位图对象 bmp 的后面:
bmp.filters = [new BlurFilter(2, 2, 3)];
在位图中使用模糊滤镜比在矢量图中使用效果更加明显。当然,设置像素是 BitmapData 对象能做的最简单的操作之一。除了获取和设置像素,BitmapData 对象还有其它二十多种方法,这些方法可用来复制像素,设置阈值,分解,合并,滚动,等等。我个人最喜欢的一个是 perlinenoise 方法,该函数允许我们创建一个随机的有组织的图案。对于制造烟,云和水波纹效果都非常有用。有兴趣的话大家可以试验一下。
读取和嵌入资源
最后一个重点话题是获取外部资源的概念,如在影片中加载位图或外部 SWF 文件。有两种方法,一种是在动画播放时将资源读入,这就是我们所熟知的读取(loading)。另一种方法是在 SWF 编译时嵌入(embed)资源。
读取资源
创建一个 Loader 对象来读取一个资源,这是flash.display.Loader 类的一个实例。 loader 是个显示对象,意味着可以使用 addChild() 方法将它加入到显示列表中,就像 sprite 和 bitmap 一样。然后告诉这个 loader 去读取一个外部 SWF 或外部位图,如 JPEG,PNG,等等。
在 AS 2 中,当处理外部文件路径或 URL 时,只需要使用一个简单的字符串表示路径。 在 AS 3 中,则需要创建一个 flash.net.URLLoader 实例,传入表示路径的字符串,并且还需要一个额外的步骤,虽然有些烦人,但是我们还是要习惯这种用法。 这里是一个在运行时读取外部资源的例子(文档类 LoadAsset.as):
package { import flash.display.Sprite; import flash.display.Loader; import flash.net.URLRequest; public class LoadAsset extends Sprite { public function LoadAsset() { init(); } private function init():void { var loader:Loader = new Loader(); addChild(loader); loader.load(new URLRequest("picture.jpg")); } } }
嵌入资源
虽然在有些情况下,在运行时读取资源很合适,但是在有些情况下有一些外部图形只想加载到 SWF 自里面。这时,如果使用 Flash IDE,可以简单地导入这个对象到库中并设置为“为 ActionScript 导出”。但在使用 Flex Builder 2 或 Flex 2 SDK 命令编译器时,没有库,那么如何在 SWF 中加载外部资源呢?
答案是使用[Embed]元数据(metadata)标签嵌入资源,元数据标签是指加到 ActionScript 文件中的非正式 ActionScript 语句。另外,它们指示编译器在编译过程中去做某种事情,[Embed]标签告诉编译器在最终的 SWF 文件中加载一个特殊的外部资源,资源可以是位图或外部 SWF 文件。告诉编译器要嵌入的资源所在的 source 路径的属性,如下:
[Embed(source="picture.jpg")]
在元数据语句的后面,直接声明一个 Class 类型的变量,如下:
[Embed(source="picture.jpg")] private var Image:Class;
现在可以使用这个变量创建一个新的资源实例,如下:
var img:Bitmap = new Image();
注意创建的这个对象是 Bitmap 类型的。如果嵌入一个外部 SWF 文件,创建的这个对象应该是 Sprite 类型的,如下:
[Embed(source="animation.swf")] private var Anim:Class; var anim:Sprite = new Anim();
这里是一个在 SWF 中嵌入外部 JPEG 的例子:
package { import flash.display.Sprite; import flash.display.Bitmap; public class EmbedAsset extends Sprite { [Embed(source="picture.jpg")]; private var Image:Class; public function EmbedAsset() { init(); } private function init():void { var img:Bitmap = new Image(); addChild(img); } } }
如果我们使用 Flash IDE ,只要将对象导入到库中并“为 ActionScript 导出”给出一个类名就可以了。不需要使用 [Embed] 元数据标签及类变量,事实上,Flash IDE 编译器甚至不支持 [Embed] 元数据标签。这里只作一个简单的介绍,因为在本书后面的内容中不会用到这个技术,但是很显然这是个非常有用的方法。
本章重要公式
在本章中我们又收集了很多有价值的工具,大多都与颜色有关。
转换为十进制: trace(hexValue);
十进制转换为十六进制: trace(decimalValue.toString(16));
颜色合成: color24 = red << 16 | green << 8 | blue; color32 = alpha << 24 | red << 16 | green << 8 | blue;
颜色提取: red = color24 >> 16; green = color24 >> 8 & 0xFF; blue = color24 & 0xFF; alpha = color32 >> 24; red = color32 >> 16 & 0xFF; green = color32 >> 8 & 0xFF; blue = color232 & 0xFF;
过控制点的曲线: // xt, yt is the point you want to draw through // x0, y0 and x2, y2 are the end points of the curve x1 = xt * 2 – (x0 + x2) / 2; y1 = yt * 2 – (y0 + y2) / 2; moveTo(x0, y0); curveTo(x1, y1, x2, y2);
本文链接:http://www.blueidea.com/tech/multimedia/2008/5795.asp
出处:蓝色理想
责任编辑:bluehearts
上一页 渲染技术 [8] 下一页
◎进入论坛RIA设计与应用版块参加讨论
|