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.

23
demandé sur soldier.moth 2009-07-08 00:03:09

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);
38
répondu Brandon Yarbrough 2015-06-27 06:46:50

vous pouvez définir des indices de rendu:

Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
    RenderingHints.VALUE_ANTIALIAS_ON);
27
répondu Josef Pfleger 2009-07-07 20:09:07

deux choses qui peuvent aider:

  1. utiliser 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'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

12
répondu finnw 2012-10-26 12:02:04

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);
}
6
répondu Clint 2012-01-25 18:33:59

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.

Circles before and after

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.

4
répondu Evgeni Sergeev 2015-07-05 15:30:01

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

3
répondu Oleg Estekhin 2017-05-23 11:54:43

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]).

-1
répondu Marco Ottina 2017-09-06 19:55:31