En computación, Java 2D es un API para dibujar gráficos en dos dimensiones usando el lenguaje de programación Java. Cada operación de dibujo Java 2Dpuede tratarse como rellenar una forma usando un pincel y componiendo el resultado en la pantalla.
El API 2D de Java mejora las capacidades de gráficos, texto e imágenes de la Abstract Windowing Toolkit (AWT), haciendo posible el desarrollo de interfaces de usuario mejoradas y nuevos tipos de aplicaciones Java. Además de sus mejoras en gráficos, letra e imágenes, el API 2D de Java soporta mejoras para la definición y composición del color, además de la detección de formas y texto en formas geométricas arbitrarias y un modelo de dibujado (rendering) para impresoras y dispositivos de visualización.
El API 2D de Java también hace posible la creación de librerías gráficas avanzadas, tales como librerías de CAD-CAM y de gráficos o librerías de efectos especiales para imágenes, así como la creación de imágenes y de filtros para archivos gráficos.
Cuando se usa en conjunto con al Java Media Framework y otras APIs de Java Media, el API 2D de Java puede utilizarse para crear y visualizar animaciones y otras presentaciones multimedia. Los APIs de Java Animation y Java Media Framework le proporcionan al API 2D de Java el soporte para el renderizado.
Conceptos básicos
Formas: Una forma en Java 2D es un límite infinitamente delgado el cual define un interior y un exterior. Los Píxeles internos de la forma están afectados por la operación de dibujo, los que están fuera no.
Intentar rellenar un segmento de línea recto extremadamente fino resultará en que no habrá píxeles afectados, por lo tanto una forma no contiene píxeles. En su lugar, un rectángulo fino se debe usar para que la forma contenga algunos píxeles.
Pinceles: Un pincel genera los colores que serán usados para cada píxel de la operación de relleno. El pincel más simple es java.awt.Color, el cual genera el mismo color para todos los píxeles. Pinceles más complicados pueden producir gradientes, imágenes, o cualquier combinación de colores. Rellenar una forma circular usando el color amarillo resulta en un círculo sólido amarillo, mientras rellenar la misma forma circular usando un pincel que genera una imagen produce un recorte circular de la imagen.
Compuestos: Durante cualquier operación de dibujo, hay una fuente (los píxeles que son producidos por el pincel) y un destino (los píxeles ya en la pantalla). Normalmente, los píxeles fuente simplemente sobrescriben los píxeles de destino, pero el compuesto permite modificar este comportamiento.
El compuesto, dados los píxeles fuente y destino, produce el resultado final que por último aparece en la pantalla. El compuesto más común esjava.awt.AlphaComposite, el cual trata los píxeles que están siendo dibujados como parcialmente transparentes, para que los píxeles destino se muestren en algún grado.
Rellenado: Para rellenar una forma, el primer paso es identificar que píxeles caen dentro de la forma. Estos píxeles serán afectados por la operación de relleno. Los píxeles que están parcialmente dentro y parcialmente fuera de la forma pueden ser afectados en menor grado si está activado el Anti-aliasing.
El pincel es requerido para generar un color de cada uno de los píxeles que se van a pintar. En el caso común del relleno de un color sólido, cada píxel será asignado al mismo color.
El compuesto toma los píxeles generados por el pincel y los combina con los píxeles que ya están en la pantalla para producir el resultado final.
Métodos de renderizado de Graphics2D
Graphics2D proporciona los siguientes métodos generales de dibujado que pueden usarse para dibujar cualquier primitivo geométrico, texto o imagen.
draw
|
Dibuja el exterior de una forma geométrica primitiva usando los atributos stroke y paint.
|
fill
|
Dibuja cualquier forma geométrica primitiva rellenado su interior con el color o patrón especificado por el atributo paint.
|
drawString
|
Dibuja cualquier cadena de texto. El atributo font se usa para convertir la fuente a Glyphs que luego se rellenan con el color o patrón especificados por el atributo paint.
|
drawImage
|
Dibuja la imagen especificada.
|
Además, Graphics2D soporta los métodos de renderizado de Graphics para formas particulares, como drawOval y fillRect.
Atributos de pincel
Los atributos del pincel (que pertenecen a la interfaz Stroke) definen las características del trazo dibujado por el lápiz o pincel en la imagen. Con BasicStroke pueden definirse características tales como el ancho de línea, la forma en que acaba un trazo o el estilo con que se unen varios segmentos en un dibujo.
Atributos de relleno
Los atributos de relleno del contexto de un Graphics2D están representados por un objeto Paint. Cuando una figura o un glyph (un glyph es, tipo gráficamente hablando, el rasgo de un signo o una letra según una fuente de texto; en otras palabras, la forma de una letra concreta a partir de su tipo, tamaño y peculiaridades: negrita, cursiva, fileteada, etc. La impresión de una secuencia de rasgos tipo gráficos produce una cadena de texto, y cada uno de estos rasgos, como se verá más adelante, son tratados como un objeto Shape más) el objeto Paint se aplica a todos los píxeles que quedan dentro de la figura en sí, y que representa el contorno del objeto dibujado por el pincel. Al rellenar el interior de una figura, el objeto Paint se encarga de gestionar todos los píxeles de la figura, a excepción de los del contorno.
Transformaciones
El contexto de un objeto Graphics2D contiene una transformación que se usa al reubicar objetos desde el espacio de usuario (user space) al espacio del dispositivo (device space) durante el renderizado. Graphics2D proporciona varios métodos que permiten modificar la transformación por defecto en su contexto. Lo más sencillo es llamar a uno de los métodos de transformación de Graphics2D como rotate(), scale(), shear() o translate(): sólo es necesario especificar para cada una de ellos las características de la transformación requerida y Graphics2Dautomáticamente hará los cambios pertinentes en el momento de hacer el dibujo.
Es más, también es posible concatenar transformaciones mediante el uso de un objeto de la clase AffineTransform, el cual puede realizar transformaciones en secuencia, tales como una rotación seguida de un cambio de escala. Cuando una transformación se concatena con otra existente, la última que se especificó es la primera en ser aplicada. Graphics2D contiene también un método setTransform (), que sobrescribe las transformaciones a aplicar a los objetos que se vayan a dibujar pero que, ojo, no es posible usar para realizar concatenación de transformaciones
Métodos de composición
Cuando dos objetos se superponen en un mismo dibujo (ya sean figuras, rasgos tipográficos o imágenes) es necesario determinar qué colores renderiza en los píxeles superpuestos: este proceso se denomina composición. Las interfaces básicas de composición de Java2D son Composite and CompositeContext. Por ejemplo, para especificar el estilo de composición que debe usarse puede establecerse un objeto de la clase AlphaComposite en el contexto de un Graphics2D llamando a su métodosetComposite (). Las instancias de la clase AlphaComposite establecen una reglade composición que describe la manera de mezclar un nuevo color con otro ya existente definiendo, por ejemplo, transparencias
Con este objetivo, para manejar transparencias, se dispone de un valor alfa adicional al crear un objeto AlphaComposite. Este valor alfa (asociado al llamado “canal alfa» de una imagen), incrementa o decremento el canal de transparencia del objeto según el valor que tome: si el valor de alfa es 1,0 el color será opaco, mientras que el color será totalmente transparente si vale 0,0. Por supuesto, los valores intermedios especifican transparencias intermedias del color. Así pues, el canal alfase usa en conjunto con un objeto de la clase Composite en el contexto de unGraphics2D para mezclar la imagen con los dibujos ya existentes.
Podemos dibujar con Graphics 2D
Figuras geométricas
Java2D provee varias clases, pertenecientes a los paquetes java.awt y java.awt.geom, que definen figuras geométricas simples, tales como puntos, líneas, curvas y rectángulos. Usando las clases geométricas, es posible definir y manipular virtualmente cualquier objeto bidimensional de una manera sencilla. Las clases e interfaces a utilizar para este fin son las siguientes:
- Interfaces:
1. PathIterator: define métodos para iterar sobre los distintos segmentos o subtrazos que conforman el contorno de una figura o rasgo tipográfico.
2. Shape: proporciona un conjunto básico de métodos para describir y generar contornos de objetos geométricos. Es implementada por GeneralPath y una multitud de clases geométricas.
- Clases:
1. Área: representa una área geométrica (con la forma que sea) que soporta operaciones de intersección, unión, etc. para obtener nuevas áreas con formas diferentes.
2. FlatteningPathIterator: se ha comentado que PathIterator proporciona los subtrazos de un contorno; estos subtrazos pueden ser segmentos o curvas de escasa complejidad. La clase FlatteningPathIterator es igual a PathIterator pero siempre devuelve segmentos, de ahí lo de flattening (aplanar en español).
3. GeneralPath: implementa a Shape. Representa el trazo de un objeto geométrico construido a partir de líneas y curvas cuadráticas y cúbicas (curvas que pueden expresarse matemáticamente con escasa complejidad).
4. RectángularShape: proporciona la base de un gran número de figuras (Shape) que se dibujan enmarcadas en un rectángulo.
5. Figuras en sí:
- Arc2D: representa un arco.
- CubicCurve2D: representa un segmento curvo.
- Ellipse2D: representa una elipse, y extiende a la clase RectángularShape
- Line2D: representa un segmento de línea, e implementa a Shape.
- Point2D: es un punto que representa un localización en un sistema de coordenadas
- QuadCurve2D: representa un segmento de curva cuadrática, e implementa a
- Shape Rectangle2D: representa un rectángulo, y extiende a RectángularShape
- RoundRectangle2D: representa un rectángulo con los vértices redondeados, y extiende también a RectángularShape. Las medidas de todas estas figuras pueden especificarse tanto en formato double como float; para ello sólo es necesario añadir la extensión .Double o .Float al nombre del constructor de la figura que se quiere crear.
Fuentes y diseño de texto
Antes de entrar de lleno en cómo dibujar texto en un objeto Graphics2D es necesario conocer algunos conceptos básicos relativos a la fuentes tipográficas y al diseño que éstas adoptan para ser impresas.
Con respecto a las fuentes, la clase Font es la que soporta la especificación de los tipos de letra y permite el uso de complicados rasgos tipográficos. Un objeto de la clase Font representa una instancia de un tipo de fuente de entre todas las que hay instaladas en el ordenador. Una fuente tiene tres nombres: • Su nombre lógico, el cual es un nombre que esta mapeado en una de las fuentes específicas de la plataforma y se usa en versiones antiguas del JDK, • Su nombre de familia, que es el nombre de la familia a la que pertenece la fuente, con la que comparte rasgos tipográficos comunes, tales como la familia Times New Roman. • El nombre real de la fuente, el cual se refiere a una fuente real instalada en el sistema.
Es posible acceder a toda la información acerca de un objeto Font a través de su método getAttributes().
Por otro lado, un objeto de la clase LineMetrics es el encargado de contener toda la información acerca de las medidas de una fuente. Esta información se utiliza para posicionar correctamente los caracteres a lo largo de la línea.
Con respecto a los conceptos de diseño, podemos decir que antes de que un trozo de texto se muestre, se le debe dar forma y posicionar en su sitio cada uno de los rasgos tipográficos utilizando el espacio interlínea y entre caracteres apropiado. Esto es lo que se llama el diseño del texto, que engloba los siguientes pasos:
1. Dar forma al texto mediante los rasgos tipográficos y los espaciados apropiados. Un rasgo tipográfico es la representación visual de uno o más caracteres según una fuente concreta. La forma, el tamaño y la posición del rasgo dependen de su contexto. Para representar un mismo carácter se pueden usar muchos rasgos distintos e incluso combinaciones de ellos, dependiendo de la fuente y del estilo. En algunos contextos, dos rasgos pueden cambiar su forma y fusionarse en un único rasgo simple, lo que forma una ligadura: esto sucedía antiguamente, por ejemplo, en las máquinas de escribir en las que para representar una vocal acentuada eran necesarios dos rasgos: el acento por sí sólo y la vocal después.
2. Ordenar el texto. Hay dos tipos de orden, el orden lógico, que es aquél en el que las secuencias de caracteres se almacenan en memoria y el orden visual, que se corresponde con el orden en que los rasgos tipográficos se muestran para ser leídos por una persona. A este orden también se le llama orden de escritura (script order en inglés). Estas dos ordenaciones no tienen porqué coincidir. Por ejemplo, el orden de escritura de la fuente Arial es de izquierda a derecha, mientras que el de la Arabic es de derecha a izquierda (ya que el árabe se lee de derecha a izquierda). El orden visual debe mantenerse con sentido incluso cuando se mezclan idiomas en un mismo trozo de texto dando lugar a un texto multidireccional: cuando en un texto se mezclan textos que se leen en un sentido con textos que se leen en otro toma especial relevancia lo que se denomina la dirección base. La dirección base es el sentido de escritura del sistema de escritura predominante (como por ejemplo un texto en español con caracteres árabes).
3. Medir y posicionar el texto. A menos que se trabaje con una fuente de ancho constante, cada caracteres posee su propio ancho. Esto significa que todo el posicionamiento y medida del texto tiene que valorarse de manera que se mire qué caracteres se han usado, no cuántos. Para adecuar la posición en que se debe renderizar cada rasgo o letra, es necesario guardar información relativa a cada carácter individual, la fuente utilizada y el estilo en uso. Por suerte, la clase TextLayout se encarga automáticamente de todo ello.
Si solamente se desea mostrar un bloque de texto en una ventana o incluso editarlo, puede emplearse un objeto de tipo JTextComponent, que realizará el diseño del texto por nosotros. Si lo que se quiere es dibujar una cadena de caracteres, se debe invocar a al método drawString() de la clase Graphics2D y dejar que Java2D haga el diseño él solo. También puede usarse drawString() para renderizar cadenas personalizadas o que contengan texto bidireccional.
Tratamiento de imágenes
Las clases e interfaces para tratamiento de imágenes en Java2D proporcionan técnicas para manejar imágenes pixeladas (formadas por una matriz de puntos a cada uno de los cuales se denomina pixel) cuyos datos están almacenados en memoria. En general, Java2D permite acceder y trabajar con imágenes almacenadas según una amplia gama de formatos: PNG (Portable Network Graphics), JPEG (Joint Photographic Experts Group), GIF, (Graphics Interchange Format), etc. además de poder manipular los datos de una imagen a través de varios tipos de operaciones de filtro. El modelo de tratamiento de imágenes soporta imágenes almacenadas en memoria con resoluciones fijas, siendo posible obtener clones de una imagen a diferentes escalas.
La clase BufferedImage proporciona el tratamiento general de la imagen. Es posible crear un objeto BufferedImage directamente en memoria y luego usarlo para contener y manipular datos de una imagen obtenidos desde un fichero o una dirección URL (Uniform Resource Locator). Un objeto BufferedImage puede mostrarse utilizando como destino de visualización a un objeto Graphics2D, o puede renderizarse en algún otro destino usando apropiadamente el contexto de Graphics2D. La clase BufferedImage contiene básicamente dos objetos, uno de clase Raster y otro de clase ColorModel.
La clase Raster proporciona el manejo primitivo de los datos de la imagen: Representa las dimensiones rectangulares de la imagen, mantiene los datos de la imagen en memoria y nos dota de un mecanismo para crear varias subimágenes a partir de un sólo buffer de datos, además de proporcionar métodos de acceso a píxeles específicos de la imagen. Un objeto Raster contiene, a su vez, a otros dos objetos: un DataBuffer y un SampleModel. La clase DataBuffer contiene datos de píxeles en memoria, mientras que la clase SampleModel interpreta los datos del buffer y los devuelve o bien como píxeles individuales, o bien como trozos rectangulares de píxeles.
La clase ColorModel permite interpretar el color asociado a los píxeles proporcionados por el SampleModel de la imagen (SampleModel permite interpretar los datos asociados a cada pixel y convertirlos, por ejemplo, en un color concreto). Así, se denomina muestra a cada uno de los distintos datos que representan a un píxel en una imagen en el momento en que ésta se codifica computacionalmente. Por ejemplo, un píxel en un sistema de colores RGB está formado por tres muestras: una para el rojo, otra para el verde y otra para el azul.
El paquete java.awt.image proporciona otras muchas clases que definen, por ejemplo, operaciones de filtrado en un objeto BufferedImage o Raster. Cada operación de procesado de imagen se encuentra inmersa en una clase que implementa la interfaz BufferedImageOp, la interfaz RasterOp o ambas a la vez, y posee un cierto grado de adaptación en el sentido de que puede afinarse su funcionamiento en base a parámetros.
Las operaciones soportadas son las siguientes: 1. Transformaciones personalizadas. 2. Cambios de escala. 3. Modificaciones independientes a cada banda de color. Una banda es el conjunto de todas las muestras de un mismo tipo en una imagen, como
Tratamiento del color
En Java2D, las clases principales relacionadas con el tratamiento del color son ColorSpace, Color y ColorModel. ColorSpace representa un sistema cualquiera para poder medir los colores; por norma general se usan tres valores o componentes numéricos distintos. Un objeto de tipo Color es un color fijo, definido en términos de sus componentes en base a un ColorSpace particular. Por último, la clase ColorModel describe un modo particular de representación interna de las muestras que describen cada píxel: mediante 4 bytes, con un entero de 32 bits, etc. A continuación, vamos a aclarar un poco estas clases y los conceptos que representan.
La clase ColorModel se usa para interpretar las muestras de un píxel de una imagen y convertirlas en un color. Esto incluye mapear cada muestra de cada banda de una imagen en componentes de un sistema particular de colores. Esta clase puede gestionar el valor de un píxel de dos formas diferentes: mediante una muestra por cada banda, o mediante un único entero de 32 bits; en este último caso debe encargarse de desempaquetar los componentes codificados por dicho entero.
Para determinar el color de un píxel particular en una imagen, es necesario saber qué información de color esta codificada en cada píxel. El objeto ColorModel asociado con una imagen encapsula los métodos necesarios para traducir un píxel hacia las muestras de color que lo componen y viceversa. La API Java2D proporciona varios modelos de color predefinidos: 1. IndexColorModel. Trabaja con el espacio RGB con cada muestra representada mediante un entero de 8 bits. Opcionalmente puede usarse un valor para el canal alfa. 2. ComponentColorModel. Parecido al anterior pero puede trabajar con cualquier ColorSpace de forma que en lugar de usar cuatro muestras usa un array de muestras acorde con el ColorSpace que sea. Esta clase puede usarse para representar la mayoría de los modelos de colores propios de cualquier dispositivo gráfico. 3. PackedColorModel. Es una clase base abstracta para modelos que representan cada píxel mediante un único valor entero de 32 bits. Estos bits deben trocearse para extraer la información asociada a cada muestra, por lo que cualquier clase que herede de PackedColorModel debe establecer los algoritmos que describen cómo se extraen las componentes del color y del canal alfa. 4. DirectColorModel. Hereda de PackedColorModel y trabaja con el espacio RGB, por lo que es similar a IndexColorModel pero sin diferenciar directamente entre cada componente o muestra.
Un objeto ColorSpace representa un sistema de cuantificación de colores usando normalmente tres valores numéricos distintos: por ejemplo RGB (Red-Green- Blue en inglés) es el sistema de colores más extendido, pero no el único. Un objeto ColorSpace identifica el sistema de colores de un objeto Color o, a través de un objeto ColorModel, de una imagen entera representada por un objeto Image, BufferedImage o GraphicsConfiguration (ésta última es una clase que describe las características de un dispositivo capaz de procesar gráficos, como un monitor o una impresora. Un mismo dispositivo físico puede tener asociadas distintas configuraciones, y por ende distintos objetos GraphicsConfiguration, utilizadas para representar por ejemplo a diferentes resoluciones).
La clase Color proporciona la descripción de un color concreto mediante un sistema de colores particular. Una instancia de Color contiene el valor de las componentes de color, así como una referencia a un objeto ColorSpace. Así, cuando se crea un objeto de tipo Color puede pasarse como parámetro del constructor, además de las componentes de color, un objeto ColorSpace, lo que permite que la clase Color puede manejar cualquier color en cualquier sistema de colores.
No hay comentarios:
Publicar un comentario