## Cómo Escribir un Filtro de Imagen

Todos los filtros de imagen deben ser subclases de la clase ImageFilter. Si un filtro de imagen va a modificar los colores o la transparencia de una imagen, en vez de crear directamente una subclase de ImageFilter, probablemente deberías crear una subclase de RGBImageFilter.

Antes de escribir un filtro de imagen, deberías encontrar otros estudiando los que son similares al que planeas escribir. También deberás estudiar los interfaces ImageProducer e ImageConsumer, para familiarizarte con ellos.

### Encontrar ejemplos

> Podrás encontrar ejemplos de subclases de RGBImageFilter en los applets de las páginas mencionadas en la página anterior

> Más adelante en esta página veras un ejemplo de una subclase directa de ImageFilter, RotateFilter.

### Crear una subclase de ImageFilter

> Como se mecionó antes, los filtros de imagen inplementan el interface ImageConsumer. Esto permite interceptar los datos destinados al consumidor de imagen. ImageConsumer define los siguientes métodos:

>     void setDimensions(int width, int height);
>     void setProperties(Hashtable props);
>     void setColorModel(ColorModel model);
>     void setHints(int hintflags);
>     void setPixels(int x, int y, int w, int h, ColorModel model, byte pixels[], int off, int scansize);
>     void setPixels(int x, int y, int w, int h, ColorModel model, int pixels[], int off, int scansize);
>     void imageComplete(int status);

> La clase ImageFilter implementa todos los métodos anteriores para que reenvien los datos del método al consumidor del filtro. Por ejemplo, ImagenFilter implementa el método setDimensions() de la siguiente forma:

>     public void setDimensions(int width, int height) {
>         consumer.setDimensions(width, height);
>     }

> Gracias a estos métodos de ImagenFilter, tu subclase no necesitará implementar todos los métodos de ImageConsumer. Sólo necesitará implementar los métodos que transmitan los datos que quieres cambiar.

> Por ejemplo , la clase CropImageFilter implementa cuatro métodos de ImageConsumer: setDimensions(), setProperties(), y dos variedades de setPixels(). También implementa un constructor con argumentos que especifica el rectángulo a recortar. Cómo otro ejemplo, la clase RGBImageFilter implementa algunos métodos de ayuda, define un método de ayuda abstracto que realiza las modificaciones reales del color de cada pixel, e implementa los siguientes métodos de ImageConsumer: setColorModel() y dos variedades de setPixels().

> La mayoría, si no todos, los filtros implementan métodos setPixels(). Estos métodos determinan exactamente qué datos de la imagen se van a utilizar para construir la imagen. Uno o los dos métodos setPixels() podrían ser llamados varias veces durante la construcción de una sola imagen. Cada llamada le da información al ImageConsumer sobre un rectángulo de pixels dentro de la imagen. Cuando se llama al método imageComplete() del ImageConsumer con cualquier estado excepto SINGLEFRAMEDONE (lo que implica que aparecerán los datos para más marcos), entonces el ImageConsumer puede asumir que no va a recibir más llamadas de setPixels(). Un imageComplete() con estado de STATICIMAGEDONE especifica no sólo que se han recibido los datos completos de la imagen, sino que además no se ha detectado ningún error.

> La siguiente ilustración y la tabla describen los argumentos de los métodos setPixels().

> x, y
>   Especifica la posición dentro de la imagen, relativa a su esquina superior izquierda, en la que empieza el rectángulo.
> w, h
>   Especifica la anchura y altura, en pixels, de este rectángulo.
> model
>   Especifica el modelo de color utilizado por los datos en el array de pixels.
> pixels[]
>   Especifica un array de pixels. El rectángulo de los datos de la imagen está contenido en este array, pero el array debería contener más de w*h entradas, dependiendo de los valores de offset y scansize. Aquí tienes la fórmula para determina que entrada en el array pixels contiene los datos del pixel situado en (x+i, y+j), donde (0 <= i < w) y (0 <= j < h):

>       offset + (j * scansize) + i

>   La fórmula anterior asume que (m,n) está en el rectángulo especificado por esta llamada a setPixels(), y que (m,n) es relativo al origen de la imagen. Abajo tienes una ilustración del array pixels para aclararlo. Muestra cómo un pixels especifico (por ejemplo (x,y)) obtiene su entrada en el array.

> offset
>   Especifica el índice (en el array pixels) del primer pixel del rectángulo.
> scansize
>   Especifica la anchura de cada fila en el array pixels. Debido a consideraciones de eficiencia, este podría ser más grande que w.

### El Filtro de Imagen RotateFilter

> La clase RotateFilter rota una imagen el ángulo especificado. Se basa en las siguientes fórmulas gráficas para calcular la nueva posición de cada pixel:

>     newX = oldX*cos(angle) - oldY*sin(angle)
>     newY = oldX*sin(angle) + oldY*cos(angle)

> RotateFilter implementa los siguientes métodos de ImageConsumer:
> setDimensions()
>   Graba la anchura y altura de la imagen sin filtrar para utilizarlas en los métodos setPixels() y imageComplete(). Calcula la anchura y altura finales de la imagen filtrada, grabándolas para utilizarlas con el método imageComplete(), crea un buffer para almacenar los datos que entran de la imagen, y llama al método setDimensions() del consumidor para seleccionar la anchura y altura nuevas.
> setColorModel()
>   Le dice al consumidor que espere los pixels en el modelo del color RGB, por defecto.
> setHints()
>   Le dice al consumidor que espere los datos de la imagen en orden desde arriba a abajo y de izquierda a derecha (el orden en que estás leyendo esta página), en pasos de líneas completas, y que cada pixel es enviado exactamente una vez.
> setPixels() (las dos variedades de este método)
>   Convierten los pixels al modelo RBG por defecto (si es necesario) y copia el pixel en buffer de almacenamiento. La mayoría de los filtros de imagen simplemente modifican el pixel y lo envían al consumidor, pero como el lado de un rectángulo girado ya no es horizontal o vertical (para la mayoría de los ángulos), este filtro puede que no envie los pixels de forma eficiente desde su método setPixels(). En su lugar, RotateFilter almacena todos los datos de los pixels hasta que recibe un mensaje imageComplete().
> imageComplete()
>   Rota la imagen y luego llamada repetidamenten a consumer.setPixels() para enviar cada línea de la imagen al consumidor. Después de enviar toda la imagen, este método llama a consumer.imageComplete().

Ozito