Creación y aplicación de filtros

Flash Player 9 y posterior, Adobe AIR 1.0 y posterior

Los filtros permiten aplicar toda una serie de efectos a los objetos de visualización y mapas de bits, desde las sombras hasta la creación de biseles y desenfoques. Cada filtro se define como una clase, de manera que al aplicar filtros se crean instancias de objetos de filtro, lo que es exactamente igual que crear cualquier otro objeto. Una vez creada una instancia de un objeto de filtro, se puede aplicar fácilmente a un objeto de visualización utilizando la propiedad filters del objeto o, en el caso de un objeto BitmapData, usando el método applyFilter() .

Crear un filtro

Para crear un objeto de filtro, basta con llamar al método constructor de la clase de filtro seleccionada. Por ejemplo, para crear un objeto DropShadowFilter, se usaría el siguiente código:

import flash.filters.DropShadowFilter; 
var myFilter:DropShadowFilter = new DropShadowFilter();

Aunque no se muestra aquí, el constructor DropShadowFilter() (como todos los constructores de clases de filtros) acepta varios parámetros opcionales que se pueden utilizar para personalizar la apariencia del efecto de filtro.

Aplicación de un filtro

Una vez construido un objeto de filtro, es posible aplicarlo a un objeto de visualización o a un objeto BitmapData; la forma de aplicarlo depende del objeto al que se vaya a aplicar.

Aplicación de un filtro a un objeto de visualización

Los efectos de filtro se aplican a los objetos de visualización mediante la propiedad filters . Dicha propiedad filters de los objetos de visualización es una instancia de Array cuyos elementos son los objetos de filtro aplicados al objeto de visualización. Para aplicar un único filtro a un objeto de visualización, se debe crear la instancia de filtro, añadirla a una instancia de Array y asignar ese objeto Array a la propiedad filters del objeto de visualización:

import flash.display.Bitmap; 
import flash.display.BitmapData; 
import flash.filters.DropShadowFilter; 
 
// Create a bitmapData object and render it to screen 
var myBitmapData:BitmapData = new BitmapData(100,100,false,0xFFFF3300); 
var myDisplayObject:Bitmap = new Bitmap(myBitmapData); 
addChild(myDisplayObject); 
 
// Create a DropShadowFilter instance. 
var dropShadow:DropShadowFilter = new DropShadowFilter(); 
 
// Create the filters array, adding the filter to the array by passing it as  
// a parameter to the Array() constructor. 
var filtersArray:Array = new Array(dropShadow); 
 
// Assign the filters array to the display object to apply the filter. 
myDisplayObject.filters = filtersArray;

Si se desea asignar varios filtros al objeto, basta con añadir todos los filtros a la instancia de Array antes de asignarla a la propiedad filters . Se pueden añadir varios objetos a un elemento Array pasándolos como parámetros a su constructor. Por ejemplo, el siguiente código aplica un filtro de bisel y otro de iluminado al objeto de visualización creado con anterioridad:

import flash.filters.BevelFilter; 
import flash.filters.GlowFilter; 
 
// Create the filters and add them to an array. 
var bevel:BevelFilter = new BevelFilter(); 
var glow:GlowFilter = new GlowFilter(); 
var filtersArray:Array = new Array(bevel, glow); 
 
// Assign the filters array to the display object to apply the filter. 
myDisplayObject.filters = filtersArray;

Es posible crear un conjunto que contiene los filtros utilizando el constructor new Array() (según se muestra en los ejemplos anteriores) o se puede emplear la sintaxis literal de Array, encerrando los filtros entre corchetes ( [] ). Por ejemplo, esta línea de código:

var filters:Array = new Array(dropShadow, blur); 

hace lo mismo que esta otra:

var filters:Array = [dropShadow, blur];

Al aplicar varios filtros a objetos de visualización, la aplicación se lleva a cabo de forma acumulativa y secuencial. Por ejemplo, si un conjunto de filtros tiene dos elementos, un filtro de bisel añadido en primer lugar y un filtro de sombra en segundo, el filtro de sombra se aplicará tanto al filtro de bisel como al objeto de visualización. Esto se debe a que el filtro de sombra está en segundo lugar en el conjunto de filtros. Para aplicar filtros de forma no acumulativa, es necesario aplicar cada filtro a una nueva copia del objeto de visualización.

Si solo se está aplicando un filtro o unos pocos a un objeto de visualización, es posible crear la instancia del filtro y asignarlo al objeto en una única sentencia. Por ejemplo, la siguiente línea de código aplica un filtro de desenfoque a un objeto de visualización llamado myDisplayObject :

myDisplayObject.filters = [new BlurFilter()];

El código anterior crea una instancia de Array utilizando su sintaxis literal (con corchetes), crea una instancia de BlurFilter como elemento de Array y asigna dicho Array a la propiedad filters del objeto de visualización llamado myDisplayObject .

Eliminación de filtros de un objeto de visualización

Eliminar todos los filtros de un objeto de visualización es tan sencillo como asignar un valor nulo a la propiedad filters :

myDisplayObject.filters = null;

Si se han aplicado varios filtros a un objeto y se desea eliminar solamente uno de ellos, es necesario seguir una serie de pasos para cambiar el conjunto de la propiedad filters . Para obtener más información, consulte Posibles problemas al trabajar con filtros .

Aplicación de un filtro a un objeto BitmapData

Para aplicar un filtro a un objeto BitmapData es necesario utilizar el método applyFilter() del objeto BitmapData:

var rect:Rectangle = new Rectangle(); 
var origin:Point = new Point(); 
myBitmapData.applyFilter(sourceBitmapData, rect, origin, new BlurFilter());

El método applyFilter() aplica un filtro a un objeto BitmapData de origen, dando lugar a una nueva imagen filtrada. Este método no modifica la imagen original. En vez de eso, el resultado de la aplicación del filtro a la imagen original se almacena en la instancia de BitmapData en la que se llama al método applyFilter() .

Funcionamiento de los filtros

Al aplicar un filtro a un objeto de visualización, se crea en caché una copia del objeto original como un mapa de bits transparente.

Una vez que un filtro se ha aplicado a un objeto de visualización, el motor de ejecución almacena en caché el objeto como un mapa de bits mientras este mantiene una lista de filtros válida. Este mapa de bits de origen se usa luego como imagen original para todas las aplicaciones posteriores de efectos de filtros.

Normalmente, un objeto de visualización contiene dos mapas de bits: uno con el objeto de visualización de origen sin filtrar (original) y otro con la imagen final una vez filtrada. La imagen final es la que se utiliza en representaciones. Mientras el objeto de visualización no cambie, la imagen final no necesita actualización.

Posibles problemas al trabajar con filtros

Existen varias fuentes de confusión o problemas potenciales que hay que tener en cuenta al trabajar con filtros.

Filtros y caché de mapas de bits

Para aplicar un filtro a un objeto de visualización, debe estar activada la caché de mapa de bits para ese objeto. Cuando se aplica un filtro a un objeto de visualización cuya propiedad cacheAsBitmap se establece en false , la propiedad cacheAsBitmap del objeto se define automáticamente como true . Si posteriormente se eliminan todos los filtros del objeto de visualización, la propiedad cacheAsBitmap recupera el valor en que se estableció por última vez.

Cambio de filtros en tiempo de ejecución

Si un objeto de visualización ya tiene aplicados uno o varios filtros, no podrá cambiar este grupo de filtros agregando o eliminando filtros en el conjunto de la propiedad filters . En su lugar, para modificar el grupo de filtros aplicados o añadir otros nuevos, debe aplicar los cambios en un conjunto diferente y, a continuación, asignar dicho conjunto a la propiedad filters del objeto de visualización correspondiente a los filtros que se aplicarán al objeto. El modo más sencillo de realizar esta tarea consiste en leer el conjunto de la propiedad filters en una variable Array y aplicar las modificaciones en este conjunto temporal. A continuación, se puede volver a asignar este conjunto a la propiedad filters del objeto de visualización. En los casos más complejos, es posible que sea necesario mantener un conjunto maestro de filtros diferente. Los cambios se aplicarán a este conjunto maestro para después asignarlo a la propiedad filters del objeto de visualización tras cada modificación.

Añadir un filtro adicional

El siguiente código demuestra el proceso de añadir un filtro adicional a un objeto de visualización que ya tiene aplicados uno o varios filtros. Inicialmente se aplica un filtro de iluminado al objeto de visualización llamado myDisplayObject ; luego, cuando se hace clic en el objeto, se llama a la función addFilters() . Mediante esta función se aplican dos filtros más a: myDisplayObject :

import flash.events.MouseEvent; 
import flash.filters.*; 
 
myDisplayObject.filters = [new GlowFilter()]; 
 
function addFilters(event:MouseEvent):void 
{ 
    // Make a copy of the filters array. 
    var filtersCopy:Array = myDisplayObject.filters; 
 
    // Make desired changes to the filters (in this case, adding filters). 
    filtersCopy.push(new BlurFilter()); 
    filtersCopy.push(new DropShadowFilter()); 
 
    // Apply the changes by reassigning the array to the filters property. 
    myDisplayObject.filters = filtersCopy; 
} 
 
myDisplayObject.addEventListener(MouseEvent.CLICK, addFilters);

Eliminación de un filtro de un grupo de filtros

Si un objeto de visualización tiene aplicados varios filtros y desea eliminar uno de ellos y seguir aplicando los demás, cópielos en un conjunto temporal, elimine de este el filtro que desee y vuelva a asignar el conjunto temporal a la propiedad filters del objeto de visualización. En la sección Recuperación de valores y eliminación de elementos de conjunto se describen varias formas de eliminar uno o varios elementos de un conjunto.

La forma más sencilla consiste en eliminar el filtro situado al principio del objeto (el último filtro que se le ha aplicado). Se puede utilizar el método pop() de la clase Array para eliminar el filtro del conjunto:

// Example of removing the top-most filter from a display object  
// named "filteredObject". 
 
var tempFilters:Array = filteredObject.filters; 
 
// Remove the last element from the Array (the top-most filter). 
tempFilters.pop(); 
 
// Apply the new set of filters to the display object. 
filteredObject.filters = tempFilters;

De igual modo, para eliminar el filtro situado al final (el primero aplicado al objeto) se puede utilizar el mismo código, substituyendo el método shift() de la clase Array en lugar del método pop() .

Para eliminar un filtro del centro de un conjunto de filtros (suponiendo que el conjunto tenga más de dos filtros), utilice el método splice() . Es preciso conocer el índice (la posición en el conjunto) del filtro que se desee eliminar. Por ejemplo, con el siguiente código se elimina el segundo filtro (el del índice 1) de un objeto de visualización:

// Example of removing a filter from the middle of a stack of filters 
// applied to a display object named "filteredObject". 
 
var tempFilters:Array = filteredObject.filters; 
 
// Remove the second filter from the array. It's the item at index 1  
// because Array indexes start from 0. 
// The first "1" indicates the index of the filter to remove; the  
// second "1" indicates how many elements to remove. 
tempFilters.splice(1, 1); 
 
// Apply the new set of filters to the display object. 
filteredObject.filters = tempFilters;

Determinación del índice de un filtro

Es preciso saber qué filtro que se va a eliminar del conjunto, de forma que se pueda identificar el índice correspondiente. Debe conocer (por el modo en que está diseñada la aplicación) o calcular el índice del filtro que se va a eliminar.

El mejor enfoque consiste en diseñar la aplicación de forma que el filtro que se desee eliminar se encuentre siempre en la misma posición en el grupo de filtros. Por ejemplo, si existe un único objeto de visualización que tiene aplicados un filtro de convolución y otro de sombra (en este orden) y desea eliminar este último y mantener el anterior, el filtro se debe situar en una posición conocida (la superior) de forma que se pueda determinar con antelación el método de Array que hay que utilizar (en este caso, Array.pop() para eliminar el filtro de sombra).

Si el filtro que desea eliminar siempre es de un tipo específico, pero no se sitúa siempre en la misma posición del grupo de filtros, puede comprobar el tipo de datos de cada filtro del conjunto para determinar cuál hay que eliminar. Por ejemplo, el siguiente código identifica el filtro de iluminado dentro de un grupo de filtros y lo elimina de este.

// Example of removing a glow filter from a set of filters, where the 
//filter you want to remove is the only GlowFilter instance applied  
// to the filtered object. 
 
var tempFilters:Array = filteredObject.filters; 
 
// Loop through the filters to find the index of the GlowFilter instance. 
var glowIndex:int; 
var numFilters:int = tempFilters.length; 
for (var i:int = 0; i < numFilters; i++) 
{ 
    if (tempFilters[i] is GlowFilter) 
    { 
        glowIndex = i; 
        break; 
    } 
} 
 
// Remove the glow filter from the array. 
tempFilters.splice(glowIndex, 1); 
 
// Apply the new set of filters to the display object. 
filteredObject.filters = tempFilters;

En un caso más complejo, por ejemplo, cuando el filtro que se va a eliminar se selecciona en tiempo de ejecución, la mejor solución consiste en crear un copia persistente e independiente del conjunto de filtros que sirve como la lista maestra de filtros. Cada vez que modifique el grupo de filtros, debe cambiar la lista maestra y, a continuación, aplicar el conjunto de filtros como la propiedad filters del objeto de visualización.

Por ejemplo, en el siguiente listado de código se aplican varios filtros de convolución a un objeto de visualización para crear diferentes efectos visuales y, en un punto posterior de la aplicación, se elimina uno de estos filtros y se mantienen los demás. En este caso, el código conserva una copia maestra del conjunto de filtros, así como una referencia al filtro que se va a eliminar. La identificación y eliminación del filtro específico es un proceso similar al anterior, excepto que en lugar de realizar una copia temporal del conjunto de filtros, se manipula la copia maestra y se aplica después al objeto de visualización.

// Example of removing a filter from a set of  
// filters, where there may be more than one  
// of that type of filter applied to the filtered  
// object, and you only want to remove one. 
 
// A master list of filters is stored in a separate, 
// persistent Array variable. 
var masterFilterList:Array; 
 
// At some point, you store a reference to the filter you 
// want to remove. 
var filterToRemove:ConvolutionFilter; 
 
// ... assume the filters have been added to masterFilterList, 
// which is then assigned as the filteredObject.filters: 
filteredObject.filters = masterFilterList; 
 
// ... later, when it's time to remove the filter, this code gets called: 
 
// Loop through the filters to find the index of masterFilterList. 
var removeIndex:int = -1; 
var numFilters:int = masterFilterList.length; 
for (var i:int = 0; i < numFilters; i++) 
{ 
    if (masterFilterList[i] == filterToRemove) 
    { 
        removeIndex = i; 
        break; 
    } 
} 
 
if (removeIndex >= 0) 
{ 
    // Remove the filter from the array. 
    masterFilterList.splice(removeIndex, 1); 
 
    // Apply the new set of filters to the display object. 
    filteredObject.filters = masterFilterList; 
}

En este enfoque (cuando se compara una referencia a un filtro almacenado con los elementos del conjunto de filtros para determinar el filtro que se va a eliminar), se debe crear una copia independiente de conjunto de filtros — el código no funciona si se compara la referencia al filtro almacenado con los elementos de un conjunto temporal copiado a partir de la propiedad filters del objeto de visualización. El motivo es que al asignar un conjunto a la propiedad filters , el motor de ejecución realiza internamente una copia de cada objeto de filtro del conjunto. Y son estas copias (en lugar de los objetos originales) las que se aplican al objeto de visualización, de forma que al leer la propiedad filters en un conjunto temporal, este contiene referencias a los objetos de filtro copiados en lugar de las referencias a los objetos de filtro originales. En consecuencia, si en el ejemplo anterior se intenta determinar el índice de filterToRemove por comparación con los filtros de un conjunto de filtros temporal, no se obtendrá ninguna coincidencia.

Filtros y transformaciones de objetos

Ninguna región filtrada (una sombra, por ejemplo) que se encuentre fuera del recuadro de delimitación rectangular de un objeto de visualización se considera parte de la superficie a efectos de la detección de colisiones (determinar si una instancia se solapa o corta con otra instancia). Dado que los métodos de detección de colisiones de la clase DisplayObject se basan en vectores, no es posible llevar a cabo esta operación sobre un resultado que es un mapa de bits. Por ejemplo, si se aplica un filtro de bisel a una instancia de botón, la detección de colisiones no estará disponible en la parte biselada de la instancia.

Los filtros no son compatibles con el escalado, la rotación ni el sesgo. Si el objeto de visualización al que se le ha aplicado un filtro es escalado (si scaleX y scaleY no tienen un valor de 100%), el efecto de filtro no se escalará con la instancia. Esto quiere decir que la forma original de la instancia rotará, se escalará o sesgará, pero el filtro no lo hará junto con la instancia.

Se puede animar una instancia con un filtro para crear efectos realistas o anidar instancias y usar la clase BitmapData para animar filtros y así lograr este efecto.

Filtros y objetos de mapas de bits

Al aplicar un filtro a un objeto BitmapData, la propiedad cacheAsBitmap toma automáticamente el valor true . De este modo, el filtro se aplica en realidad a la copia del objeto en lugar de al original.

Esta copia se coloca a continuación en la visualización principal (sobre el objeto original) tan cerca como sea posible del píxel más cercano. Si los límites del mapa de bits original cambian, la copia del mapa de bits con el filtro se vuelve a crear de nuevo en lugar de estirarla o distorsionarla.

Si se quitan todos los filtros de un objeto de visualización, se restablece el valor que la propiedad cacheAsBitmap tenía antes de aplicarse el filtro.