Ejemplo de filtrado de objetos de visualización: Filter Workbench

Flash Player 9 y posterior, Adobe AIR 1.0 y posterior

Filter Workbench proporciona una interfaz de usuario con la que es posible aplicar distintos filtros a imágenes y otro contenido visual y ver el código resultante que se puede utilizar para generar el mismo efecto en ActionScript. Además de ofrecer una herramienta para la experimentación con filtros, esta aplicación ilustra las siguientes técnicas:

  • Creación de instancias de diferentes filtros

  • Aplicación de varios filtros a un objeto de visualización

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 Filter Workbench se encuentran en la carpeta Samples/FilterWorkbench. La aplicación consta de los siguientes archivos:

Archivo

Descripción

com/example/programmingas3/filterWorkbench/FilterWorkbenchController.as

Clase que proporciona la principal funcionalidad de la aplicación, incluido el cambio de contenido al que se aplican los filtros y la aplicación de filtros al contenido.

com/example/programmingas3/filterWorkbench/IFilterFactory.as

Interfaz que define los métodos comunes implementados por cada una de las clases Factory de los filtros. Esta interfaz define la funcionalidad común que utiliza la clase FilterWorkbenchController para interactuar con cada clase Factory de los filtros.

En la carpeta com/example/programmingas3/filterWorkbench/:

BevelFactory.as

BlurFactory.as

ColorMatrixFactory.as

ConvolutionFactory.as

DropShadowFactory.as

GlowFactory.as

GradientBevelFactory.as

GradientGlowFactory.as

Conjunto de clases, cada una de las cuales implementa la interfaz IFilterFactory. Cada una de estas clases proporciona la funcionalidad de crear y definir valores para un único tipo de filtro. Los paneles de propiedad de filtro de la aplicación utilizan estas clases Factory para crear instancias de sus filtros específicos, los cuales son recuperados y aplicados al contenido de la imagen por la clase FilterWorkbenchController.

com/example/programmingas3/filterWorkbench/IFilterPanel.as

Interfaz que define los métodos comunes implementados por las clases que, a su vez, definen los paneles de interfaz de usuario utilizados para manipular los valores de filtro en la aplicación.

com/example/programmingas3/filterWorkbench/ColorStringFormatter.as

Clase de utilidad que incluye un método para convertir un valor de color numérico a un formato de cadena hexadecimal.

com/example/programmingas3/filterWorkbench/GradientColor.as

Clase que funciona como un objeto de valor, combinando en un único objeto los tres valores (color, alfa y proporción) asociados a cada color en las clases GradientBevelFilter y GradientGlowFilter.

Interfaz de usuario (Flex)

FilterWorkbench.mxml

El archivo principal que define la interfaz de usuario de la aplicación.

flexapp/FilterWorkbench.as

Clase que proporciona la funcionalidad para la interfaz de usuario principal de la aplicación; esta clase se utiliza como la clase de código subyacente para el archivo MXML de la aplicación.

En la carpeta flexapp/filterPanels:

BevelPanel.mxml

BlurPanel.mxml

ColorMatrixPanel.mxml

ConvolutionPanel.mxml

DropShadowPanel.mxml

GlowPanel.mxml

GradientBevelPanel.mxml

GradientGlowPanel.mxml

Conjunto de componentes MXML que proporciona la funcionalidad en cada panel utilizado para ajustar las opciones para un filtro único.

flexapp/ImageContainer.as

Un objeto de visualización que funciona como contenedor para la imagen cargada de la pantalla.

flexapp/controls/BGColorCellRenderer.as

Procesador de celdas personalizado que se utiliza para cambiar el color de fondo de una celda en el componente DataGrid.

flexapp/controls/QualityComboBox.as

Control personalizado que define un cuadro combinado que permite ajustar la calidad en varios paneles de filtros.

flexapp/controls/TypeComboBox.as

Control personalizado que define un cuadro combinado que permite ajustar el tipo en varios paneles de filtros.

Interfaz de usuario (Flash)

FilterWorkbench.fla

El archivo principal que define la interfaz de usuario de la aplicación.

flashapp/FilterWorkbench.as

Clase que proporciona la funcionalidad para la interfaz de usuario principal de la aplicación; esta clase se utiliza como la clase de documento para el archivo FLA de la aplicación.

En la carpeta flashapp/filterPanels:

BevelPanel.as

BlurPanel.as

ColorMatrixPanel.as

ConvolutionPanel.as

DropShadowPanel.as

GlowPanel.as

GradientBevelPanel.as

GradientGlowPanel.as

Conjunto de clases que proporciona la funcionalidad para cada panel utilizado para ajustar las opciones para un filtro único.

Para cada clase, existe además un símbolo MovieClip asociado en la biblioteca del archivo FLA principal de la aplicación, cuyo nombre coincide con el de la clase (por ejemplo, el símbolo “BlurPanel” está relacionado con la clase definida en BlurPanel.as). Los componentes que conforman la interfaz de usuario se ubican y designan de acuerdo con estos símbolos.

flashapp/ImageContainer.as

Un objeto de visualización que funciona como contenedor para la imagen cargada de la pantalla.

flashapp/BGColorCellRenderer.as

Procesador de celdas personalizado que se utiliza para cambiar el color de fondo de una celda en el componente DataGrid.

flashapp/ButtonCellRenderer.as

Procesador de celdas personalizado que se utiliza para incluir un componente de botón en una celda del componente DataGrid.

Contenido de imagen filtrado

com/example/programmingas3/filterWorkbench/ImageType.as

Esta clase sirve como objeto de valor que contiene el tipo y el URL del archivo de imagen única en el que la aplicación puede cargar y aplicar los filtros. La clase también incluye un conjunto de constantes que representan los archivos de imagen disponibles.

images/sampleAnimation.swf,

images/sampleImage1.jpg,

images/sampleImage2.jpg

Imágenes y otro contenido visual al que se aplican los filtros en la aplicación.

Pruebas con filtros de ActionScript

La aplicación Filter Workbench se ha diseñado para ayudar a probar los diferentes efectos de filtro y a generar el código ActionScript necesario para lograr cada uno. La aplicación permite elegir entre tres archivos diferentes que incluyen contenido visual, tales como imágenes de mapa de bits y animaciones creadas por Flash, así como aplicar ocho filtros de ActionScript diferentes a la imagen seleccionada, ya sea de forma individual o en combinación con otros filtros. La aplicación incluye los filtros siguientes:

  • Bisel (flash.filters.BevelFilter)

  • Desenfoque (flash.filters.BlurFilter)

  • Matriz de colores (flash.filters.ColorMatrixFilter)

  • Convolución (flash.filters.ConvolutionFilter)

  • Sombra (flash.filters.DropShadowFilter)

  • Iluminado (flash.filters.GlowFilter)

  • Bisel degradado (flash.filters.GradientBevelFilter)

  • Iluminado degradado (flash.filters.GradientGlowFilter)

Tras seleccionar una imagen y el filtro que se le va a aplicar, la aplicación muestra un panel con una serie de controles para el ajuste de las propiedades específicas del filtro elegido. Por ejemplo, en la siguiente imagen aparece la aplicación con el filtro de bisel seleccionado:

La vista previa se actualiza en tiempo real conforme se ajustan las propiedades del filtro. También es posible aplicar varios filtros. Para ello, personalice un filtro, haga clic en el botón Aplicar, personalice otro filtro y vuelva a hacer clic en dicho botón, y así sucesivamente.

A continuación se describen algunas funciones y limitaciones de los paneles de filtro de la aplicación:

  • El filtro de matriz de colores incluye un grupo de controles que permiten manipular de forma directa las propiedades de imagen comunes, tales como el brillo, el contraste, la saturación y el matiz. Asimismo, es posible especificar valores personalizados de la matriz de colores.

  • El filtro de convolución, disponible solo con ActionScript, incluye un conjunto de valores de matriz de convolución utilizados comúnmente, al tiempo que es posible especificar valores personalizados. No obstante, aunque la clase ConvolutionFilter acepta matrices de cualquier tamaño, la aplicación Filter Workbench utiliza una matriz fija de 3 x 3, el tamaño de filtro que se utiliza con más frecuencia.

  • Los filtros de mapa de desplazamiento y de sombreado no se encuentran disponibles en la aplicación Filter Workbench, solo en ActionScript.

Creación de instancias de filtros

La aplicación Filter Workbench incluye un conjunto de clases (una para cada filtro disponible) que son utilizadas por los distintos paneles para crear los filtros. Al seleccionar un filtro, el código de ActionScript asociado al panel correspondiente crea una instancia de la clase Factory de filtro apropiada. (Estas clases también se denominan clases de fábrica porque su finalidad es crear instancias de otros objetos, del mismo modo que en una fábrica del mundo real se crean productos individuales.)

Cada vez que se cambia el valor de una propiedad en el panel, el código de este llama al método apropiado de la clase de fábrica. Cada clase de fábrica incluye métodos específicos utilizados por el panel para crear la instancia de filtro apropiada. Por ejemplo, al seleccionar el filtro de desenfoque, la aplicación crea una instancia de BlurFactory. La clase BlurFactory incluye un método modifyFilter() que acepta tres parámetros: blurX , blurY , y quality , los cuales se utilizan conjuntamente para crear la instancia de BlurFilter deseada:

private var _filter:BlurFilter; 
 
public function modifyFilter(blurX:Number = 4, blurY:Number = 4, quality:int = 1):void 
{ 
    _filter = new BlurFilter(blurX, blurY, quality); 
    dispatchEvent(new Event(Event.CHANGE)); 
}

Por otro lado, al seleccionar el filtro de convolución se obtiene una mayor flexibilidad y, en consecuencia, es posible controlar un mayor número de propiedades. En la clase ConvolutionFactory se llama al siguiente código cuando se selecciona un valor diferente en el panel del filtro:

private var _filter:ConvolutionFilter; 
 
public function modifyFilter(matrixX:Number = 0,  
                                                matrixY:Number = 0,  
                                                matrix:Array = null,  
                                                divisor:Number = 1.0,  
                                                bias:Number = 0.0,  
                                                preserveAlpha:Boolean = true,  
                                                clamp:Boolean = true,  
                                                color:uint = 0,  
                                                alpha:Number = 0.0):void 
{ 
    _filter = new ConvolutionFilter(matrixX, matrixY, matrix, divisor, bias, preserveAlpha, clamp, color, alpha); 
    dispatchEvent(new Event(Event.CHANGE)); 
}

Observe en cada caso que, cuando se modifican los valores del filtro, el objeto Factory distribuye un evento Event.CHANGE para comunicar a los detectores que los valores del filtro han cambiado. La clase FilterWorkbenchController, responsable de aplicar los filtros al contenido filtrado, detecta dicho evento a fin de determinar cuándo es necesario recuperar una nueva copia del filtro y volver a aplicarla al contenido filtrado.

La clase FilterWorkbenchController no necesita conocer los detalles específicos de la clase de fábrica de cada filtro, solo saber que el filtro ha cambiado y poder acceder a una copia del mismo. Para que esto sea posible, la aplicación incluye una interfaz, IFilterFactory, que define el comportamiento que debe tener una clase de fábrica de filtro para que la instancia de FilterWorkbenchController de la aplicación pueda cumplir su cometido. IFilterFactory define el método getFilter () utilizado en la clase FilterWorkbenchController:

function getFilter():BitmapFilter;

Observe que la definición del método de interfaz getFilter() especifica que devuelve una instancia de BitmapFilter en lugar de un tipo de filtro específico. La clase BitmapFilter no define ningún tipo específico de filtro, sino que se trata de la clase de base a partir de la cual se crean todas las clases de filtro. Cada clase de fábrica de filtro define una implementación específica del método getFilter() en la cual se devuelve una referencia al objeto de filtro creado. Por ejemplo, aquí se muestra una versión abreviada del código fuente de la clase ConvolutionFactory:

public class ConvolutionFactory extends EventDispatcher implements IFilterFactory 
{ 
    // ------- Private vars ------- 
    private var _filter:ConvolutionFilter; 
    ... 
    // ------- IFilterFactory implementation ------- 
    public function getFilter():BitmapFilter 
    { 
        return _filter; 
    } 
    ... 
}

En la implementación de la clase ConvolutionFactory del método getFilter() se devuelve una instancia de ConvolutionFilter, aunque esto no lo sabrán necesariamente los objetos que llamen a getFilter() — según la definición del método getFilter() que sigue ConvolutionFactory, debe devolver una instancia de BitmapFilter, que podría ser una instancia de cualquiera de las clases de filtro de ActionScript.

Aplicación de filtros a objetos de visualización

Tal y como se ha explicado anteriormente, la aplicación Filter Workbench utiliza una instancia de la clase FilterWorkbenchController (denominada en lo sucesivo "instancia de controlador"), la cual realiza la tarea de aplicar los filtros al objeto visual seleccionado. Antes de que la instancia de controlador pueda aplicar un filtro, debe identificar la imagen o el contenido visual al que se aplicará. Cuando se selecciona una imagen, la aplicación llama al método setFilterTarget() de la clase FilterWorkbenchController, pasando una de las constantes definidas en la clase ImageType:

public function setFilterTarget(targetType:ImageType):void 
{ 
    ... 
    _loader = new Loader(); 
    ... 
    _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, targetLoadComplete); 
    ... 
}

Con esta información, la instancia de controlador carga el archivo designado y, a continuación, lo almacena en una variable de instancia denominada _currentTarget :

private var _currentTarget:DisplayObject; 
 
private function targetLoadComplete(event:Event):void 
{ 
    ... 
    _currentTarget = _loader.content; 
    ... 
}

Cuando se selecciona un filtro, la aplicación llama al método setFilter() de la instancia de controlador, ofreciendo al controlador una referencia al objeto de fábrica de filtro pertinente, el cual se almacena una variable de instancia denominada _filterFactory .

private var _filterFactory:IFilterFactory; 
 
public function setFilter(factory:IFilterFactory):void 
{ 
    ... 
     
    _filterFactory = factory; 
    _filterFactory.addEventListener(Event.CHANGE, filterChange); 
}

Observe que, tal y como se ha descrito anteriormente, la instancia de controlador no conoce el tipo específico de datos de la instancia de fábrica de filtro que se le facilita; solo sabe que el objeto implementa la instancia de IFilterFactory, lo que significa que incluye un método getFilter() y distribuye un evento change ( Event.CHANGE ) cuando se modifica el filtro.

Cuando se cambian las propiedades de un filtro en su panel correspondiente, la instancia de controlador detecta que el filtro se ha modificado a través del evento change de la fábrica del filtro, evento que llama al método filterChange() de la instancia de controlador. Este método, a su vez, llama al método applyTemporaryFilter() :

private function filterChange(event:Event):void 
{ 
    applyTemporaryFilter(); 
} 
 
private function applyTemporaryFilter():void 
{ 
    var currentFilter:BitmapFilter = _filterFactory.getFilter(); 
     
    // Add the current filter to the set temporarily 
    _currentFilters.push(currentFilter); 
     
    // Refresh the filter set of the filter target 
    _currentTarget.filters = _currentFilters; 
     
    // Remove the current filter from the set 
    // (This doesn't remove it from the filter target, since  
    // the target uses a copy of the filters array internally.) 
    _currentFilters.pop(); 
}

La tarea de aplicar el filtro al objeto de visualización se produce en el método applyTemporaryFilter() . En primer lugar, el controlador recupera una referencia al objeto de filtro llamando al método getFilter() de la fábrica de filtro.

var currentFilter:BitmapFilter = _filterFactory.getFilter();

La instancia de controlador incluye una variable de instancia de Array, denominada _currentFilters , que es un conjunto en el cual almacena todos los filtros aplicados al objeto de visualización. El siguiente paso consiste en añadir el filtro recién actualizado a dicho conjunto:

_currentFilters.push(currentFilter);

A continuación, el código asigna el conjunto de filtros a la propiedad filters del objeto de visualización, la cual aplica en realidad los filtros a la imagen:

_currentTarget.filters = _currentFilters;

Por último, dado que este filtro recién añadido sigue siendo el filtro "de trabajo", conviene no aplicarlo de forma permanente al objeto de visualización, por lo que se elimina del conjunto _currentFilters :

_currentFilters.pop();

La eliminación del filtro no afecta al objeto de visualización filtrado, ya que este realiza una copia del conjunto de filtros cuando se asigna a la propiedad filters y utiliza este conjunto interno en lugar del original. Por este motivo, los cambios realizados en el conjunto de filtros no afectan al objeto de visualización hasta que dicho conjunto se vuelve a asignar a su propiedad filters .