Buforowanie bitmap

W przypadku złożonych treści wektorowych należy korzystać (zgodnie z potrzebami) z buforowania bitmapy.

Dobrą optymalizację można uzyskać, używając funkcji buforowania bitmap. Ta funkcja buforuje obiekt wektorowy, renderuje go wewnętrznie jako bitmapę, a następnie korzysta z tej bitmapy przy renderowaniu. Rezultatem może być znaczny wzrost wydajności renderingu, jednak niekiedy kosztem miejsca w pamięci. Funkcja buforowania bitmapy jest przeznaczona dla złożonych treści wektorowych, takich jak złożone gradienty i tekst.

Włączenie buforowania bitmapy dla animowanego obiektu, który zawiera złożone grafiki wektorowe (takie jak tekst lub gradienty) powoduje zwiększenie wydajności. Jeśli jednak buforowanie bitmapy zostało włączone dla obiektu wyświetlanego np. klipu filmowego z odtwarzaną osią czasu, wynik będzie przeciwny. Dla każdej klatki środowisko wykonawcze musi zaktualizować buforowaną bitmapę, a następnie ponownie narysować ją na ekranie, co wymaga użycia wielu cykli procesora. Stosowanie funkcji buforowania bitmapy jest korzystne tylko wówczas, gdy buforowana bitmapa może zostać wygenerowana jednorazowo, a następnie może być używana bez konieczności aktualizacji.

Jeśli buforowanie bitmapy zostanie włączone dla obiektu Sprite, wówczas możliwe będzie przesunięcie obiektu bez ponownego generowania buforowanej bitmapy w środowisku wykonawczym. Zmiana właściwości x i y obiektu nie powoduje ponownego generowania. Jednak każda próba obrócenia obiektu, zmiany jego skali oraz zmiany jego wartości alfa powoduje, że środowisko wykonawcze ponownie generuje buforowaną bitmapę, czego rezultatem jest obniżenie wydajności.

Uwaga: Ograniczenie to nie dotyczy właściwości DisplayObject.cacheAsBitmapMatrix dostępnej w środowisku AIR oraz w narzędziu Packager for iPhone. Dzięki właściwości cacheAsBitmapMatrix można obracać, skalować i pochylać obiekty wyświetlane oraz zmieniać ich wartości alfa, nie wywołując ponownego generowania bitmapy.

Zbuforowana bitmapa może zajmować znacznie więcej pamięci niż zwykły klip filmowy. Na przykład: zbuforowany klip filmowy o rozdzielczości 250 x 250 pikseli może zajmować nawet 250 kB pamięci, podczas gdy ten sam klip w postaci niezbuforowanej będzie zajmował zaledwie 1 kB pamięci.

Poniższy przykład prezentuje obiekt klasy Sprite, który zawiera obraz jabłka. Do symbolu jabłka dołączana jest następująca klasa:

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(); 
        } 
    } 
}

W kodzie wykorzystano klasę Sprite zamiast klasy MovieClip, ponieważ oś czasu nie jest wymagana dla każdego jabłka z osobna. W celu zapewnienia najlepszej wydajności należy użyć obiektu o możliwie najmniejszym rozmiarze. Następnie poniższy kod tworzy instancję klasy:

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); 
        } 
    } 
}

Gdy użytkownik kliknie przycisk myszy, jabłka zostaną utworzone bez buforowania. Gdy użytkownik naciśnie klawisz C (kod 67), wektory jabłek zostaną zbuforowane jako bitmapy i przedstawione na ekranie. Ta technika znacznie zwiększa wydajność renderingu w komputerach stacjonarnych oraz na urządzeniach mobilnych wyposażonych w stosunkowo wolne procesory.

Funkcja buforowania bitmapy zwiększa wydajność renderingu, ale może powodować szybkie przydzielanie dużych ilości pamięci. Po zbuforowaniu obiektu jego powierzchnia zostaje zapisana w pamięci jako przezroczysta bitmapa, co przedstawia poniższy diagram:

Obiekt i bitmapa jego powierzchni zapisane w pamięci

Program Flash Player 10.1 i środowisko AIR 2.5 optymalizują użycie pamięci, stosując metodę optymalizacji opisaną w sekcji Filtry i dynamiczne usuwanie bitmapy . Jeśli buforowany obiekt wyświetlany jest ukryty lub znajduje się poza ekranem, jego bitmapa w pamięci jest zwalniana, gdy nie jest używana przez pewien czas.

Uwaga: Jeśli właściwość opaqueBackground obiektu wyświetlanego jest ustawiona na określony kolor, środowisko wykonawcze traktuje obiekt wyświetlany jako nieprzezroczysty. Jeśli używana jest właściwość cacheAsBitmap , środowisko wykonawcze tworzy nieprzezroczystą 32-bitową bitmapę w pamięci. Kanał Alfa jest ustawiony na 0xFF, co zwiększa wydajność, ponieważ w celu wyświetlenia bitmapy na ekranie nie jest wymagana przezroczystość. Brak mieszania Alfa powoduje jeszcze szybszy rendering. Jeśli bieżąca głębia barw jest ograniczona do 16 bitów, wówczas bitmapa w pamięci jest zapisana jako obraz 16-bitowy. Użycie właściwości opaqueBackground nie powoduje jawnej aktywacji buforowania bitmapy.

W celu zmniejszenia obciążenia pamięci należy użyć właściwości cacheAsBitmap i aktywować ją na każdym obiekcie wyświetlanym, a nie na jednym kontenerze. Aktywacja buforowania bitmapy w kontenerze sprawia, że końcowa bitmapa zajmuje znacznie więcej miejsca w pamięci — jest zapisana jako przezroczysta bitmapa o wymiarach 211 x 279 pikseli. Obraz zajmuje około 229 kB:

Powiększ obraz
Aktywacja buforowania bitmapy w kontenerze

Ponadto buforowanie kontenera ogranicza ryzyko aktualizacji całej bitmapy w pamięci, jeśli jakiekolwiek jabłko zacznie się poruszać w klatce. Aktywacja buforowania bitmapy dla poszczególnych instancji powoduje zbuforowanie w pamięci sześciu powierzchni o wielkości 7 kB, co skutkuje wykorzystaniem tylko 42 kB pamięci:

Powiększ obraz
Aktywacja buforowania bitmapy dla instancji

Uzyskiwanie dostępu do poszczególnych instancji jabłka za pośrednictwem listy wyświetlania, a następnie wywoływanie metody getChildAt() powoduje zapisywanie odwołań w obiekcie Vector, co umożliwia łatwiejszy dostęp:

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); 
        } 
    } 
}

Należy pamiętać o tym, że buforowanie bitmapy zwiększa zakres renderingu, jeśli buforowana treść nie będzie obracana, skalowana lub zmieniana w każdej klatce. Jednak nie powoduje ona zwiększenia wydajności renderingu w przypadku transformacji innych niż translacja na osiach x i y. W takich przypadkach program Flash Player aktualizuje zbuforowaną kopię bitmapy dla każdej transformacji, jaka ma miejsce w ramach obiektu wyświetlanego. Aktualizacja zbuforowanej kopii może spowodować powolne działanie oraz znaczące obciążenie procesora i baterii. Również to ograniczenie nie dotyczy właściwości cacheAsBitmapMatrix w środowisku AIR oraz w narzędziu Packager for iPhone

Poniższy kod zmienia wartość Alfa w metodzie Movement, co powoduje zmianę krycia jabłka w każdej klatce:

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(); 
}

Korzystanie z buforowania bitmapy wywołuje obniżenie wydajności. Każda zmiana wartości alfa zmusza środowisko wykonawcze do zaktualizowania bitmapy buforowanej w pamięci.

Filtry działają w oparciu o bitmapy, które są aktualizowane przy każdym przesunięciu głowicy odtwarzania zbuforowanego klipu filmowego. Dlatego zastosowanie filtru automatycznie powoduje ustawienie właściwości cacheAsBitmap na true . Poniższy rysunek ilustruje animowany klip filmowy:

Animowany klip filmowy

Filtry nie powinny być stosowane względem treści animowanych, ponieważ mogą one powodować problemy z wydajnością. Na poniższym rysunku widoczny jest efekt dodania filtru Cień:

Animowany klip filmowy z filtrem cienia

Rezultat jest następujący: jeśli oś czasu w klipie filmowym będzie odtwarzana, wówczas konieczne będzie ponowne wygenerowanie bitmapy. Bitmapa musi zostać ponownie wygenerowana także wówczas, gdy treść zostanie zmodyfikowana w dowolny sposób inny niż prosta transformacja x lub y. Dla każdej klatki środowisko wykonawcze musi ponownie narysować bitmapę, co powoduje wzrost obciążenia procesora, obniżenie wydajności i skrócenie czasu działania przy zasilaniu akumulatorowym.

Paul Trani przedstawia przykłady optymalizacji grafiki z bitmapami za pomocą programu Flash Professional i kodu ActionScript w następujących szkoleniach wideo: