Cómo dibujar un Círculo de aspecto decente en Java

He intentado usar el método drawOval con la misma altura y ancho, pero a medida que aumenta el diámetro, el círculo se vuelve peor. ¿Qué puedo hacer para tener un círculo de aspecto decente sin importar el tamaño? ¿Cómo implementaría anti-aliasing en Java o algún otro método?

Resulta que Java2D (que asumo es lo que estás usando) ya es bastante bueno en esto. Hay un tutorial decente aquí: http://www.javaworld.com/javaworld/jw-08-1998/jw-08-media.html

La línea importante es:

graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 

puedes establecer sugerencias de renderizado:

 Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 

Dos cosas que pueden ayudar:

  1. Utilice Graphics2D.draw(Shape) con una instancia de java.awt.geom.Ellipse2D lugar de Graphics.drawOval
  2. Si el resultado aún no es satisfactorio, intente utilizar Graphics2D.setRenderingHint para habilitar el antialiasing

Ejemplo

 public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g; Shape theCircle = new Ellipse2D.Double(centerX - radius, centerY - radius, 2.0 * radius, 2.0 * radius); g2d.draw(theCircle); } 

Ver la respuesta de Josef para un ejemplo de setRenderingHint

Por supuesto, establece su radio a lo que necesite:

 @Override public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); Ellipse2D.Double hole = new Ellipse2D.Double(); hole.width = 28; hole.height = 28; hole.x = 14; hole.y = 14; g2d.draw(hole); } 

Gracias a Oleg Estekhin por señalar el informe de errores, porque explica cómo hacerlo.

Aquí hay algunos círculos pequeños antes y después. Magnificado algunas veces para ver la cuadrícula de píxeles.

Círculos antes y después

Al avanzar una fila, se mueven ligeramente por cantidades de subpixel.

La primera columna es sin dar pistas. El segundo es solo con antialias. El tercero es con antialias y modo puro.

Tenga en cuenta que solo con sugerencias antialias, los primeros tres círculos son iguales y los dos últimos también son iguales. Parece que está ocurriendo una transición discreta. Probablemente redondeando en algún punto.

Aquí está el código. Está en Jython para legibilidad, pero controla la biblioteca de tiempo de ejecución de Java y puede ser portado sin pérdidas a una fuente Java equivalente, con exactamente el mismo efecto.

 from java.lang import * from java.io import * from java.awt import * from java.awt.geom import * from java.awt.image import * from javax.imageio import * bim = BufferedImage(30, 42, BufferedImage.TYPE_INT_ARGB) g = bim.createGraphics() g.fillRect(0, 0, 100, 100) g.setColor(Color.BLACK) for i in range(5): g.draw(Ellipse2D.Double(2+0.2*i, 2+8.2*i, 5, 5)) g.setRenderingHint( RenderingHints. KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) for i in range(5): g.draw(Ellipse2D.Double(12+0.2*i, 2+8.2*i, 5, 5)) g.setRenderingHint( RenderingHints. KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE) for i in range(5): g.draw(Ellipse2D.Double(22+0.2*i, 2+8.2*i, 5, 5)) #You'll probably want this too later on: #g.setRenderingHint( RenderingHints. KEY_INTERPOLATION, # RenderingHints.VALUE_INTERPOLATION_BICUBIC) #g.setRenderingHint( RenderingHints. KEY_RENDERING, # RenderingHints.VALUE_RENDER_QUALITY) ImageIO.write(bim, "PNG", File("test.png")) 

Resumen: necesita VALUE_ANTIALIAS_ON y VALUE_STROKE_PURE para obtener círculos de aspecto adecuados dibujados con precisión subpixel.

La incapacidad de dibujar un “círculo de aspecto decente” se relaciona con el viejo error 6431487 .

Activar el antialiasing no ayuda mucho, solo verifique el tipo de “círculo” producido por drawOval () o drawShape (Eclipse) cuando el tamaño del círculo requerido es de 16 píxeles (sigue siendo bastante común para el tamaño del icono) y antialiasing está activado. Los círculos antialias más grandes se verán mejor, pero siguen siendo asimétricos, si alguien se preocupa por mirarlos de cerca.

Parece que para dibujar un “círculo de aspecto decente” uno tiene que dibujar uno manualmente. Sin antialiasing será un algoritmo de círculo medio (esta pregunta tiene una respuesta con un bonito código Java).

EDITADO: 06 de septiembre de 2017

Ese es un algoritmo inventado por mí para dibujar un círculo sobre una matriz entera. La misma idea podría usarse para escribir un círculo dentro de una Imagen Buffered. Si está tratando de dibujar ese círculo con la clase Graphics, este no es el answare que está buscando (a menos que desee modificar cada asignación de color con g.drawLine (x, y, x + 1, y), pero podría ser muy lento).

 protected boolean runOnCircumference(int[][] matrix, int x, int y, int ray, int color) { boolean ret; int[] rowUpper = null, rowInferior = null, rowCenterUpper = null, rowCenterInferior = null; if (ret = ray > 0) { if (ray == 1) { matrix[y][x + 1] = color; rowUpper = matrix[++y]; rowUpper[x] = color; rowUpper[x + 2] = color; matrix[y][x] = color; } else { double rRay = ray + 0.5; int r = 0, c = 0, ray2 = ray << 1, ray_1 = ray - 1, halfRay = (ray >> 1) + ray % 2, rInf, ray1 = ray + 1, horizontalSymmetricOldC; // draw cardinal points rowUpper = matrix[ray + y]; rowUpper[x] = color; rowUpper[x + ray2] = color; matrix[y][x + ray] = color; matrix[ray2 + y][x + ray] = color; horizontalSymmetricOldC = ray1; rInf = ray2; c = ray_1; for (r = 0; r < halfRay; r++, rInf--) { rowUpper = matrix[r + y]; rowInferior = matrix[rInf + y]; while (c > 0 && (Math.hypot(ray - c, (ray - r)) < rRay)) { rowUpper[x + c] = color; rowUpper[x + horizontalSymmetricOldC] = color; rowInferior[x + c] = color; rowInferior[x + horizontalSymmetricOldC] = color; // get the row pointer to optimize rowCenterUpper = matrix[c + y]; rowCenterInferior = matrix[horizontalSymmetricOldC + y]; // draw rowCenterUpper[x + r] = color; rowCenterUpper[x + rInf] = color; rowCenterInferior[x + r] = color; rowCenterInferior[x + rInf] = color; horizontalSymmetricOldC++; c--; } } // end r circle } } return ret; } 

Lo intenté muchas veces, verificando manualmente su corrección, así que creo que funcionará. No hice ningún control de rango solo para simplificar el código. Espero que te ayude a ti y a todos a dibujar un círculo sobre una matriz (por ejemplo, esos progtwigdores que intentan crear sus propios videojuegos en código puro y necesitan administrar un mapa de juego orientado a la matriz para almacenar los objetos que se encuentran en el juego-mapa [si necesita ayuda con esto, envíeme un correo electrónico]).