位图缓存

在适当的时候,对复杂的矢量内容使用位图缓存功能。

使用位图缓存功能可实现良好的优化。此功能缓存矢量对象,将其作为位图在内部呈现,并使用该位图进行呈现。此结果会显著提高呈现的性能,但需要占用大量内存。针对复杂的矢量内容使用位图缓存功能,如复杂渐变或文本。

为包含复杂的矢量图形(例如文本或渐变)的动画对象打开位图缓存可提高性能。但是,如果在显示对象(如播放其时间轴的影片剪辑)中启用了位图缓存,您将获得相反的效果。在各个帧上,运行时必须更新缓存的位图,然后在屏幕上重绘该位图,这一过程需要许多 CPU 周期。仅当缓存的位图可以一次生成,且随后无需更新即可使用时,才适合使用位图缓存功能。

为 Sprite 对象打开位图缓存后,移动该对象不会使运行时重新生成缓存的位图。更改对象的 x y 属性不会导致重新生成。然而,任何试图旋转、缩放对象或更改其 alpha 值的行为都将导致运行时重新生成缓存的位图,从而降低性能。

注: AIR 和 Packager for iPhone 中提供的 DisplayObject.cacheAsBitmapMatrix 属性没有此限制。通过使用 cacheAsBitmapMatrix 属性,您可以旋转、缩放、倾斜显示对象以及更改其 Alpha 值,而不触发重新生成位图。

缓存位图占用的内存大于常规影片剪辑实例。例如,如果舞台上的影片剪辑为 250 x 250 像素,缓存它可能会使用 250 KB 内存,而未缓存它只需 1 KB。

以下示例使用一个包含苹果图像的 Sprite 对象。以下类将附加到苹果符号:

package org.bytearray.bitmap 
{     
    import flash.display.Sprite; 
    import flash.events.Event; 
     
    public class Apple extends Sprite 
    { 
        private var destinationX:Number; 
        private var destinationY:Number; 
         
        public function Apple () 
        { 
            addEventListener(Event.ADDED_TO_STAGE,activation); 
            addEventListener(Event.REMOVED_FROM_STAGE,deactivation);     
        } 
         
        private function activation(e:Event):void  
        { 
            initPos();     
            addEventListener (Event.ENTER_FRAME,handleMovement);     
        } 
         
        private function deactivation(e:Event):void  
        { 
            removeEventListener(Event.ENTER_FRAME,handleMovement);     
        } 
         
        private function initPos():void 
        { 
            destinationX = Math.random()*(stage.stageWidth - (width>>1)); 
            destinationY = Math.random()*(stage.stageHeight - (height>>1)); 
        } 
         
        private function handleMovement(e:Event):void  
        { 
            x -= (x - destinationX)*.5; 
            y -= (y - destinationY)*.5; 
             
            if (Math.abs(x - destinationX) < 1 && Math.abs(y - destinationY) < 1) 
                initPos(); 
        } 
    } 
}

以上代码使用的是 Sprite 类而不是 MovieClip 类,因为并非每个苹果都需要时间轴。为了获得最佳性能,请尽可能使用最轻型的对象。接下来,将使用以下代码实例化此类:

import org.bytearray.bitmap.Apple; 
 
stage.addEventListener(MouseEvent.CLICK,createApples); 
stage.addEventListener(KeyboardEvent.KEY_DOWN,cacheApples); 
 
const MAX_NUM:int = 100; 
var apple:Apple; 
var holder:Sprite = new Sprite(); 
 
addChild(holder); 
 
function createApples(e:MouseEvent):void 
{ 
    for (var i:int = 0; i< MAX_NUM; i++) 
    { 
        apple = new Apple(); 
         
        holder.addChild(apple); 
    } 
} 
 
function cacheApples(e:KeyboardEvent):void 
{ 
    if (e.keyCode == 67) 
    { 
        var lng:int = holder.numChildren; 
         
        for (var i:int = 0; i < lng; i++) 
        { 
            apple = holder.getChildAt (i) as Apple; 
             
            apple.cacheAsBitmap = Boolean(!apple.cacheAsBitmap); 
        } 
    } 
}

当用户单击鼠标时,将创建苹果,而不需要进行缓存。当用户按下 C 键(键代码为 67),苹果矢量将作为位图缓存并显示在屏幕上。当 CPU 较慢时,此技术可显著提高台式机和移动设备上的呈现性能。

但是,虽然使用位图缓存功能可以提高呈现性能,但也会快速占用大量内存。缓存对象后,其表面将捕获为透明位图并存储在内存中,如下图所示:

对象及其存储在内存中的表面位图

Flash Player 10.1 和 AIR 2.5 优化内存使用的方法与 滤镜和动态位图卸载 中介绍的方法相同。如果隐藏缓存的显示对象或将其置于屏幕之外,则在一段时间未使用后释放其位图占用的内存。

注: 如果将显示对象的 opaqueBackground 属性设置为特定颜色,运行时会将显示对象视为不透明。当运行时与 cacheAsBitmap 属性一起使用时,它将在内存中创建一个非透明的 32 位位图。将 alpha 通道设置为 0xFF 可提高性能,因为在屏幕上绘制位图时不要求透明度。避免使用 alpha 混合可提高呈现速度。如果当前屏幕深度限制为 16 位,则内存中的位图会存储为 16 位图像。使用 opaqueBackground 属性不会隐式激活位图缓存。

要节省内存,请使用 cacheAsBitmap 属性,并对每个显示对象而不是容器激活该属性。对容器激活位图缓存会导致最终位图占用更多内存,从而创建一个尺寸为 211 x 279 像素的透明位图。此图像大约占用 229 KB 内存:

查看完全大小图形
对容器激活位图缓存

此外,通过缓存容器,如果任何苹果开始在帧上移动时,您将面临在内存中更新整个位图的风险。对各个实例激活位图缓存会导致在内存中缓存 6 个 7KB 的表面,总共仅使用 42 KB 内存:

查看完全大小图形
对实例激活位图缓存

访问显示列表中的各个苹果实例并调用 getChildAt() 方法,会将引用存储在 Vector 对象中以便于访问:

import org.bytearray.bitmap.Apple; 
 
stage.addEventListener(KeyboardEvent.KEY_DOWN, cacheApples); 
 
const MAX_NUM:int = 200; 
var apple:Apple; 
var holder:Sprite = new Sprite(); 
 
addChild(holder); 
 
var holderVector:Vector.<Apple> = new Vector.<Apple>(MAX_NUM, true); 
 
for (var i:int = 0; i< MAX_NUM; i++) 
{ 
    apple = new Apple(); 
     
    holder.addChild(apple); 
     
    holderVector[i] = apple; 
} 
 
function cacheApples(e:KeyboardEvent):void 
{ 
    if (e.keyCode == 67) 
    { 
        var lng:int = holderVector.length 
         
        for (var i:int = 0; i < lng; i++) 
        { 
            apple = holderVector[i]; 
             
            apple.cacheAsBitmap = Boolean(!apple.cacheAsBitmap); 
        } 
    } 
}

请记住,如果不在各个帧上旋转、缩放或更改缓存的内容,则位图缓存可提高呈现效果。但是,对于 X 和 Y 轴上的平移之外的任何转换,则不会提高呈现性能。在这些情况下,每次在显示对象上发生转换时,Flash Player 都将更新缓存的位图副本。更新缓存副本会导致占用大量 CPU、降低性能以及消耗大量电池电量。同样,AIR 或 Packager for iPhone 中的 cacheAsBitmapMatrix 属性也没有此限制。

以下代码会更改移动方法中的 alpha 值,这会更改每个帧中苹果的不透明度:

private function handleMovement(e:Event):void  
{ 
        alpha = Math.random(); 
        x -= (x - destinationX)*.5; 
        y -= (y - destinationY)*.5; 
             
        if (Math.abs(x - destinationX) < 1 && Math.abs(y - destinationY) < 1) 
            initPos(); 
}

使用位图缓存会导致性能降低。更改 alpha 值会强制运行时在修改 alpha 值时更新内存中的缓存位图。

滤镜依赖每次缓存的影片剪辑的播放头移动时更新的位图。因此,使用滤镜会自动将 cacheAsBitmap 属性设置为 true 。下图演示了一个动画影片剪辑:

动画影片剪辑

请避免在动画内容中使用滤镜,因为这可能导致性能问题。在下图中,设计人员添加了一个投影滤镜:

使用投影滤镜的动画影片剪辑

因此,如果播放影片剪辑内部的时间轴,则必须重新生成位图。如果以简单的 x 或 y 转换以外的任何方式修改内容,则也必须重新生成位图。每个帧都会强制运行时重绘位图,这需要更多的 CPU 资源、导致性能非常低,并缩短电池寿命。

Paul Trani 提供了以下培训视频,演示了如何使用 Flash Professional 和 ActionScript 来优化使用位图的图形: