Esempio di bitmap: animazione di rotazione della luna

Flash Player 9 e versioni successive, Adobe AIR 1.0 e versioni successive

L'esempio di animazione di rotazione della luna mostra le tecniche per lavorare con gli oggetti Bitmap e i dati immagine bitmap (oggetti BitmapData). Nell'esempio viene illustrato come creare un'animazione di una luna sferica rotante utilizzando i dati originari di un'immagine piatta della superficie lunare. Verranno mostrate le seguenti tecniche:

  • Caricamento di un'immagine esterna e accesso ai dati originari dell'immagine

  • Creazione di un'animazione mediante la copiatura ripetuta dei pixel provenienti da parti differenti dell'immagine di origine

  • Creazione di un'immagine bitmap mediante l'impostazione dei valori dei pixel

Per ottenere i file dell'applicazione per questo esempio, visitate la pagina www.adobe.com/go/learn_programmingAS3samples_flash_it . I file di applicazione dell'animazione di rotazione della luna si trovano nella cartella Samples/SpinningMoon. L'applicazione è composta dai seguenti file:

File

Descrizione

SpinningMoon.mxml

o

SpinningMoon.fla

Il file principale dell'applicazione in Flex (MXML) o Flash (FLA)

com/example/programmingas3/moon/MoonSphere.as

Classe necessaria per le funzioni di caricamento, visualizzazione e animazione della luna.

moonMap.png

File di immagine contenente una fotografia della superficie lunare, da caricare e utilizzare per la creazione dell'animazione di rotazione della luna.

Caricamento di un'immagine esterna come dati bitmap

La prima attività da compiere per questo esempio è caricare un file di immagine esterno, contenente un'immagine della superficie lunare. L'operazione di caricamento è gestita da due metodi nella classe MoonSphere: la funzione di costruzione MoonSphere() , dove viene avviato il processo di caricamento, e il metodo imageLoadComplete() , che viene chiamato una volta caricata completamente l'immagine esterna.

Il procedimento per caricare un'immagine esterna è simile a quello per caricare un SWF esterno; entrambi utilizzano un'istanza della classe flash.display.Loader per effettuare l'operazione di caricamento. Il codice del metodo MoonSphere() che avvia il caricamento dell'immagine è il seguente:

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

La prima riga dichiara l'istanza di caricamento denominata imageLoader . La terza riga è quella che avvia effettivamente il processo di caricamento chiamando il metodo load() dell'oggetto Loader, assegnando un'istanza URLRequest corrispondente all'URL dell'immagine da caricare. La seconda riga imposta il listener dell'evento che viene attivato al termine del caricamento dell'immagine. Notare che il metodo addEventListener() non viene chiamato sull'istanza del Loader, ma sulla proprietà contentLoaderInfo dell'oggetto Loader. L'istanza del Loader in sé non invia eventi relativi al contenuto che viene caricato. La sua proprietà contentLoaderInfo , tuttavia, contiene un riferimento all'oggetto LoaderInfo che è associato al contenuto che viene caricato nell'oggetto Loader (in questo caso l'immagine esterna). Tale oggetto LoaderInfo fornisce diversi eventi relativi al progresso e al completamento del caricamento del contenuto esterno, compreso l'evento complete ( Event.COMPLETE ) che avvia una chiamata al metodo imageLoadComplete() al termine del caricamento dell'immagine.

Se l'avvio del caricamento dell'immagine esterna è una parte importante del processo, è altrettanto importante sapere cosa fare alla fine del caricamento. Come mostrato nel codice riportato sopra, al termine del caricamento dell'immagine viene chiamata la funzione imageLoadComplete() . Questa funzione effettua una serie di operazioni sui dati dell'immagine caricata, che verranno descritti in seguito. Ad ogni modo, la prima cosa da fare per utilizzare i dati dell'immagine è accedervi. Quando viene utilizzato un oggetto Loader per caricare un'immagine esterna, l'immagine caricata diventa un'istanza Bitmap che viene associata all'oggetto Loader come oggetto di visualizzazione secondario. In questo caso, l'istanza Loader viene resa disponibile per il metodo listener di evento come parte dell'oggetto evento che viene assegnato al metodo come parametro. Le prime righe del metodo imageLoadComplete() sono le seguenti:

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

Notate che il parametro dell'oggetto evento è denominato event ed è un'istanza della classe Event. Tutte le istanze della classe Event hanno una proprietà target che si riferisce agli oggetti che lanciano l'evento (in questo caso l'istanza LoaderInfo sulla quale il metodo addEventListener() era stato chiamato, come descritto in precedenza). L'oggetto LoaderInfo, a sua volta, ha una proprietà content che (una volta completato il processo di caricamento) contiene l'istanza Bitmap dell'immagine bitmap caricata. Se desiderate visualizzare l'immagine direttamente su schermo, potete includere questa istanza Bitmap ( event.target.content ) in un contenitore di oggetti di visualizzazione. In alternativa, è possibile anche includere l'oggetto Loader in un contenitore di oggetti di visualizzazione. In questo esempio, tuttavia, il contenuto caricato viene utilizzato come origine dei dati immagine originari invece che essere utilizzato per la visualizzazione su schermo. Di conseguenza, la prima riga del metodo imageLoadComplete() legge la proprietà bitmapData dell'istanza Bitmap caricata ( event.target.content.bitmapData ) e la memorizza nella variabile di istanza denominata textureMap , la quale, viene utilizzata come origine dei dati dell'immagine per creare l'animazione della rotazione della luna. Questo è descritto in seguito.

Creazione di un'animazione mediante copiatura dei pixel

Una definizione base di animazione è l'illusione di movimento creata dal cambiamento di un'immagine nel tempo. In questo esempio, l'obiettivo è creare l'illusione di una luna sferica che ruota attorno al suo asse verticale. Tuttavia, per le finalità dell'animazione, è possibile ignorare gli aspetti di distorsione sferica dell'esempio. Soffermatevi sull'immagine effettivamente caricata e utilizzata come origine dei dati per l'immagine della luna:

Come potete notare, l'immagine non è composta da una o più sfere; si tratta di una fotografia di forma rettangolare della superficie lunare. Poiché la foto è stata scattata esattamente sull'equatore lunare, le parti dell'immagine in prossimità dei margini superiore e inferiore dell'immagine sono allungati e distorti. Più avanti verrà illustrato come utilizzare un filtro mappa di spostamento per rimuovere questa distorsione dall'immagine e ottenere una forma sferica. Tuttavia, poiché l'immagine di origine è rettangolare, per creare l'illusione che la sfera stia ruotando è sufficiente che il codice faccia scorrere la superficie lunare orizzontalmente.

Notate che l'immagine contiene due copie affiancate della fotografia della superficie lunare. Questa immagine è l'immagine di origine dalla quale i dati dell'immagine vengono ripetutamente copiati per creare l'illusione del movimento. L'affiancamento di due copie della stessa immagine consente di creare un effetto di scorrimento continuo e ininterrotto in modo più semplice. Verrà ora esaminato il funzionamento del processo di animazione passo per passo.

Il processo comprende due oggetti ActionScript separati. Per prima vi è l'immagine di origine caricata, che nel codice è rappresentata dall'istanza BitmapData denominata textureMap . Come descritto in precedenza, textureMap viene compilata con i dati immagine non appena viene caricata l'immagine esterna, utilizzando il seguente codice:

textureMap = event.target.content.bitmapData;

Il contenuto di textureMap è l'immagine rettangolare della luna. Inoltre, per creare la rotazione, nel codice viene utilizzata un'istanza Bitmap denominata sphere , che corrisponde all'oggetto di visualizzazione che mostra l'immagine della luna su schermo. Come textureMap , l'oggetto sphere viene creato e compilato con i suoi dati immagine iniziali nel metodo imageLoadComplete() , utilizzando il seguente codice:

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

Come mostrato nel codice, viene creata un'istanza per sphere . La sua proprietà bitmapData (i dati immagine originari visualizzati da sphere ) viene creata con la stessa altezza di textureMap , ma con una larghezza pari alla sua metà. In altre parole, il contenuto di sphere avrà le dimensioni di una sola foto della luna (poiché l'immagine textureMap contiene due foto della luna affiancate). Successivamente la proprietà bitmapData viene riempita con i dati dell'immagine grazie al metodo copyPixels() . I parametri del metodo copyPixels() possono indicare diverse cose:

  • Il primo parametro indica che i dati dell'immagine vengono copiati da textureMap .

  • Il secondo parametro, una nuova istanza Rectangle, specifica da quale parte di textureMap deve essere acquisita l'istantanea dell'immagine; in questo caso l'istantanea è un rettangolo che ha origine nell'angolo superiore sinistro di textureMap (indicato dai primi due parametri Rectangle() : 0, 0 ) e la larghezza e l'altezza dell'istantanea del rettangolo corrispondono alle proprietà width e height di sphere .

  • Il terzo parametro, una nuova istanza Point con valori x e y di 0 , definisce la destinazione dei dati pixel: in questo caso, si tratta dell'angolo in alto a sinistra (0, 0) di sphere.bitmapData .

Rappresentato visivamente, il codice copia i pixel da textureMap evidenziati nell'immagine seguente e li incolla in sphere . In altre parole, il contenuto BitmapData di sphere è la porzione di textureMap evidenziata nell'immagine in basso:

Questo, tuttavia, è soltanto lo stato iniziale di sphere , ovvero il primo contenuto di dati immagine copiato in sphere .

Dopo il caricamento dell'immagine di origine e la creazione di sphere , l'attività finale effettuata dal metodo imageLoadComplete() è quella di impostare l'animazione. L'animazione è gestita da un'istanza Timer denominata rotationTimer , che viene creata e avviata dal codice seguente:

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

Il codice crea per prima cosa l'istanza Timer denominata rotationTimer ; il parametro assegnato alla funzione di costruzione Timer() indica che rotationTimer deve avviare il suo evento timer ogni 15 millisecondi. Successivamente viene chiamato il metodo addEventListener() con l'indicazione che quando si verifica l'evento timer ( TimerEvent.TIMER ), viene chiamato il metodo rotateMoon() . Alla fine il timer viene avviato chiamando il relativo metodo start() .

Grazie al modo in cui rotationTimer è definito, Flash Player chiama il metodo rotateMoon() approssimativamente una volta ogni 15 millisecondi nella classe MoonSphere, che è la posizione in cui si verifica l'animazione della luna. Il codice sorgente del metodo rotateMoon() è il seguente:

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

Il codice effettua tre operazioni:

  1. Il valore della variabile sourceX (impostato inizialmente su 0) aumenta di 1.

    sourceX += 1;

    Come potete vedere, sourceX viene utilizzato per determinare la posizione all'interno di textureMap dalla quale i pixel verranno copiati in sphere , in modo che questo codice abbia l'effetto di spostare il rettangolo di un pixel più a destra su textureMap . Tornando alla rappresentazione visiva, dopo diversi cicli di animazione il rettangolo di origine si sarà spostato di diversi pixel a destra, nel modo rappresentato nella figura:

    Dopo diversi altri cicli, il rettangolo si sarà ulteriormente spostato:

    Questo spostamento graduale e costante della posizione dalla quale i pixel sono copiati costituisce l'elemento fondamentale dell'animazione. Spostando lentamente e continuamente la posizione verso destra, l'immagine che viene visualizzata sullo schermo in sphere appare in continuo movimento verso sinistra. Questo è il motivo per cui l'immagine di origine ( textureMap ) deve avere due copie della foto della superficie lunare. Dal momento che il rettangolo si sposta continuamente verso destra, nella maggior parte dei casi esso non si trova su una singola foto, ma a cavallo delle due.

  2. Il lento spostamento verso destra del rettangolo pone un unico problema. Alla fine, infatti, il rettangolo raggiungerà il margine estremo di textureMap e terminerà i pixel provenienti dalla foto della luna da copiare in sphere :

    La successiva riga di codice risolve questo problema:

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

    Il codice controlla se sourceX (il lato sinistro del rettangolo) ha raggiunto la metà di textureMap . Se questa condizione è vera, sourceX viene reimpostato su 0, con il conseguente spostamento del rettangolo sul margine sinistro di textureMap e il nuovo riavvio del ciclo.

  3. Dopo il calcolo del valore di sourceX , la fase finale per la creazione dell'animazione consiste nella copia dei pixel del nuovo rettangolo di origine su sphere . Il codice che effettua questa operazione è molto simile al codice che aveva compilato sphere all'inizio (descritto in precedenza); l'unica differenza è che in questo caso, nella chiamata alla funzione di costruzione newRectangle() , la posizione del margine sinistro del rettangolo viene assegnata da sourceX :

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

Questo codice viene chiamato ripetutamente, una volta ogni 15 millisecondi. Poiché la posizione del rettangolo di origine viene continuamente spostata e i pixel vengono copiati in sphere , su schermo verrà visualizzata la foto della luna rappresentata da sphere , in continuo scorrimento. In altre parole sembrerà che la luna ruoti costantemente.

Creazione dell'aspetto sferico

La luna, naturalmente, è una sfera e non un rettangolo. Di conseguenza, nel corso dell'animazione continua è necessario convertire la foto rettangolare della superficie lunare in sfera. Questa operazione richiede due passaggi distinti: la creazione di una maschera per nascondere tutto il contenuto tranne un'area circolare della foto della superficie lunare, e la creazione di un filtro mappa di spostamento per distorcere l'aspetto della foto della luna in modo che appaia tridimensionale.

Per prima cosa, si utilizza una maschera di forma circolare per nascondere tutto il contenuto dell'oggetto MoonSphere con l'eccezione dell'area circolare creata dal filtro. Il seguente codice crea la maschera come istanza Shape e la applica come maschera all'istanza MoonSphere:

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

Notate che, poiché MoonSphere è un oggetto di visualizzazione (è basato sulla classe Sprite), è possibile applicare la maschera direttamente all'istanza MoonSphere utilizzando la sua proprietà mask ereditata.

Nascondere alcune parti della foto utilizzando una maschera di forma circolare non è sufficiente per creare un effetto di rotazione sferica realistico. A causa del modo in cui è stata scattata la foto della superficie lunare, le dimensioni della stessa non sono proporzionali; le porzioni dell'immagine che si trovano in prossimità dei margini superiore e inferiore dell'immagine risultano più distorte e allungate rispetto alle parti che si trovano all'equatore. Per distorcere l'aspetto della foto e farla sembrare tridimensionale viene utilizzato un filtro mappa di spostamento.

Un filtro mappa di spostamento è un tipo di filtro utilizzato per distorcere un'immagine. In questo caso, per farla sembrare più realistica, la distorsione della foto della luna si ottiene schiacciando orizzontalmente la parte alta e quella bassa dell'immagine, e lasciando invariata la parte centrale. Poiché il filtro agisce su una porzione quadrata della foto, schiacciando la parte alta e quella bassa, ma non la parte centrale, il risultato sarà un cerchio. Un effetto secondario dell'animazione di questa immagine distorta è che la parte centrale dell'immagine sembra coprire una distanza maggiore in pixel rispetto alle aree vicine alla parte alta e a quella bassa; questo crea l'illusione che il cerchio sia un oggetto tridimensionale (una sfera).

Il seguente codice viene utilizzato per creare il filtro mappa di spostamento, denominato displaceFilter :

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

Il primo parametro, fisheyeLens , è detto immagine della mappa; in questo caso è un oggetto BitmapData che viene creato a livello di codice. La creazione di tale immagine è descritta in Creazione di un'immagine bitmap mediante l'impostazione dei valori dei pixel . Gli altri parametri descrivono la posizione all'interno dell'immagine filtrata in cui il filtro deve essere applicato, quali canali di colore devono essere utilizzati per controllare l'effetto di spostamento e fino a che punto influiranno sullo spostamento. Dopo la creazione del filtro mappa di spostamento questo viene applicato a sphere , sempre all'interno del metodo imageLoadComplete() :

sphere.filters = [displaceFilter];

L'immagine finale, con l'applicazione della maschera e del filtro mappa di spostamento, avrà questo aspetto:

A ogni ciclo dell'animazione della rotazione lunare, il contenuto BitmapData di sphere viene sovrascritto da una nuova istantanea dei dati dell'immagine di origine. Tuttavia, non è necessario riapplicare il filtro ogni volta. Questo perché il filtro viene applicato all'istanza Bitmap (l'oggetto di visualizzazione) e non ai dati bitmap (le informazioni originarie sui pixel). Tenete presente che l'istanza Bitmap non coincide con gli effettivi dati bitmap: si tratta solo di un oggetto di visualizzazione che mostra su schermo i dati bitmap. Per fare un'analogia, l'istanza Bitmap è come un proiettore usato per la proiezione di diapositive sullo schermo, mentre l'oggetto BitmapData è come la diapositiva vera e propria che può essere vista attraverso il proiettore. I filtri possono essere applicati direttamente a un oggetto BitmapData; nella nostra analogia questo significherebbe applicare un filtro direttamente sulla diapositiva per alterare l'immagine. I filtri possono tuttavia essere applicati anche a qualsiasi oggetto di visualizzazione, comprese le istanze Bitmap; questo può essere paragonato al mettere un filtro davanti alla lente del proiettore per distorcere la proiezione sullo schermo (senza alterare la diapositiva originale). Poiché i dati bitmap originari sono accessibili tramite una proprietà BitmapData dell'istanza Bitmap, si sarebbe potuto applicare il filtro direttamente su quei dati. Tuttavia, in questo caso ha più senso applicare il filtro all'oggetto di visualizzazione Bitmap anziché ai dati bitmap.

Per informazioni dettagliate riguardo all'uso del filtro mappa di spostamento in ActionScript, fate riferimento a Filtraggio degli oggetti di visualizzazione .

Creazione di un'immagine bitmap mediante l'impostazione dei valori dei pixel

Un aspetto importante del filtro mappa di spostamento è che agisce su due immagini. La prima, l'immagine di origine, è l'immagine effettivamente alterata dal filtro. In questo esempio, l'immagine di origine è l'istanza Bitmap denominata sphere . L'altra immagine utilizzata dal filtro è detta immagine mappa. L'immagine mappa non viene realmente visualizzata su schermo. Il colore di ognuno dei suoi pixel, infatti, è utilizzato come input per la funzione di spostamento: il colore del pixel che si trova in corrispondenza di certe coordinate x e y nell'immagine mappa determina la quantità di spostamento (spostamento fisico in posizione), che deve essere applicata al pixel corrispondente alle coordinate x e y dell'immagine di origine.

Di conseguenza, per utilizzare il filtro mappa di spostamento per creare un effetto sfera è necessario avere un'immagine mappa appropriata: un'immagine che abbia uno sfondo grigio e un cerchio che sia riempito con le tonalità di un singolo colore (rosso) che sfumino orizzontalmente da quelle più scure a quelle più chiare, come mostrato nella figura:

Poiché in questo esempio vengono utilizzate solo un'immagine mappa e un filtro, l'immagine mappa viene creata una sola volta, nel metodo imageLoadComplete() (in altre parole al termine del caricamento dell'immagine esterna). L'immagine mappa ( fisheyeLens ) viene creata chiamando il metodo createFisheyeMap() della classe MoonSphere:

var fisheyeLens:BitmapData = createFisheyeMap(radius);

All'interno del metodo createFisheyeMap() viene tracciata l'immagine mappa, un pixel alla volta, utilizzando il metodo setPixel() della classe BitmapData. Il codice completo per il metodo createFisheyeMap() è riportato di seguito, accompagnato da un'analisi passo per passo del suo funzionamento:

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

Per prima cosa quando il metodo viene chiamato riceve un parametro, radius , che indica il raggio dell'immagine circolare da creare. Quindi il codice crea l'oggetto BitmapData sul quale verrà tracciato il cerchio. Questo oggetto denominato result viene alla fine restituito come risultato del metodo. Come mostrato nel seguente snippet di codice, il valore result dell'istanza BitmapData viene creato con una larghezza e un'altezza pari al diametro del cerchio, senza trasparenza (il terzo parametro è impostato su false ) e riempito con il colore 0x808080 (grigio):

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

In seguito il codice utilizza due cicli da ripetere per ogni pixel dell'immagine. Il ciclo esterno esamina ogni colonna da sinistra a destra (servendosi della variabile i per rappresentare la posizione orizzontale del pixel manipolato al momento), mentre il ciclo interno esamina ogni pixel della colonna attiva dall'alto verso il basso (per mezzo della variabile j che rappresenta la posizione verticale del pixel attivo). Il codice per i cicli (il contenuto del ciclo interno è omesso) è riportato di seguito:

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

Man mano che i cicli esaminano i pixel uno ad uno, viene calcolato un valore per ogni pixel (il valore del colore di quel pixel nell'immagine mappa). Questo processo richiede quattro passaggi:

  1. Il codice calcola la distanza del pixel attivo dal centro del cerchio lungo l'asse x ( i - radius ). Questo valore è diviso per il raggio per ottenere un valore in percentuale anziché una distanza assoluta ( (i - radius) / radius ). Questo valore percentuale viene memorizzato in una variabile denominata pctX , e il valore equivalente per l'asse y viene calcolato e memorizzato nella variabile pctY , come mostrato nel seguente codice:

    var pctX:Number = (i - radius) / radius; 
    var pctY:Number = (j - radius) / radius;
  2. Utilizzando una formula trigonometrica standard, il teorema di Pitagora applicato a pctX e pctY , si calcola la distanza lineare tra il centro del cerchio e il pixel attivo. Questo valore viene memorizzato in una variabile denominata pctDistance , come mostrato di seguito:

    var pctDistance:Number = Math.sqrt(pctX * pctX + pctY * pctY);
  3. Successivamente il codice verifica se la distanza in percentuale è inferiore a 1 (cioè al 100% del raggio, o in altre parole, se il pixel attivo si trova all'interno del cerchio). Se il pixel si trova all'interno del cerchio, gli viene assegnato un valore di colore (la cui procedura è descritta in dettaglio nel passaggio 4); altrimenti il suo colore resterà quello predefinito, cioè grigio:

    if (pctDistance < 1) 
    { 
        ... 
    }
  4. Per i pixel che si trovano all'interno del cerchio viene calcolato un valore di colore. Il colore finale sarà una tonalità di rosso cha va dal nero (0% rosso) del margine sinistro del cerchio al rosso chiaro (100% rosso) del margine destro. Il valore del colore all'inizio viene calcolato nelle sue tre componenti (rosso, verde e blu) come mostrato dal seguente codice:

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

    Notare che è stato assegnato un valore soltanto alla componente rossa del valore del colore (la variabile red ). I valori del verde e del blu (le variabili green e blue ) sono mostrati solo per una maggiore chiarezza, ma possono essere omessi. Poiché lo scopo di questo metodo è di creare un cerchio che contenga le sfumature del rosso, i valori del verde e del blu non sono necessari.

    Una volta individuati i valori delle tre componenti del colore, essi vengono combinati in un singolo valore intero di colore utilizzando un algoritmo standard di spostamento di bit, mostrato nel seguente codice:

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

    Alla fine, dopo aver calcolato il valore del colore, questo valore viene assegnato al pixel attivo utilizzando il metodo setPixel() del result dell'oggetto BitmapData, illustrato di seguito.

    result.setPixel(i, j, rgb);