Przykład z bitmapą: animowany obracający się księżyc

Flash Player 9 i nowsze wersje, Adobe AIR 1.0 i nowsze wersje

Przykład animowanego wirującego księżyca ilustruje techniki pracy z obiektami Bitmap oraz z danymi obrazów bitmapowych (obiektami BitmapData). Ten przykład ilustruje tworzenie animacji ruchu wirowego sferycznego księżyca za pomocą płaskiego obrazu powierzchni księżyca wykorzystywanego jako dane nieprzetworzone. Poniższe techniki ilustrują:

  • Ładowanie obrazu zewnętrznego i uzyskiwanie dostępu do jego danych nieprzetworzonych

  • Tworzenie animacji przez powtarzanie kopiowania pikseli z innej części obrazu źródłowego

  • Tworzenie obrazu bitmapowego przez ustawienie wartości pikseli

Aby pobrać pliki tej przykładowej aplikacji, należy przejść na stronę www.adobe.com/go/learn_programmingAS3samples_flash_pl . Pliki aplikacji Animowany wirujący księżyc można znaleźć w folderze Samples/SpinningMoon. Aplikacja składa się z następujących plików:

File

Opis

SpinningMoon.mxml

lub

SpinningMoon.fla

Główny plik aplikacji we Flex (MXML) lub Flash (FLA).

com/example/programmingas3/moon/MoonSphere.as

Klasa udostępniająca funkcjonalności takie jak ładowanie, wyświetlanie i animowanie księżyca.

moonMap.png

Plik obrazu zawierający zdjęcie powierzchni księżyca, ładowany i animowany w celu utworzenia animowanego, wirującego księżyca.

Ładowanie obrazu zewnętrznego w formie danych bitmapy

Pierwszym, głównym zadaniem w tym przykładzie jest załadowanie zewnętrznego pliku obrazu, stanowiącego zdjęcie powierzchni księżyca. Operacja ładowania odbywa się za pomocą dwu metod klasy MoonSphere: konstruktora MoonSphere() , gdzie inicjowany jest proces ładowania, metody imageLoadComplete() , która jest wywoływana po całkowitym załadowaniu obrazu zewnętrznego.

Ładowanie obrazu zewnętrznego przypomina ładowanie zewnętrznego pliku SWF; w obu przypadkach do wykonania operacji ładowania używana jest instancja klasy flash.display.Loader. Rzeczywisty kod w metodzie MoonSphere() , która rozpoczyna ładowanie obrazu, jest następujący:

var imageLoader:Loader = new Loader(); 
imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoadComplete); 
imageLoader.load(new URLRequest("moonMap.png"));

W pierwszej linii znajduje się deklaracja instancji Loader o nazwie imageLoader . Dopiero trzecia linia faktycznie rozpoczyna proces ładowania, wywołując metodę obiektu Loader load() , i przekazując instancję URLRequest reprezentującą adres URL obrazu do załadowania. Druga linia powoduje ustawienie detektora zdarzeń, który zostanie wyzwolony w przypadku całkowitego załadowania obrazu. Należy zauważyć, że metoda addEventListener() nie jest wywoływana w instancji Loader samoistnie; zamiast tego jest ona wywoływana w zależności od właściwości contentLoaderInfo obiektu Loader. Instancja Loader nie przekazuje samoistnie zdarzeń związanych z ładowaną zawartością. Jej właściwość contentLoaderInfo zawiera jednak odniesienie do obiektu LoaderInfo, który jest skojarzony z ładowaną do obiektu Loader zawartością (w tym wypadku z obrazem zewnętrznym). Ten obiekt LoaderInfo udostępnia kilka zdarzeń związanych z postępem i ukończeniem ładowania zawartości zewnętrznej, w tym ze zdarzeniem complete ( Event.COMPLETE ), które wyzwala wywołanie metody imageLoadComplete() po całkowitym załadowaniu obrazu.

Chociaż uruchomienie ładowania obrazu zewnętrznego stanowi ważną część procesu, tak samo ważna jest wiedza, co należy zrobić po ukończeniu ładowania. Zgodnie z kodem pokazanym powyżej funkcja imageLoadComplete() jest wywoływana po załadowaniu obrazu. Funkcja ta odpowiada za kilka kwestii związanych z danymi ładowanego obrazu, opisanymi w dalszej części podręcznika. Jednak aby można było użyć danych obrazu, funkcja ta musi mieć do nich dostęp. W przypadku korzystania z obiektu Loader do ładowania obrazu zewnętrznego ładowany obraz staje się instancją Bitmap dołączoną jako potomny obiekt wyświetlania do obiektu Loader. W takim przypadku dla metody detektora zdarzeń dostępna jest instancja Loader przekazywana następnie do metody jako parametr. Pierwsze wiersze metody imageLoadComplete() są następujące:

private function imageLoadComplete(event:Event):void 
{ 
    textureMap = event.target.content.bitmapData; 
    ... 
}

Należy zauważyć, że parametr obiektu zdarzenia ma nazwę event , oraz że jest to instancja klasy Event. Każda instancja klasy Event ma właściwość target , która odnosi się do obiektu wyzwalającego zdarzenie (w tym przypadku, jest to instancja LoaderInfo, dla której wywołano metodę addEventListener() , zgodnie z opisem powyżej). Obiekt LoaderInfo z kolei ma właściwość content , która (po ukończeniu procesu ładowania) zawiera instancję Bitmap z załadowanym obrazem bitmapowym. W celu wyświetlenia obrazu bezpośrednio na ekranie można wówczas dołączyć tę instancję Bitmap ( event.target.content ) do kontenera obiektu wyświetlanego. (Można również dołączyć obiekt Loader do kontenera obiektu wyświetlanego). W tym przypadku jednak załadowana treść stanowi źródło nieprzetworzonych danych obrazu, nie jest zaś wyświetlana na ekranie. Konsekwentnie, pierwsza linia metody imageLoadComplete() odczytuje właściwość bitmapData załadowanej instancji Bitmap ( event.target.content.bitmapData ) i zapisuje ją w zmiennej instancji o nazwie textureMap , która stanowi źródło danych obrazu użytych do utworzenia animacji obracającego się księżyca. To zagadnienie zostało opisane w następnej sekcji.

Tworzenie animacji przez kopiowanie pikseli

Podstawowa definicja animacji to złudzenie ruchu, lub zmiany, utworzone w wyniku zmiany obrazu w czasie. W tym przykładzie celem jest utworzenie iluzji wirowania sferycznego księżyca wokół jego pionowej osi. Dla celów tej animacji można jednak zignorować kwestię zniekształcenia sferycznego w tym przykładzie. Należy uwzględnić rzeczywisty obraz ładowany jako źródło i używany w charakterze źródła dla danych obrazu księżyca:

Jak można zobaczyć, obraz nie ma kształtu sfery ani kilku sfer; jest to tylko prostokątna fotografia powierzchni księżyca. Ponieważ jednak zdjęcie zostało zrobione dokładnie na równiku księżyca, części obrazu w górnej i dolnej jego części są rozciągnięte i zniekształcone. W celu usunięcia zniekształceń i uzyskania kształtu sferycznego użyjemy filtra mapy przemieszczeń, opisanego poniżej. Ponieważ jednak obraz źródłowy ma kształt prostokąta, w celu wytworzenia iluzji, że sfera się obraca, konieczne jest po prostu przesunięcie zdjęcia powierzchni księżyca w poziomie przez kod.

Należy przy tym zauważyć, że obraz w istocie zawiera dwie kopie powierzchni księżyca, ułożone obok siebie. Obraz ten jest obrazem źródłowym, z którego dane obrazu są kopiowane w sposób cykliczny w celu utworzenia efektu ruchu. Dysponując umieszczonymi obok siebie dwiema kopiami obrazu, można łatwiej uzyskać niezakłócony niczym efekt przewijania. Przejdźmy teraz krok po kroku przez proces animacji, aby zobaczyć, jak to działa.

Proces w istocie obejmuje dwa różne obiekty ActionScript. Pierwszy z nich to ładowany obraz źródłowy, reprezentowany w kodzie przez instancję BitmapData o nazwie textureMap . Zgodnie ze wcześniejszym opisem, dane obrazu są wypełniane instancją textureMap , gdy tylko zewnętrzny obraz zostanie załadowany z wykorzystaniem tego kodu:

textureMap = event.target.content.bitmapData;

Zawartość obiektu textureMap jest prostokątnym obrazem księżyca. Ponadto, w celu utworzenia animowanego ruchu w przykładzie zastosowano instancję Bitmap o nazwie sphere , która jest rzeczywistym obiektem wyświetlanym — księżycem. Podobnie jak textureMap , obiekt sphere jest tworzony i wypełniany początkowymi danymi obrazu w metodzie imageLoadComplete() za pomocą następującego kodu:

sphere = new Bitmap(); 
sphere.bitmapData = new BitmapData(textureMap.width / 2, textureMap.height); 
sphere.bitmapData.copyPixels(textureMap, 
                         new Rectangle(0, 0, sphere.width, sphere.height), 
                         new Point(0, 0));

Zgodnie z treścią kodu tworzona jest instancja sphere . Jej właściwość bitmapData (nieprzetworzone dane obrazu wyświetlane przez instancję sphere ) jest tworzona przy użyciu tej samej wysokości oraz połowy szerokości instancji textureMap . Innymi słowy, zawartość instancji sphere będzie odpowiadała rozmiarom zdjęcia księżyca (ponieważ obraz textureMap zawiera dwa zdjęcia księżyca umieszczone obok siebie). Następnie właściwość bitmapData jest wypełniana danymi obrazu przy wykorzystaniu metody copyPixels() . Parametry należące do metody copyPixels() mogą stanowić o kilku kwestiach:

  • Pierwszy z parametrów wskazuje, że dane obrazu są kopiowane z instancji textureMap .

  • Drugi z parametrów, nowa instancja Rectangle, określa, z której części instancji textureMap należy wykonać migawkę obrazu; w tym przypadku migawką jest prostokąt rozpoczynający się w górnym lewym rogu instancji textureMap (oznaczonej przez dwa pierwsze parametry Rectangle() : 0, 0 ) oraz szerokość i wysokość migawki odpowiadająca właściwościom width i height instancji sphere .

  • Trzeci z parametrów, nowa instancja Point charakteryzująca się wartościami x i y wynoszącymi 0 , definiuje miejsce docelowe danych pikseli — w tym przypadku, lewy górny róg (0, 0) elementu sphere.bitmapData .

Można opisać to tak, że kod kopiuje piksele z instancji textureMap opisanej na poniższym obrazie i wkleja je do instancji sphere . Innymi słowy, zawartość BitmapData instancji sphere jest fragmentem instancji textureMap (patrz podświetlenie):

Należy jednak pamiętać, że jest to tylko początkowy stan instancji sphere — zawartość pierwszego obrazu, kopiowana do instancji sphere .

Po załadowaniu obrazu źródłowego oraz utworzeniu instancji sphere ostatnim zadaniem wykonywanym przez metodę imageLoadComplete() jest skonfigurowanie animacji. Animacja jest sterowana przez instancję Timer zwaną rotationTimer , tworzoną i uruchamianą następującym kodem:

var rotationTimer:Timer = new Timer(15); 
rotationTimer.addEventListener(TimerEvent.TIMER, rotateMoon); 
rotationTimer.start();

Kod tworzy najpierw instancję Timer o nazwie rotationTimer ; parametr ten, przekazywany do konstruktora Timer() , wskazuje, że instancja rotationTimer powinna wyzwalać zdarzenie timer co 15 milisekund. Następnie wywoływana jest metoda addEventListener() , określająca, że wystąpienie zdarzenia timer ( TimerEvent.TIMER ) powoduje wywołanie metody rotateMoon() . Ostatecznie instancja timer jest faktycznie uruchamiana przez wywołanie jej metody start() .

Ze względu na sposób, w jaki zdefiniowano instancję rotationTimer , co około 15 milisekund Flash Player wywołuje metodę rotateMoon() w klasie MoonSphere, to jest tam, gdzie faktycznie ma miejsce animacja księżyca. Kod źródłowy metody rotateMoon() brzmi następująco:

private function rotateMoon(event:TimerEvent):void 
{ 
    sourceX += 1; 
    if (sourceX > textureMap.width / 2) 
    { 
        sourceX = 0; 
    } 
     
    sphere.Data.copyPixels(textureMap, 
                                    new Rectangle(sourceX, 0, sphere.width, sphere.height), 
                                    new Point(0, 0)); 
     
    event.updateAfterEvent(); 
}

Kod realizuje trzy funkcje:

  1. Wartość zmiennej sourceX (początkowo ustawiona na wartość 0), z przyrostem co 1.

    sourceX += 1;

    Jak się dalej okaże, zmienna sourceX jest używana do określenia lokalizacji w instancji textureMap , z której będą kopiowane piksele do instancji sphere , tak, że kod ten będzie wywoływał przesunięcie prostokąta o jeden piksel na prawo w instancji textureMap . Wracając do reprezentacji wizualnej, po kilku cyklach animacji prostokąt źródłowy zostanie przesunięty o kilka pikseli na prawo, o tak:

    Po kolejnych kilku cyklach prostokąt znajdzie się jeszcze dalej:

    To stopniowe, niezakłócone niczym przesuwanie się miejsca, z której kopiowane są piksele, jest kluczem do utworzenia animacji. Powoli, lecz w sposób ciągły przesuwając lokalizację źródłową na prawo, powodujemy ciągłe przesuwanie się obrazu wyświetlanego na ekranie w instancji sphere na lewo. Jest to przyczyną dla której konieczne staje się posiadanie dwu kopii obrazu źródłowego ( textureMap ), będącego zdjęciem powierzchni księżyca. Ze względu na to, że prostokąt w sposób ciągły przemieszcza się na prawo, przez większość czasu nie znajduje się on nad pojedynczym zdjęciem księżyca, a raczej zachodzi na oba zdjęcia.

  2. Powolny ruch prostokąta źródłowego na prawo stwarza tylko jeden problem. Ostatecznie prostokąt osiągnie prawą krawędź instancji textureMap i wyjdzie poza zakres pikseli zdjęcia księżyca kopiowanych do instancji sphere :

    Kolejne wiersze kodu stanowią rozwiązanie tego problemu:

    if (sourceX >= textureMap.width / 2) 
    { 
        sourceX = 0; 
    }

    Kod kontroluje, czy sourceX (lewa krawędź prostokąta) osiągnęła środek instancji textureMap . Jeśli tak, resetuje ona sourceX z powrotem na 0, cofając ją do lewej krawędzi instancji textureMap ; cykl rozpoczyna się od nowa:

  3. Po obliczeniu odpowiedniej wartości sourceX ostatnim krokiem utworzenia animacji jest faktyczne skopiowanie nowych pikseli prostokąta źródłowego do instancji sphere . Kod umożliwiający to jest bardzo podobny do kodu odpowiadającego na samym początku za wypełnienie instancji sphere (jak opisano w powyższych sekcjach); jedyną różnicę stanowi fakt, że w tym przypadku wywołanie konstruktora new Rectangle() powoduje umieszczenie lewej krawędzi prostokąta w miejscu sourceX :

    sphere.bitmapData.copyPixels(textureMap, 
                                new Rectangle(sourceX, 0, sphere.width, sphere.height), 
                                new Point(0, 0));

Należy pamiętać, że ten kod jest wywoływany cyklicznie, w odstępach 15-milisekundowych. w miarę przesuwania lokalizacji prostokąta źródłowego oraz kopiowania pikseli do instancji sphere na ekranie widać, jak zdjęcie księżyca reprezentowane przez instancję sphere przesuwa się. Innymi słowy, wygląda to tak, jakby księżyc stale się obracał.

Tworzenie efektu sferycznego

Księżyc to oczywiście sfera, a nie prostokąt. W naszym przykładzie konieczne jest zatem użycie prostokątnego zdjęcia powierzchni księżyca, w stanie ciągłej animacji, i przekonwertowanie go do formy sferycznej. Wymaga to wykonania dwu osobnych kroków: użycia maski do ukrycia wszystkiego poza okrągłym obszarem zdjęcia powierzchni księżyca, oraz filtra mapy przemieszczenia służącego do odkształcenia zdjęcia księżyca tak, aby wyglądało ono na trójwymiarowe.

Po pierwsze, maska o kształcie okręgu służy do przesłonięcia całej zawartości obiektu MoonSphere, z wyjątkiem sfery utworzonej przez filtr. Poniższy kod umożliwia utworzenie maski w postaci instancji Shape, oraz zastosowanie jej w formie maski instancji MoonSphere:

moonMask = new Shape(); 
moonMask.graphics.beginFill(0); 
moonMask.graphics.drawCircle(0, 0, radius); 
this.addChild(moonMask); 
this.mask = moonMask;

Należy pamiętać, że ponieważ MoonSphere jest obiektem wyświetlanym (opartym na klasie Sprite), maskę można nałożyć bezpośrednio na instancję MoonSphere za pomocą jej dziedziczonej właściwości mask .

Do utworzenia realistycznego efektu wirującej sfery nie wystarczy niestety proste ukrycie fragmentu zdjęcia. Z uwagi na sposób, w jaki zrobiono zdjęcie księżyca, jego wymiary są nieproporcjonalne; fragmenty obrazu przesunięte w górę oraz w dół obrazy są znacznie bardziej zniekształcone i wyciągnięte w porównaniu z fragmentami w obszarze równika. W celu odkształcenia zdjęcia księżyca tak, aby miało ono charakter trójwymiarowy, użyjemy filtra mapy przemieszczeń.

Filtr mapy przemieszczeń jest to rodzaj filtra używany do odkształcania obrazu. W naszym przypadku zdjęcie księżyca zostanie odkształcone tak, aby wyglądało bardziej „realistycznie”. W tym celu górna i dolna część obrazu zostaną ściśnięte w poziomie, podczas gdy środkowa część obrazu pozostanie niezmieniona. Przyjmując, że filtr działa na fragmencie o kształcie kwadratu, ściśnięcie górnej i dolnej części, a pozostawienie niezmienionej środkowej części, umożliwi uzyskanie kształtu okręgu. Efektem „ubocznym” w przypadku tej akurat animacji jest fakt, że środek obrazu wydaje się przemieszczać znacznie dalej, niż obszary w górnej i dolnej części — to złudzenie dodatkowo wzmacnia efekt trójwymiarowości (złudzenie sferyczności księżyca).

Poniższy kod służy do utworzenia filtra mapy przemieszczeń o nazwie displaceFilter :

var displaceFilter:DisplacementMapFilter; 
displaceFilter = new DisplacementMapFilter(fisheyeLens, 
                                new Point(radius, 0),  
                                BitmapDataChannel.RED, 
                                BitmapDataChannel.GREEN, 
                                radius, 0);

Pierwszy z parametrów, fisheyeLens , jest znany jako obraz mapy; w tym przypadku jest to obiekt BitmapData tworzony programowo. Tworzenie obrazu opisano w sekcji Tworzenie obrazu bitmapowego przez ustawienie wartości pikseli . Pozostałe parametry opisują położenie filtrowanego obrazu, w którym powinien zostać nałożony filtr, oraz to, które kanały kolorów zostaną użyte do kontroli efektu przemieszczenia, a także, w jakim stopniu będą one miały wpływ na przemieszczenie. Po utworzeniu filtra mapy przemieszczeń jest on nakładany na instancję sphere (nadal w metodzie imageLoadComplete() ):

sphere.filters = [displaceFilter];

Ostateczny obraz, wraz z maską i filtrem mapy przemieszczeń wygląda następująco:

Wraz z każdym cyklem animacji wirującego księżyca zawartość obiektu BitmapData sfery jest nadpisywana nową migawką danych obrazu źródłowego. Filtr nie wymaga jednak ponownego zastosowania za każdym razem. Dzieje się tak, ponieważ filtr jest nakładany na instancję Bitmap (obiekt wyświetlany), nie zaś na dane bitmapowe (nieprzetworzone informacje o pikselach). Należy przy tym pamiętać, że instancja Bitmap nie stanowi faktycznych danych o bitmapie; jest to obiekt wyświetlany przedstawiający dane bitmapy na ekranie. Posługując się analogią, instancję Bitmap można przyrównać do projektora slajdów wyświetlającego je na ekranie, a obiekt BitmapData do faktycznego slajdu, który można wyświetlić za pomocą projektora. Filtr można nałożyć bezpośrednio na obiekt BitmapData, co byłoby równoważne rysowaniu po samym slajdzie. Można również nałożyć filtr tylko na obiekt wyświetlany, w tym na instancję Bitmap; oznaczałoby to umieszczenie filtra przed obiektywem rzutnika, tak aby działanie filtra dotyczyło wyłącznie obrazu wyświetlanego na ekranie (bez ingerencji w oryginalne zdjęcie). Ponieważ nieprzetworzone dane bitmapowe są dostępne za pośrednictwem instancji Bitmap właściwości bitmapData, filtr może zostać nałożony bezpośrednio na nieprzetworzone dane bitmapy. W tym przypadku sens ma raczej nałożenie filtra na obiekt wyświetlany Bitmap niż na dane bitmapy.

Szczegółowe informacje dotyczące używania filtra mapy przemieszczeń języka ActionScript można znaleźć w sekcji Filtrowanie obiektów wyświetlanych .

Tworzenie obrazu bitmapowego przez ustawienie wartości pikseli

Jedną z istotnych kwestii dotyczących filtra mapy przemieszczeń jest to, że w rzeczywistości wymaga on użycia dwu obrazów. Jeden z nich, obraz źródłowy, jest obrazem naprawdę zmienionym w wyniku działania filtru. W tym przykładzie obraz źródłowy jest to instancja obiektu Bitmap pod nazwą sphere . Drugi obraz używany przez filtr zwany jest obrazem mapy. Obraz mapy nie jest wyświetlany na ekranie. Zamiast tego, kolor każdego z jego pikseli jest używany jako dane wejściowe dla funkcji przemieszczania — kolor piksela o danych współrzędnych x, y na obrazie mapy determinuje, jak duże przemieszczenie (fizyczne przesunięcie) jest stosowane wobec piksela o tych współrzędnych x, y w obrazie źródłowym.

Konsekwentnie, w celu użycia filtra mapy przemieszczeń do utworzenia efektu sferycznego konieczne jest zastosowanie odpowiedniego obrazu mapy — o szarym tle i okręgu z wypełnieniem gradientowym jednego koloru (czerwonego) rozjaśniającym się w poziomie, tak jak przedstawiono poniżej:

Ponieważ w przykładzie tym użyto wyłączeni jednego obrazu mapy i filtra, obraz mapy jest tworzony tylko raz, w metodzie imageLoadComplete() (innymi słowy, po zakończeniu ładowania obrazu zewnętrznego). Tworzony jest obraz mapy o nazwie fisheyeLens przez wywołanie klasy MoonSphere metody createFisheyeMap() :

var fisheyeLens:BitmapData = createFisheyeMap(radius);

Wewnątrz metody createFisheyeMap() odbywa się faktyczne kreślenie obrazu mapy, po jednym pikselu naraz, z wykorzystaniem metody setPixel() klasy BitmapData. Kompletny kod dla metody createFisheyeMap() zamieszczono poniżej; opatrzono go omówieniem krok po kroku wyjaśniającym jego działanie:

private function createFisheyeMap(radius:int):BitmapData 
{ 
    var diameter:int = 2 * radius; 
     
    var result:BitmapData = new BitmapData(diameter, 
                                        diameter, 
                                        false, 
                                        0x808080); 
     
    // Loop through the pixels in the image one by one 
    for (var i:int = 0; i < diameter; i++) 
    { 
        for (var j:int = 0; j < diameter; j++) 
        { 
            // Calculate the x and y distances of this pixel from 
            // the center of the circle (as a percentage of the radius). 
            var pctX:Number = (i - radius) / radius; 
            var pctY:Number = (j - radius) / radius; 
             
            // Calculate the linear distance of this pixel from 
            // the center of the circle (as a percentage of the radius). 
            var pctDistance:Number = Math.sqrt(pctX * pctX + pctY * pctY); 
             
            // If the current pixel is inside the circle, 
            // set its color. 
            if (pctDistance < 1) 
            { 
                // Calculate the appropriate color depending on the 
                // distance of this pixel from the center of the circle. 
                var red:int; 
                var green:int; 
                var blue:int; 
                var rgb:uint; 
                red = 128 * (1 + 0.75 * pctX * pctX * pctX / (1 - pctY * pctY)); 
                green = 0; 
                blue = 0; 
                rgb = (red << 16 | green << 8 | blue); 
                // Set the pixel to the calculated color. 
                result.setPixel(i, j, rgb); 
            } 
        } 
    } 
    return result; 
}

Przede wszystkim wywołanie tej metody wiąże się z otrzymaniem parametru radius , wskazującego promień obrazu w kształcie okręgu, który ma zostać utworzony. Następnie kod tworzy obiekt BitmapData, w którym zostanie narysowany okrąg. Obiekt ten o nazwie result , jest następnie przekazywany z powrotem jako wartość zwracana metody. Zgodnie z tym, co pokazano w poniższym fragmencie kodu, tworzoną instancję result obiektu BitmapData charakteryzują szerokość i wysokość odpowiadające średnicy okręgu, bez przezroczystości (trzeci parametr równy false ), oraz ze wstępnym wypełnieniem kolorem 0x808080 (średnio szary):

var result:BitmapData = new BitmapData(diameter, 
                                    diameter, 
                                    false, 
                                    0x808080);

Następnie w kodzie wykorzystywane są dwie pętle służące do uzyskania iteracji cyklu wykonywanego na kolejnych pikselach. Pętla zewnętrzna przechodzi przez każdą z kolumn obrazu od lewej do prawej (korzystając przy tym ze zmiennej i do reprezentowania położenia poziomego piksela, którego dotyczy manipulacja), podczas gdy pętla wewnętrzna przechodzi przez każdy z pikseli z bieżącej kolumny od góry do dołu (korzystając przy tym ze zmiennej j reprezentującej pionowe położenie bieżącego piksela). Kod dla pętli (pominięto środkowy fragment pętli) jest następujący:

for (var i:int = 0; i < diameter; i++) 
{ 
    for (var j:int = 0; j < diameter; j++) 
    { 
        ... 
    } 
}

W miarę wykonywania pętli na kolejnych pikselach obliczane są kolejne wartości (wartości koloru tego piksela na obrazie mapy). Ten proces obejmuje cztery kroki:

  1. Kod oblicza odległość bieżącego piksela od środka okręgu wzdłuż osi x ( i - radius ). Wartość ta jest dzielona przez długość promienia, co umożliwia wyrażenie jej procentowo względem długości promienia, nie zaś w wartościach bezwzględnych ( (i - radius)/ radius ). Ta wartość procentowa jest zapisywana pod zmienną o nazwie pctX , a wartość jej równoważna, dla osi y, jest obliczana i zapisywana pod zmienną pctY , co ilustruje poniższy kod:

    var pctX:Number = (i - radius) / radius; 
    var pctY:Number = (j - radius) / radius;
  2. Korzystając z powszechnie znanego wzoru trygonometrycznego (twierdzenie Pitagorasa), można obliczyć odległość liniową środka okręgu od bieżącego punktu, na podstawie znanych wartości pctX i pctY . Uzyskana wartość jest zapisywana pod zmienną o nazwie pctDistance , zgodnie z poniższym przykładem:

    var pctDistance:Number = Math.sqrt(pctX * pctX + pctY * pctY);
  3. Następnie kod kontroluje, czy wartość procentowa odległości jest mniejsza od 1 (co odpowiada 100% długości promienia, lub, innymi słowy, czy piksel znajduje się w obszarze zakreślonym promieniem okręgu). Jeśli piksel przypada na obszar poza okręgiem, jest mu przypisywana obliczona wartość koloru (tę część w niniejszym wątku pominięto; opisano ją w kroku 4); w przeciwnym wypadku z pikselem nie dzieje się nic, tak że kolor pozostaje domyślny — średnio szary:

    if (pctDistance < 1) 
    { 
        ... 
    }
  4. W przypadku pikseli przypadających poza okrąg wartość koloru jest obliczana wyłącznie dla piksela. Ostatecznym kolorem będzie odcień czerwonego w zakresie od czarnego (0% czerwonego) przy lewej krawędzi okręgu do jasnego (100% czerwonego) przy prawej krawędzi okręgu. Wartość koloru jest obliczana początkowo w trzech składnikach (czerwonym, niebieskim i zielonym) z osobna, zgodnie z poniższym przykładem:

    red = 128 * (1 + 0.75 * pctX * pctX * pctX / (1 - pctY * pctY)); 
    green = 0; 
    blue = 0;

    Należy zauważyć, że tylko czerwony składnik koloru (zmienna red ) faktycznie ma wartość. Wartości kolorów zielonego oraz niebieskiego (zmienne green i blue ) są wyświetlane jedynie dla przejrzystości opisu; w rzeczywistości mogłyby być pominięte. Ponieważ celem tej metody jest utworzenie okręgu zawierającego gradient czerwieni, nie są wymagane wartości kolorów zielonego ani niebieskiego.

    Po wyznaczeniu trzech różnych wartości kolorów są one łączone w pojedynczą wartość koloru w postaci liczby całkowitej za pomocą standardowego algorytmu przesuwania bitowego, zgodnie z zawartością poniższego kodu:

    rgb = (red << 16 | green << 8 | blue);

    Ostatecznie, po obliczeniu wartości koloru, wartość ta jest przypisywana do bieżącego piksela za pomocą metody setPixel() obiektu result BitmapData, jak poniżej:

    result.setPixel(i, j, rgb);