Tabla de Contenidos

Programación gráfica 2d en Java

Graphics

Imágenes

Ejemplos

BufferedImage image = ImageIO.read (new ByteArrayInputStream (rawImageBytes));
ByteArrayOutputStream baos = new ByteArrayOutputStream( 1000 );
ImageIO.write( aBufferedImage, "jpeg", baos );
baos.flush();
byte[] resultImageAsRawBytes = baos.toByteArray();
baos.close();
BufferedImage image = ImageIO.read( new File( "rabbit.jpg" ) );
ImageIO.write( aBufferedImage, "jpeg", new File ("snap.jpg"));
BufferedImage bufferedImage = new BufferedImage ( imageWidth,
                                                  imageHeight,
                                                  BufferedImage.TYPE_INT_BGR  );
bufferedImage.createGraphics().drawImage( image, 0, 0, this);
BufferedImage image = null;
try
   {
   image = ImageIO.read( url );
   }
catch ( IOException e )
   {
   System.out.println( "image missing" );
   }

Clase java.awt.Graphics

Métodos útiles:

java.awt.GraphicsEnvironment

Mediante esta clase podemos recopilar información del sistema de gráficos de la máquina donde se ejecuta el programa, para ello debemos coger el GraphicsEnvirontment local llamando al método getLocalGraphicsEnvirontment. Por ejemplo, para recoger las fuentes existentes en la máquina:

GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
	for (String s : ge.getAvailableFontFamilyNames())
		System.out.println(s);

DobleBuffer

Adición de un color Alpha a una imágen

Hola amigos!! Hoy vamos a asignar un color transparente para la imágen, para ello necesitamos:

  1. Un BufferImage
  2. Un Color el cual asignaremos como alpha
  3. Saber como coger el color de un pixel (<bufferimage>.getRGB (X, Y)) y como asignarlo (<bufferimage>.setRGB(x,y,color)).
  4. Ya'stá.

Escogeremos el color que será el alpha y recorreremos la imágen pixel a pixel, si ese pixel es el color en cuestión le diremos que no tiene opacidad:

	java.awt.Color colorAlpha = new Color(buffer.getRGB(0,0));
	for (int i=0; i<buffer.getHeight(); i++)
		for (int j=0; j<buffer.getWidth(); j++)
			if (new Color(buffer.getRGB(j, i)).equals(colorAlpha))
				buffer.setRGB(j,i,0&0&0&0);

Donde colorAlpha ha sido el color elegido (el que tiene el primer pixel de arriba a la izquierda) y buffer es la BufferImage.

Graphics2D

Propiedaddes del Graphics2D

Estilo de lápiz. Para usarla lo que hacemos es pasarle un objeto java.awt.BasicStroke. El primer parámetro de este es la anchura. Los siguientes son constantes estáticas de la clase BasicStroke, por ejemplo BasicStroke.CAP_ROUND redondearía los bordes. Un Stroke como el siguiente haría una línea discontinua:

	float dash[] = {10.0f, 5.0f};
	g2.setStroke (new BasicStroke (8.0f, BasicStroke.CAP_BUTT,
              		 BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f));

Estilo de relleno. Podemos usar distintos tipos de relleno, pasandole un objeto color nos crea un relleno plano, pero si le pasamos un objeto java.awt.GradientPaint podemos conseguir rellenos con degradados.

	java.awt.GradientPaint redtowhite = new java.awt.GradientPaint
		(0,0,Color.RED,300,300,Color.WHITE);
        	G.setPaint(redtowhite);

TexturePaint nos será útil para crear figuras rellenas con una BufferedImage como textura.

Estilo de composición de dibujos. Lo usamos para hacer operaciones con shapes ya dibujadas sobre el Graphics (unión, intersección…). Debemos usarla sobre el Graphics de un objeto BufferedImage.

Transformación de rotación, escala…

Restricción de dibujo. Una vez pasemos a este método del objeto Graphics una shape, todo lo dibujado quedará enmascarado por ese polígono.

Fuente que se usará.

Forma de renderizar.

setTransform y AffineTransform

El método setTransform aplica una transformación (escalado, traslación, rotación…) al objeto Graphics. Esta transformación se hace mediante el objeto AffineTransfrom, este tiene métodos para cada transformación permitida. Y una vez pasado al setTransform de un graphics todo lo que se dibuje sobre él a partir de entonces será transformado.
Si sólo queremos transformar una imágen tenemos dos formas de hacerlo:

  1. Dibujar la imágen con la transformación ya aplicada. Existe un método sobreescrito del drawImage al que le pasas: imágen, transformación y el lugar donde se muestra, si llamamos a este la imágen nos aparecerá transformada. Hemos de pensar que se dibujará en el punto 0,0, para moverla hemos de trasladarla
  2. Aplicar la transformación al Graphics y dibujar la imágen, luego resetear la transformación y la volvemos a aplicar al Graphics (¿? no se pasan los objetos por referencia?). Para resetearla únicamente hemos de llamar a su método setToIdentity.

Para hacer un espejo horizontal haremos un: scale(-1,1). Al método de escalado se le pasan dos doubles donde 1,1 es el tamaño actual, 0.5,0.5 la mitad… Si la aplicamos a todo el Graphics este se nos girará y probablemente no veamos parte de la pantalla.
Otra forma de aplicar las transformaciones es: nos quedamos con la transformación actual, creamos la nuestra y aplicamos nuestras acciones. Dibujamos. Y volvemos a aplicar la inicial:

	AffineTransform origAT = g2d.getTransform( );
	AffineTransform rot = new AffineTransform( );
	rot.rotate( Math.toRadians(angle), img.getWidth( )/2, img.getHeight( )/2);
	g2d.transform(rot);
	g2d.drawImage(src, 0, 0, null);
	g2d.setTransform(origAT); 

java.awt.geom

En esta clase encontramos métodos para dibujar elementos geométricos de forma avanzada. (Curvas, Puntos, Rectángulos (hasta con punta redondeada!! (RoundRectangle2D)), elipses…). Para dibujarlas llamamos al método draw del objeto Graphics2D, a él se le pasa una Shape (un objeto de los anteriores). Tengamos en cuenta que generalmente para hacer una nueva línea, rectángulo…. NO usaremos la clase Line2D Rectangle2D, sino una clase interna “Double” o “Float”: G.draw(new Line2D.Double(new Point2D.Double(0, 0), new Point2D.Double(400, 300)));

		Rectangle rect1 = new Rectangle(20,50,100,100);
		Area a = new Area(rect1);
		Rectangle rect2 = new Rectangle(90,90,100,100);
		Area b = new Area(rect2);
		b.intersect(a);
		G.fill(b);

Anti-Alising en el texto

Podemos configurar el objeto de Graphics 2d para que al dibujo de texto no sea pixelado, sino suavizado:

	... (Graphics g)
	if (g instanceof Graphics2D) {
            	Graphics2D g2 = (Graphics2D)g;
	            g2.setRenderingHint(
            	    RenderingHints.KEY_TEXT_ANTIALIASING,
	                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
	}
	g.drawString("Hola", 50, 100);

Medir un String

Para medir un texto usaremos la clase java.awt.FontMetrics, no podremos crear un objeto de esta clase mediante un constructor (ya que son protegidos), pero sí que podremos pedir a un objeto Graphics que nos devuelva el FontMetrics que tiene asociado, mediante el método getFontMetrics, y usarlo. Para que coja un string usaremos el método stringWidth, que ya te devuelve el ancho, y una vez con un string cogido podemos pedir el alto: getHeight.
El código siguiente te centra un String (str) en el frame:

		FontMetrics met = G.getFontMetrics();
		int w = met.stringWidth(str);
		int h = met.getHeight();
		int xFin = (this.getWidth() / 2) - (w / 2);
		int yFin = (this.getHeight() / 2) - (h / 2);
		G.drawString(str, xFin,yFin);

Hemos de recordar que si cogemos el FontMetrics y luego cambiamos la fuente el FontMetrics del Graphics cambia pero el nuestro no (no debería ser una referencia???? ¿?¿?), por lo que lo tendríamos que volver a recoger.

Creación de Sprites

Llegados a un punto, querremos hacer una pequeña animación, para ello podremos tener varias imágenes y las iríamos mostrando de una en una. Pero tal vez queramos organizarlo de otra forma y montar todas las imágenes en una y, cuando vayamos iniciemos el programa, este leerá la imágen grande y la convertirá en pequeñas. Para ello haremos lo siguiente:

  1. Crearemos un BufferedImage de la imágen grande.
  2. Calcularemos cuanto será el tamaño de cada imágen pequeña, para ello haremos el ancho de la imágen (getWidth()) dividido por el número de imágenes pequeñas que caben en horizontal (y tendremos el ancho de las pequeñas). También dividiremos el alto de la grande por el número de imágenes que hay en vertical (y ya tenemos el alto).
  3. Ahora crearemos la estructura donde guardaremos las imágenes pequeñas, si escogemos un array este deberá ser de tamaño “número de figuras en horizontal” * “número de figuras en vertical”. Yo escojo un array de BufferImage.
  4. Iremos inicializando cada elemento del array.
  5. Dibujaremos en cada BufferImage del array la porción de imágen que le toque, para ello cogeremos su Graphics y sobre él haremos un drawImage. Usaremos el método al cual se le pasan como parámetros: 1) Una imágen, 2) par de ints correspondientes al punto donde se empieza a dibujar, 3) par de ints correspondientes al tamaño, 4) par de ints donde se empieza a coger de la imágen destino, 5) par de ints hasta donde se cogen de la imágen destino, 6) el ImageObserver.

El código siguiente llena el array de ImageBuffer (strip) que lee del BufferImagen orígen (img). Sabemos que una imágen pequeña tiene tamaño width\height. La imágen orígen son 6 imágenes (numImgs) pequeñas puestas en horizontal, sólo hay una fila de sprites:

for (int i=0; i < numImgs; i++) {
	strip[i] = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
	Graphics g = strip[i].getGraphics();
	g.drawImage(img, 0,0, width, height, width * i, 0, (width * i)+width, height, null);
	g.dispose();
}