Ejemplo: Luna giratoria animada



Con el ejemplo de la luna giratoria animada se muestran estas técnicas para trabajar con objetos Bitmap y datos de imagen de mapa de bits (objetos BitmapData). El ejemplo crea una animación de una luna esférica giratoria utilizando una imagen plana de la superficie de la luna como datos de imagen sin procesar. Se muestran las siguientes técnicas:

  • Carga de una imagen externa y acceso a sus datos de imagen sin procesar.

  • Creación de una animación copiando píxeles de forma repetida de diferentes partes de una imagen de origen.

  • Creación de una imagen de mapa de bits estableciendo valores de píxel.

Para obtener los archivos de la aplicación de este ejemplo, consulte www.adobe.com/go/learn_programmingAS3samples_flash_es. Los archivos de la aplicación de la luna giratoria animada se encuentran en la carpeta Samples/SpinningMoon. La aplicación consta de los siguientes archivos:

Archivo

Descripción

SpinningMoon.mxml

o

SpinningMoon.fla

Archivo principal de la aplicación en Flex (MXML) o en Flash (FLA).

com/example/programmingas3/moon/MoonSphere.as

Clase que realiza la funcionalidad de cargar, visualizar y animar la luna.

moonMap.png

Archivo de imagen que contiene una fotografía de la superficie de la luna, que se carga y se utiliza para crear la luna giratoria animada.

Carga de una imagen externa como datos de mapa de bits

La primera tarea importante que se realiza en este ejemplo es cargar un archivo de imagen externo, que es una fotografía de la superficie de la luna. La operación de carga se administra mediante dos métodos en la clase MoonSphere: el constructor MoonSphere(), donde se inicia el proceso de carga y el método imageLoadComplete(), que se llama cuando la imagen externa está completamente cargada.

La carga de una imagen externa es similar a la carga de un archivo SWF externo; ambos utilizan una instancia de la clase flash.display.Loader para realizar la operación de carga. El código real del método MoonSphere() que inicia la carga de la imagen se presenta del siguiente modo:

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

La primera línea declara la instancia de Loader denominada imageLoader. La tercera línea inicia realmente el proceso de carga llamando al método load() del objeto Loader, transmitiendo una instancia de URLRequest que representa la URL de la imagen que se va a cargar. La segunda línea configura el detector de eventos que se activará cuando la imagen se haya cargado por completo. Se debe tener en cuenta que el método addEventListener() no se llama en la propia instancia de Loader; en cambio, se llama en la propiedad contentLoaderInfo del objeto Loader. La propia instancia de Loader no distribuye eventos relacionados con el contenido que se está cargando. No obstante, su propiedad contentLoaderInfo contiene una referencia al objeto LoaderInfo que se asocia al contenido que se está cargando en el objeto Loader (la imagen externa en este caso). El objeto LoaderInfo proporciona varios eventos relacionados con el curso y la finalización de la carga de contenido externo, incluyendo el evento complete (Event.COMPLETE) que activará una llamada al método imageLoadComplete() cuando la imagen se haya cargado por completo.

Aunque el inicio de la carga de la imagen externa es una parte importante del proceso, es igualmente relevante saber qué hacer cuando finalice la carga. Tal y como se muestra en el código anterior, la función imageLoadComplete() se llama cuando se carga la imagen. La función realiza varias operaciones con los datos de imagen cargados, lo que se describe en secciones posteriores. No obstante, para utilizar los datos de imagen, es necesario acceder a los mismos. Si se usa un objeto Loader para cargar una imagen externa, la imagen cargada se convierte en una instancia de Bitmap, que se asocia como objeto de visualización secundario del objeto Loader. En este caso, la instancia de Loader está disponible para el método del detector de eventos como parte del objeto de evento que se transmite al método como parámetro. Las primeras líneas del método imageLoadComplete() se presentan del siguiente modo:

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

Observe que el parámetro del objeto de evento se denomina event y es una instancia de la clase Event. Todas las instancias de la clase Event disponen de una propiedad target, que hace referencia al objeto que activa el evento (en este caso, la instancia de LoaderInfo en la que se llamó el método addEventListener(), tal y como se describió anteriormente). Por su parte, el objeto LoaderInfo tiene una propiedad content que (una vez completado el proceso de carga) contiene la instancia de Bitmap con la imagen de mapa de bits cargada. Si desea visualizar la imagen directamente en pantalla, puede asociar esta instancia de Bitmap (event.target.content) a un contenedor de objetos de visualización. (También puede asociar el objeto Loader a un contenedor de objetos de visualización). No obstante, en este ejemplo el contenido cargado se utiliza como origen de los datos de la imagen sin procesar en lugar de mostrarse en pantalla. Por ello, la primera línea del método imageLoadComplete() lee la propiedad bitmapData de la instancia de Bitmap cargada (event.target.content.bitmapData) y la almacena en la variable de la instancia denominada textureMap, que, tal y como se describe en la siguiente sección, se utiliza como origen de los datos de imagen para crear la animación de la luna en rotación.

Creación de animación con copia de píxeles

Una definición básica de animación es la ilusión de movimiento o cambio, creado mediante el cambio de una imagen en el tiempo. En este ejemplo, el objetivo es crear la ilusión de una rotación de la luna esférica alrededor de su eje vertical. Sin embargo, para los objetivos de la animación, se puede omitir el aspecto de distorsión esférica del ejemplo. Observe la imagen real que se carga y se utiliza como el origen de los datos de imagen de la luna:

Como se puede ver, la imagen no es una o varias esferas; se trata de una fotografía rectangular de la superficie de la luna. Debido a que la foto fue tomada exactamente en el ecuador de la luna, las partes de la imagen más cercanas a la parte superior e inferior de la imagen están expandidas y distorsionadas. Para eliminar la distorsión de la imagen y hacerla parecer esférica, utilizaremos un filtro de mapa de desplazamiento, tal y como se describe más adelante. Son embargo. debido a que esta imagen de origen es un rectángulo, para crear la ilusión de que la esfera está girando, el código simplemente necesita deslizar la fotografía de la superficie de la luna horizontalmente, tal y como se indica en los siguientes párrafos.

Se debe tener en cuenta que la imagen incluye realmente dos copias de la fotografía de la superficie de la luna próximas entre sí. Esta imagen es la de origen a partir de la que los datos de imagen se copian repetidamente para crear la apariencia de movimiento. Al tener dos copias de la imagen próximas entre sí, un efecto de desplazamiento ininterrumpido y continuo puede ser más fácil de crear. Analicemos el proceso de animación paso a paso para ver su funcionamiento.

El proceso incluye dos objetos de ActionScript independientes. En primer lugar se dispone de la imagen de origen cargada, que en el código se representa mediante una instancia de BitmapData denominada textureMap. Tal y como se ha descrito anteriormente, textureMap se llena con datos de imagen una vez cargada la imagen externa, utilizando este código:

textureMap = event.target.content.bitmapData;

El contenido de textureMap es la imagen mostrada anteriormente. Asimismo, para crear la rotación animada, en el ejemplo se utiliza una instancia de Bitmap denominada sphere, que es el objeto de visualización real que muestra la imagen de la luna en pantalla. Al igual que sucede con textureMap, el objeto sphere se crea y se llena con sus datos de imagen inicial en el método imageLoadComplete(), empleando el siguiente código:

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

Tal y como muestra el código, se crea una instancia de sphere. Su propiedad bitmapData (los datos de la imagen sin procesar que se muestran mediante sphere) se crea con la misma altura y la mitad de anchura de textureMap. Es decir, el contenido de sphere será el tamaño de una fotografía de la luna (ya que la imagen textureMap contiene dos fotografías de la luna, una junto a otra). A continuación, la propiedad bitmapData se llena con los datos de imagen utilizando su método copyPixels(). Los parámetros en la llamada al método copyPixels() indican varios puntos:

  • El primer parámetro indica que los datos de imagen se copian desde textureMap.

  • El segundo parámetro, una nueva instancia de Rectangle, especifica desde qué parte de textureMap se debe tomar la instantánea de la imagen; en este caso, la instantánea es un rectángulo que comienza en la esquina superior izquierda de textureMap (indicado mediante los dos primeros parámetros Rectangle(): 0, 0) y la altura y anchura de la instantánea del rectángulo coinciden con las propiedades width y height de sphere.

  • El tercer parámetro, una nueva instancia de Point con los valores x e y de 0, define el destino de los datos de píxel; en este caso, la esquina superior izquierda de (0, 0) de sphere.bitmapData.

Representado visualmente, el código copia los píxeles desde textureMap destacado en la siguiente imagen y los pega en sphere. Es decir, el contenido de BitmapData de sphere es la parte de textureMap resaltada aquí:

No obstante, recuerde que esto sólo es el estado inicial de sphere; el contenido de la primera imagen que se copia en sphere.

Con la imagen de origen cargada y sphere creada, la tarea final realizada por el método imageLoadComplete() es configurar la animación. La animación se activa mediante una instancia de Timer denominadarotationTimer, que se crea y se inicia mediante el siguiente código:

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

En primer lugar, el código crea la instancia de Timer denominada rotationTimer; el parámetro transmitido al constructor Timer() indica que rotationTimer debe activar su evento timer cada 15 millisegundos. A continuación, se llama al método addEventListener(), especificando que cuando sucede el evento timer (TimerEvent.TIMER), se llama al método rotateMoon(). Finalmente, timer se inicia realmente llamando a su método start().

Debido al modo en que se define rotationTimer, aproximadamente cada 15 millisegundos Flash Player llama al método rotateMoon() en la clase MoonSphere, que es donde sucede la animación de la luna. El código fuente del método rotateMoon() se presenta del siguiente modo:

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

El código realiza tres operaciones:

  1. El valor de la variable sourceX (establecido inicialmente en 0) se incrementa en 1.

    sourceX += 1;

    Tal y como se puede observar, sourceX se usa para determinar la ubicación en textureMap desde donde los píxeles se copiarán en sphere, por lo que este código tiene el efecto de mover el rectángulo un píxel a la derecha en textureMap. Volviendo a la representación visual, tras varios ciclos de animación, el rectángulo de origen se habrá movido varios píxeles hacia la derecha, tal y como se muestra a continuación:

    Tras varios ciclos más, el rectángulo se habrá movido incluso más lejos:

    Este cambio constante y gradual en la ubicación desde la que se copian los píxeles es la clave en la animación. Con el movimiento lento y continuo de la ubicación de origen hacia la derecha, la imagen que se muestra en la pantalla en sphere parecer deslizarse continuamente hacia la izquierda. Por este motivo, es necesario que la imagen de origen (textureMap) tenga dos copias de la fotografía de la superficie de la luna. Debido a que el rectángulo se mueve continuamente hacia derecha, la mayor parte del tiempo no lo hace sobre una sola fotografía de la luna, sino que se superponen las dos fotografías.

  2. Con el movimiento lento del rectángulo de origen hacia la derecha, existe un problema. Finalmente el rectángulo llegará al borde derecho de textureMap y no dispondrá de píxeles de la fotografía de la luna para copiar en sphere:

    En las siguientes líneas de código se aborda este problema:

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

    El código comprueba si sourceX (borde izquierdo del rectángulo) ha alcanzado la mitad de textureMap. Si es así, vuelve a restablecer sourceX a 0, moviéndolo de nuevo hacia el borde izquierdo de textureMap y volviendo a iniciar el ciclo:

  3. Con el valor adecuado y calculado de sourceX, el paso final en la creación de la animación consiste en copiar los nuevos píxeles del rectángulo de origen en sphere. El código que realiza esta operación es muy similar al que llenó en un principio sphere (descrito anteriormente); en este caso la única diferencia es que en la llamada al constructor new Rectangle(), el borde izquierdo del rectángulo se sitúa en sourceX:

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

Recuerde que este código se llama repetidamente, cada 15 milisegundos. Debido a que la ubicación del rectángulo de origen se desplaza continuamente y los píxeles se copian en sphere, la apariencia en pantalla es que la imagen de la fotografía de la luna representada mediante sphere se desliza constantemente. Es decir, la luna parece girar de forma continua.

Creación de la apariencia esférica

Obviamente, la luna es una esfera y no un rectángulo. Por lo tanto, en el ejemplo es necesario tomar la fotografía de la superficie de la luna rectangular, conforme se anima continuamente, y convertirla en una esfera. Esto implica dos pasos independientes: una máscara se utiliza para ocultar todo el contenido excepto una región circular de la fotografía de la superficie lunar y un filtro de mapa de desplazamiento se emplea para distorsionar la apariencia de la fotografía para hacerla parecer tridimensional.

En primer lugar, se usa una máscara con forma de círculo para ocultar todo el contenido del objeto MoonSphere excepto la esfera creada por el filtro. El siguiente código crea la máscara como una instancia de Shape y la aplica como la máscara de la instancia de MoonSphere:

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

Se debe tener en cuenta que dado que MoonSphere es un objeto de visualización (se basa en la clase Sprite), la máscara se puede aplicar directamente a la instancia de MoonSphere utilizando su propiedad heredada mask.


Ocultar simplemente las partes de la fotografía utilizando una máscara con forma de círculo no es suficiente para crear un efecto de esfera giratoria con un aspecto realista. Debido al modo en que se tomó la fotografía de la superficie lunar, sus dimensiones no son proporcionales; las partes de la imagen que se encuentran más hacia la parte superior o inferior de la imagen están más distorsionadas y expandidas en comparación con las partes del ecuador. Para distorsionar la apariencia de la fotografía para hacerla parecer tridimensional, utilizaremos un filtro de mapa de desplazamiento.

Un filtro de mapa de desplazamiento es un tipo de filtro que se usa para distorsionar una imagen. En este caso, la fotografía de la luna se “distorsionará” para hacerla más realista, contrayendo la parte superior e inferior de la imagen horizontalmente, mientras el centro se deja sin cambio. Suponiendo que el filtro funcione en una parte con forma de cuadrado de la fotografía, la contracción de la parte superior e inferior pero no del centro convertirá el cuadrado en un círculo. Un efecto indirecto de la animación de esta imagen distorsionada es que el centro de la imagen parece moverse más lejos en la distancia de píxeles real que las áreas cercanas a la parte superior e inferior, lo que crea la ilusión de que el círculo es realmente un objeto tridimensional (una esfera).

El siguiente código se utiliza para crear el filtro de mapa de desplazamiento denominado displaceFilter:

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

El primer parámetro, fisheyeLens, se conoce como la imagen del mapa; en este caso se trata de un objeto BitmapData que se crea mediante programación. La creación de esta imagen se describe más adelante en la sección Creación de una imagen de mapa de bits estableciendo valores de píxel. Los demás parámetros describen la posición en la imagen filtrada en la que se debe aplicar el filtro, qué canales de color se utilizarán para controlar el efecto de desplazamiento y hasta qué punto afectarán al desplazamiento. Una vez creado el filtro de mapa de desplazamiento, se aplica a sphere, aún en el método imageLoadComplete():

sphere.filters = [displaceFilter];

La imagen final, con máscara y filtro de mapa de desplazamiento aplicados, presenta el siguiente aspecto:


Con cada ciclo de la animación de rotación de la luna, el contenido de BitmapData de la esfera se sobrescribe con una nueva instantánea de los datos de la imagen de origen. No obstante, el filtro no necesita volver a aplicarse cada vez. Esto se debe a que el filtro se aplica en la instancia de Bitmap (el objeto de visualización) en lugar de en los datos del mapa de bits (información del píxel sin procesar). Recuerde que la instancia de Bitmap no son los datos de mapa de bits reales; se trata de un objeto de visualización que muestra los datos de mapa de bits en pantalla. Para utilizar una analogía, una instancia de Bitmap es como el proyector de diapositivas que se utiliza para visualizar diapositivas fotográficas en una pantalla y un objeto BitmapData es como la diapositiva real que se puede presentar mediante un proyector. Es posible aplicar un filtro directamente a un objeto BitmapData, lo que sería comparable a dibujar directamente en una diapositiva fotográfica para alterar la imagen. También se puede aplicar un filtro a cualquier objeto de visualización, incluyendo una instancia de Bitmap; esto sería como colocar un filtro frente a la lente del proyector de diapositivas para distorsionar el resultado que se muestra en pantalla (sin que la diapositiva original se vea alterada). Debido a que se puede acceder a los datos de mapa de bits mediante la propiedad bitmapData de una instancia de Bitmap, el filtro podría haberse aplicado directamente en los datos de mapa bits sin procesar. Sin embargo, en este caso resulta lógico aplicar el filtro al objeto de visualización Bitmap en lugar de a los datos de mapa de bits.

Para obtener información detallada sobre el uso del filtro de mapa de desplazamiento en ActionScript, consulte Aplicación de filtros a objetos de visualización.

Creación de una imagen de mapa de bits estableciendo valores de píxel

Un aspecto importante del filtro de mapa de desplazamiento es que implica realmente dos imágenes. Una imagen, la imagen de origen, es la que se vé modificada por el filtro. En este ejemplo, la imagen de origen en la instancia de Bitmap denominada sphere. La otra imagen utilizada por el filtro se denomina imagen del mapa. La imagen del mapa no se visualiza realmente en pantalla. En cambio, el color de cada uno de sus píxeles se utiliza como una entrada en la función de desplazamiento; el color del píxel en una determinada coordenada x, y en la imagen del mapa determina el grado de desplazamiento (cambio físico de posición) que se aplica al píxel en dicha coordenada x, y en la imagen de origen.

Por lo tanto, para utilizar el filtro de mapa de desplazamiento para crear un efecto de esfera, el ejemplo necesita la imagen del mapa adecuada; una imagen con fondo gris y un círculo relleno con un degradado de un solo color (rojo) que pase horizontalmente de un tono oscuro a claro, tal y como se muestra a continuación:


Debido a que únicamente se utiliza un filtro y una imagen del mapa en este ejemplo, la imagen del mapa sólo se crea una sola vez, en el método imageLoadComplete() (es decir, cuando la imagen externa termina de cargarse). La imagen del mapa, denominada fisheyeLens, se crea llamando al método createFisheyeMap() de la clase MoonSphere:

var fisheyeLens:BitmapData = createFisheyeMap(radius);

Dentro del método createFisheyeMap(), la imagen del mapa se dibuja un píxel cada vez utilizando el método setPixel() de la clase BitmapData. El código completo para el método createFisheyeMap() se incluye a continuación, seguido de un análisis paso a paso sobre su funcionamiento:

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

En primer lugar, cuando se llama al método éste recibe un parámetro, radius, que indica el radio de la imagen de forma circular que se va a crear. A continuación, el código crea el objeto BitmapData en el que se dibujará el círculo. Dicho objeto, llamado result, se transmite finalmente como el valor devuelto del método. Tal y como se muestra en el siguiente fragmento de código, la instancia de BitmapData result se crea con una anchura y una altura similares al diámetro del círculo, sin transparencia (false para el tercer parámetro) y rellena previamente con el color 0x808080 (gris medio):

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

A continuación, el código utiliza dos bucles para repetir cada píxel de la imagen. El bucle exterior recorre todas las columnas de la imagen de izquierda a derecha (utilizando la variable i para representar la posición horizontal del píxel que se está manipulando en ese momento), mientras que el bucle interior recorre todos los píxeles de la columna actual desde la parte superior a la inferior (con la variable j que representa la posición vertical del píxel actual). El código de los bucles (donde se omite el contenido del bucle interior) se muestra a continuación:

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

A medida que los bucles recorren los píxeles uno por uno, en cada píxel se calcula un valor (el valor del color del píxel en la imagen del mapa). Este proceso consta de cuatro pasos:

  1. El código calcula la distancia del píxel actual desde el centro del círculo a través del eje x (i - radius). Dicho valor se divide por el radio para hacerlo un porcentaje del radio en lugar de una distancia absoluta ((i - radius) / radius). Ese valor del porcentaje se almacena en una variable denominadapctX y el valor equivalente para el eje y se calcula y almacena en la variable pctY, tal y como se indica en este código:

    var pctX:Number = (i - radius) / radius; 
    var pctY:Number = (j - radius) / radius;
  2. Utilizado una fórmula trigonométrica estándar, el teorema de Pitágoras, la distancia lineal entre el centro del círculo y el punto actual se calcula a partir de pctX y pctY. Dicho valor se almacena en una variable denominada pctDistance, tal y como se muestra a continuación:

    var pctDistance:Number = Math.sqrt(pctX * pctX + pctY * pctY);
  3. A continuación, el código comprueba si el porcentaje de distancia es inferior a 1 (lo que significa el 100% del radio; es decir, si el píxel que se considera está en el radio del ciclo). Si el píxel se encuentra dentro del círculo, se la asigna un valor de color calculado (se omite aquí, pero se describe en el paso 4); de lo contrario, no sucede nada más con ese píxel, de forma que su color se deja como gris medio predeterminado:

    if (pctDistance < 1) 
    { 
        ... 
    }
  4. Para los píxeles que se encuentren dentro del círculo, se calcula un valor de color. El color final será una tonalidad de rojo que comprende desde negro (0% rojo) en el borde izquierdo del círculo hasta rojo claro (100%) en el borde derecho del círculo. El valor de color se calcula en un principio en tres partes (rojo, verde y azul), tal y como se indica a continuación:

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

    Se debe tener en cuenta que únicamente la parte roja del color (variable red) dispone realmente de un valor. Los valores de azul y verde (variables green y blue) se muestran aquí para mayor claridad, pero se pueden omitir. El objetivo de este método es crear un círculo que incluya un degradado de rojo, por lo que los valores de verde o azul no son necesarios.

    Una vez que se determinen los tres valores de color independientes, se combinan en un solo valor de color entero utilizando un algoritmo estándar de desplazamiento de bits, tal y como se muestra en este código:

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

    Finalmente, con el valor de color calculado, dicho valor se asigna al píxel actual utilizando el método setPixel() del objeto BitmapData de result, tal y como se indica a continuación:

    result.setPixel(i, j, rgb);