web-dev-qa-db-fra.com

Comment dessiner un cercle décent dans Java

J'ai essayé d'utiliser la méthode drawOval avec une hauteur et une largeur égales, mais à mesure que le diamètre augmente, le cercle devient de moins en moins beau. Que puis-je faire pour avoir un cercle d'apparence décent, peu importe la taille. Comment pourrais-je implémenter l'anticrénelage dans Java ou une autre méthode.

24
soldier.moth

En fin de compte, Java2D (que je suppose être ce que vous utilisez) est déjà assez bon dans ce domaine! Il y a un tutoriel décent ici: http://www.javaworld.com/javaworld/jw-08-1998/jw-08-media.html

La ligne importante est:

graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                          RenderingHints.VALUE_ANTIALIAS_ON);
43
Brandon Yarbrough

vous pouvez définir des indices de rendu:

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

Deux choses qui peuvent aider:

  1. Utilisez Graphics2D.draw(Shape) avec une instance de Java.awt.geom.Ellipse2D au lieu de Graphics.drawOval
  2. Si le résultat n'est toujours pas satisfaisant, essayez d'utiliser Graphics2D.setRenderingHint pour activer l'anticrénelage

Exemple

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);
}

Voir la réponse de Josef pour un exemple de setRenderingHint

12
finnw

Bien sûr, vous définissez votre rayon sur ce dont vous avez besoin:

@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);
}
6
Clint

Merci à Oleg Estekhin d'avoir signalé le rapport de bogue, car il explique comment le faire.

Voici quelques petits cercles avant et après. Agrandie plusieurs fois pour voir la grille de pixels.

Circles before and after

En descendant d'une rangée, ils se déplacent légèrement de sous-pixels.

La première colonne est sans indice de rendu. Le second concerne uniquement les antialias. Le troisième est avec antialias et mode pur.

Notez comment avec les astuces anti-crénelage uniquement, les trois premiers cercles sont identiques et les deux derniers sont également identiques. Il semble y avoir une transition discrète. Arrondi probablement à un moment donné.

Voici le code. Il est en Jython pour la lisibilité, mais il pilote la bibliothèque d'exécution Java en dessous et peut être porté sans perte vers une source équivalente Java, avec exactement le même effet).

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"))

Résumé: vous avez besoin des deux VALUE_ANTIALIAS_ON et VALUE_STROKE_PURE pour obtenir des cercles d'aspect corrects avec une précision de sous-pixel.

4
Evgeni Sergeev

L'incapacité à dessiner un "cercle d'apparence décent" est liée au très ancien bug 6431487 .

L'activation de l'anticrénelage n'aide pas beaucoup - il suffit de vérifier le type de "cercle" produit par drawOval () ou drawShape (Eclipse) lorsque la taille de cercle requise est de 16 pixels (toujours assez courante pour la taille de l'icône) et que l'anticrénelage est activé. Les cercles anti-crénelés plus grands auront une meilleure apparence, mais ils sont toujours asymétriques, si quelqu'un veut bien les regarder de près.

Il semble que pour dessiner un "cercle décent", il faut en dessiner un manuellement. Sans anticrénelage, ce sera l'algorithme du cercle médian (cette question a une réponse avec un joli Java pour cela).

3
Oleg Estekhin