Comment dessiner un cercle décent en Java
j'ai essayé d'utiliser la méthode drawOval avec la même hauteur et la largeur, mais comme le diamètre augmente le cercle devient de plus en plus mauvais. Que puis-je faire pour avoir un joli cercle peu importe la taille. Comment implémenter l'anti-aliasing en java ou une autre méthode.
7 réponses
comme il s'avère, Java2D (qui je suppose est ce que vous utilisez) est déjà assez bon à cela! Il y a un bon tutoriel 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);
vous pouvez définir des indices de rendu:
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
deux choses qui peuvent aider:
- utiliser
Graphics2D.draw(Shape)
avec une instance dejava.awt.geom.Ellipse2D
au lieu deGraphics.drawOval
- si le résultat n'est toujours pas satisfaisant, essayez d'utiliser
Graphics2D.setRenderingHint
pour activer l'antialiasing
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
bien sûr, vous réglez 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);
}
merci à Oleg Estekhin pour avoir pointé le rapport de bug, car il explique comment le faire.
Voici quelques petits cercles avant et après. Agrandi plusieurs fois pour voir la grille des pixels.
en descendant d'affilée,ils se déplacent légèrement par sous-pixels.
la première colonne est sans indication de rendu. La deuxième est avec anticrénelage. Le troisième est avec antialias et mode pur.
Notez comment avec antialias conseils seulement, les trois premiers cercles sont les mêmes, et les deux derniers sont également les mêmes. Il semble y avoir une transition discrète. Probablement en train d'arrondir à un moment donné.
voici le code. Il est en Jython pour la lisibilité, mais il conduit la bibliothèque Java runtime en dessous et peut être porté sans perte vers une source Java équivalente, 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 à la fois VALUE_ANTIALIAS_ON
et VALUE_STROKE_PURE
pour obtenir des cercles d'apparence appropriés dessinés avec la précision de sous-pixel.
incapacité à dessiner un "cercle d'apparence convenable" est lié au très vieux bug 6431487 .
activer l'antialiasing n'aide pas beaucoup - il suffit de vérifier le genre de "cercle" produit par le drawOval() ou drawShape(Eclipse) lorsque la taille de cercle requise est de 16 pixels (encore assez commun pour la taille de l'icône) et l'antialiasing est activé. De plus grands cercles antialiasés auront l'air mieux mais ils sont encore asymétriques, si quelqu'un veut bien les regarder étroitement.
Il semble que de dessiner un "joli cercle" on doit tirer manuellement. Sans antialiasing, il sera midpoint circle algorithme (ce question a une réponse avec un joli code java pour elle).
MODIFIÉ: 06 septembre 2017
c'est un algorithme inventé par moi pour dessiner un cercle sur une matrice entière. La même idée pourrait être utilisée pour écrire un cercle à l'intérieur d'un tampon. Si vous essayez de dessiner ce cercle en utilisant les graphiques de classe ce N'est pas la réponse que vous recherchez (sauf si vous souhaitez modifier chaque couleur-assignation avec G. drawLine (x, y, x+1, y), but it could be very slow).
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;
}
j'ai essayé tellement de fois, vérifier manuellement l'exactitude, donc je pense que cela va fonctionner. Je n'ai pas vérifié la portée juste pour simplifier le code. J'espère que cela vous aidera et que tout le monde souhaite dessiner un cercle au-dessus d'une matrice (par exemple, ces programmeurs qui essaient de créer leurs propres jeux vidéo sur du code pur et ont besoin de gérer une matrix-oriented game-map pour stocker les objets se trouvant sur la game-map [si vous avez besoin d'aide sur ce, m'envoyer un e-mail]).