2016-09-27 1 views
0

J'ai ce programme:Java Graphics.fillXxx() en combinaison avec Graphics2D.scale()

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Font; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.geom.Rectangle2D; 

import javax.swing.JComponent; 
import javax.swing.JFrame; 
import javax.swing.SwingUtilities; 
import javax.swing.WindowConstants; 


public class TestLine { 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       new TestLine().start(); 
      } 
     }); 
    } 

    private static void start() { 
     JFrame frame = new JFrame(); 
     frame.setContentPane(new CarthPanel()); 
     frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
     frame.pack(); 
     frame.setVisible(true); 
    } 

    private static class CarthPanel extends JComponent { 

     @Override 
     public Dimension getPreferredSize() { 
      return new Dimension(200, 200); 
     } 

     @Override 
     protected void paintComponent(Graphics g) { 
      Graphics2D gg = (Graphics2D) g; 

      int w = gg.getClipBounds().width; 
      int h = gg.getClipBounds().height; 
      System.out.println("(w,h)=(" + w + "," + h + ")"); 

      gg.translate(w - 1, 0); // <<< when uncommenting both lines, mirroring applies 
      gg.scale(-1.0, 1.0); // 

      paintTest(gg, w, h); 
     } 

     private static void paintTest(Graphics2D g, int w, int h) { 
      // black background 
      g.setColor(Color.black); 
      g.fillRect(0, 0, w, h); 

      // colored corners 
      g.setColor(Color.RED); 
      g.drawLine(0, 0, 10, 0); 
      g.drawLine(0, 0, 0, 10); 
      g.setColor(Color.RED); 
      g.drawLine(0, 199, 10, 199); 
      g.drawLine(0, 199, 0, 189); 
      g.setColor(Color.CYAN); 
      g.drawLine(189, 0, 199, 0); 
      g.drawLine(199, 0, 199, 10); 
      g.setColor(Color.CYAN); 
      g.drawLine(189, 199, 199, 199); 
      g.drawLine(199, 199, 199, 189); 

      // yellow squares 
      g.setColor(Color.yellow); 
      g.drawRect(3, 3, 10, 10); 
      g.fillRect(186, 3, 11, 11); 
      g.fillRect(3, 186, 11, 11); 
      g.drawRect(186, 186, 10, 10); 

      String chars = "ABC"; 
      g.setFont(Font.decode("Arial 72")); 
      Rectangle2D rect = g.getFontMetrics().getStringBounds(chars, g); 
      g.drawString(chars, (int) (200 - rect.getWidth())/2, (int) (200 - rect.getHeight())); 
     } 
    } 
} 

Si vous exécutez ce programme une fois avec deux lignes particulières et commentées puis une fois avec les mêmes lignes en commentaire (voir le code), vous pouvez voir ceci:

[Graphics.scale() and Graphics.translate()[1]

Si vous ne l'avez pas repérer le problème, voici une photo zoomée:

Zoomed

On s'attendrait à ce que l'image soit parfaitement reflétée. Ceci est vrai pour tous les traits et formes creuses (= méthodes drawXxx()). Cependant, toutes les formes remplies (= méthodes fillXxx()) sont dessinées exactement à un pixel à gauche de l'endroit où je les attend; par exemple. les rectangles jaunes remplis, et vous pouvez également remarquer que le fond noir s'est décalé, comme vu par la ligne blanche à la frontière droite.

Est-ce un bug ou est-ce prévu? Je soupçonne qu'il a quelque chose à voir avec la différence comment « largeur » et « hauteur » sont en cours de traitement dans des procédés drawXxx() et fillXxx():

  • résultats drawRect (x, y, w, h) dans un rectangle creux avec des limites d'axe X x et x + w, de sorte que le rectangle est w + 1 pixels de large. FillRect (x, y, w, h) donne un rectangle rempli avec les limites x et x + w-1 de l'axe X, de sorte que le rectangle a une largeur de w pixels.

Que manque-t-il?

Répondre

-1

Il s'agit d'un bug, ce n'est pas une fonctionnalité :) Certes, ce n'est pas prévu, l'image en miroir doit être parfaite, il n'y a aucune raison de ne pas le faire.

Il existe un moyen d'utiliser BufferedImage pour le rendre normalement, puis dessiner ce BufferedImage retourné, le seul inconvénient est un peu d'influence sur les performances et le texte n'est pas très bien dessiné en utilisant subpixeling LCD.

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Font; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.geom.Rectangle2D; 
import java.awt.image.BufferedImage; 

import javax.swing.JComponent; 
import javax.swing.JFrame; 
import javax.swing.SwingUtilities; 
import javax.swing.WindowConstants; 


public class TestLine { 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       new TestLine().start(); 
      } 
     }); 
    } 

    private static void start() { 
     JFrame frame = new JFrame(); 
     frame.setContentPane(new CarthPanel()); 
     frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 
     frame.pack(); 
     frame.setVisible(true); 
    } 

    private static class CarthPanel extends JComponent { 

     @Override 
     public Dimension getPreferredSize() { 
      return new Dimension(200, 200); 
     } 

     @Override 
     protected void paintComponent(Graphics g) { 
      Graphics2D gg = (Graphics2D) g; 

//   System.out.println("(w,h)=(" + w + "," + h + ")"); 

      gg.translate(getWidth(), 0); // <<< when uncommenting both lines, mirroring applies 
      gg.scale(-1.0, 1.0); // 

      BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB); 

      try { 
       paintTest(img.createGraphics()); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 

      g.drawImage(img, 0, 0, null); 
     } 



     private void paintTest(Graphics2D g) { 
      // black background 
      g.setColor(Color.black); 
      g.fillRect(0, 0, getWidth(), getHeight()); 

      // colored corners 
      g.setColor(Color.RED); 
      g.drawLine(0, 0, 10, 0); 
      g.drawLine(0, 0, 0, 10); 
      g.setColor(Color.RED); 
      g.drawLine(0, 199, 10, 199); 
      g.drawLine(0, 199, 0, 189); 
      g.setColor(Color.CYAN); 
      g.drawLine(189, 0, 199, 0); 
      g.drawLine(199, 0, 199, 10); 
      g.setColor(Color.CYAN); 
      g.drawLine(189, 199, 199, 199); 
      g.drawLine(199, 199, 199, 189); 

      // yellow squares 
      g.setColor(Color.yellow); 
      g.drawRect(3, 3, 10, 10); 
      g.fillRect(186, 3, 11, 11); 
      g.fillRect(3, 186, 11, 11); 
      g.drawRect(186, 186, 10, 10); 

      String chars = "ABC"; 
      g.setFont(Font.decode("Arial 72")); 
      Rectangle2D rect = g.getFontMetrics().getStringBounds(chars, g); 
      g.drawString(chars, (int) (200 - rect.getWidth())/2, (int) (200 - rect.getHeight())); 
     } 
    } 
} 
+0

Que voulez-vous dire par "tous les drawXXX sont faux"? De mes tests c'est clairement les formes remplies qui sont décalées, pas les formes décrites. Vous avez raison: le contexte graphique de BufferedImage fonctionne! Cela doit donc être un bug dans le contexte graphique du composant Java. – Timmos

+0

Je pense que votre exemple est presque correct, 'g.drawImage (img, 0, 0, null);' devrait être 'gg.drawImage (img, 0, 0, null);'. – Timmos

+0

Je suppose que votre réponse n'est pas correcte après tout.Chaque fois que vous prenez un contexte graphique à partir d'un BufferedImage et que vous le redimensionnez/traduisez, les méthodes fillXxx() entraînent également des décalages de pixels. Dans votre exemple, vous avez tout dessiné directement sur le BufferedImage (sans aucune transformation), puis vous avez mis à l'échelle/traduit tout le contenu en une seule fois lorsque vous dessinez l'image sur l'objet graphique Java Component. Cela fonctionne en effet, mais votre hypothèse selon laquelle cela est lié au contexte graphique lui-même est fausse: le bogue est là chaque fois que vous dessinez sur un contexte mis à l'échelle/traduit - drawXxx() est pixelisé. – Timmos

0

Pas une réponse, plutôt une hypothèse - la « contient (x, y) » d'un rectangle sera true pour les côtés qui forme la (x, y) coin de la rect et false pour les côtés faire le coin (maxx, maxy). Démo:

Rectangle r=new Rectangle(0, 0, 10, 10); 
System.out.println(r.contains(0, 5)); // true 
System.out.println(r.contains(5, 0)); // true 
System.out.println(r.contains(10, 5)); // false 
System.out.println(r.contains(5, 10)); // false 

L'hypothèse est que fillRect ne considérera pas les « côtés max » comme points à l'intérieur à remplir.

Pour évaluer l'hypothèse, je vous suggère d'essayer d'utiliser un GeneralPath qui définit le même rectangle et de voir qu'il y a une différence dans le remplissage. GeneralPath devrait être interdit de faire des suppositions sur "les bords supérieurs droits sont hors de l'intérieur de la forme"