2009-07-07 10 views
22

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 plus mauvais. Que puis-je faire pour avoir un cercle d'apparence décente, peu importe la taille. Comment pourrais-je implémenter l'anti-aliasing dans java ou une autre méthode.Comment dessiner un cercle d'apparence décente en Java

Répondre

37

Comme il se trouve, Java2D (que je Je suppose que c'est ce que vous utilisez) est déjà très bon à cela! Il y a un tutoriel décent ici: http://www.javaworld.com/javaworld/jw-08-1998/jw-08-media.html

La ligne est importante:

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

Vous devez aimer les liens 11 ans, j'espère qu'ils ne mourront jamais. –

+16

Il semble que les constantes aient été déplacées de Graphics2D à RenderingHints. Voir [cet exemple] (http://www.java2s.com/Code/JavaAPI/java.awt/Graphics2DsetRenderingHintKeyhintKeyObjecthintValue.htm). –

+1

Il a 17 ans maintenant ... toujours en vie xD lol – maganap

27

vous pouvez définir des notes de rendu:

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

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 est toujours pas satisfaisante, essayez d'utiliser Graphics2D.setRenderingHint pour activer l'anti-cré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

6

Bien sûr, vous définissez votre rayon à ce que jamais 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); 
} 
+0

Vous devez également définir la position de l'ellipse, et je pense que vous voulez g2d.draw pas g2d.stroke – finnw

3

pour dessiner un Incapacité « cercle décent d'apparence » est lié au bogue très ancien 6431487. Activer l'antialiasing 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 (encore assez commun pour la taille de l'icône) et anti-crénelage est sur. Les cercles anti-alias plus grands seront plus beaux, mais ils sont encore asymétriques, si quelqu'un veut les regarder de près.

Il semble que pour dessiner un «cercle d'apparence correcte», il faut en dessiner un manuellement. Sans antialiasing, il s'agira d'un algorithme de cercle médian (cette question a une réponse avec un joli code java).

-1

ÉDITÉE: 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 dans un BufferedImage. Si vous essayez de dessiner ce cercle en utilisant la classe Graphics, ce n'est pas le serveur d'espionnage que vous recherchez (sauf si vous souhaitez modifier chaque attribution de couleur avec g.drawLine (x, y, x + 1, y), mais pourrait être très lent).

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

Je l'ai essayé tellement de fois, en vérifiant manuellement la correction, donc je pense que ça va marcher. Je n'ai fait aucune vérification de gamme pour simplifier le code. J'espère que cela vous aidera et que tout le monde voudra dessiner un cercle autour d'une matrice (par exemple, les programmeurs qui essaient de créer leurs propres jeux vidéo sur du code pur et doivent gérer une carte de jeu orientée matricielle pour stocker les objets la carte de jeu [si vous avez besoin d'aide à ce sujet, écrivez-moi]).

3

Merci à Oleg Estekhin pour avoir signalé 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 de pixels.

Circles before and after

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

La première colonne est sans indication de rendu. La seconde est avec antialias seulement. Le troisième est avec antialias et le mode pur.

Notez qu'avec les indications antialias, les trois premiers cercles sont les mêmes et les deux derniers sont identiques. Il semble qu'il y ait une transition discrète. Probablement arrondi à un moment donné.

Voici le code. Il est en Jython pour la lisibilité, mais il conduit la bibliothèque d'exécution Java en dessous et peut être transféré 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 dessinés avec une bonne recherche de précision sous-pixel.

Questions connexes