2013-08-06 4 views
0

J'utilise Java Graphics2D pour dessiner des lignes et des étiquettes dans un JPanel. Le code du dessin est extrêmement lent (drawString() semble le pire, mais je ne pense plus que ce soit le coupable, juste l'appel le plus fréquenté).Graphics2D.drawString très lent

L'environnement d'exécution est Mac OS X 10.8.4, et les détails VM sont:

JVM: Java HotSpot(TM) 64-Bit Server VM (20.51-b01-457, mixed mode) 
Java: version 1.6.0_51, vendor Apple Inc. 
Java Home: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home 
JVM Flags: <none> 

complet exemple de code autonome suit:

package com.controlj.test; 

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.*; 
import java.awt.geom.*; 
import java.util.ArrayList; 
import java.util.List; 

public class TestDrawString { 

List<Line2D> lines = new ArrayList<Line2D>(); 
private Rectangle2D bounds; 

public TestDrawString(Rectangle2D rectBounds) { 
    bounds = rectBounds; 
    int x0 = (int)Math.floor(bounds.getX()); 
    int y0 = (int)Math.floor(bounds.getY()); 
    int x1 = (int)Math.ceil(bounds.getMaxX()); 
    int y1 = (int)Math.ceil(bounds.getMaxY()); 
    for(int i = x0; i != x1 + 1; i++) 
     lines.add(new Line2D.Float(i, y0, i, y1)); 
    for(int i = y0; i != y1 + 1; i++) 
     lines.add(new Line2D.Float(x0, i, x1, i)); 
} 

public void draw(Graphics2D g) { 
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); 
    g.setColor(Color.white); 
    g.setStroke(new BasicStroke(0)); 
    for(Line2D l : lines) 
     g.draw(l); 
    AffineTransform current = g.getTransform(); 
    Point2D origin = new Point2D.Double(bounds.getX(), bounds.getY()); 
    origin = current.transform(origin, origin); 
    g.setTransform(new AffineTransform()); 
    g.setFont(new Font("Monospaced", Font.PLAIN, 12)); 
    for(Line2D l : lines) { 
     Point2D p = new Point2D.Double(l.getX1(), l.getY1()); 
     p = current.transform(p, p); 
     if(l.getX1() == l.getX2()) 
      g.drawString(Integer.toString((int)l.getX1()), (int)p.getX() + 1, (int)origin.getY() - 4); 
     else 
      g.drawString(Integer.toString((int)l.getY1()), (int)origin.getX() + 1, (int)p.getY() - 4); 
    } 
    g.setTransform(current); 
} 

static class ImageLayers extends JPanel { 

    private TestDrawString grids; 
    private Rectangle2D.Double rectBounds; 
    private AffineTransform at; 
    private AffineTransform rat = null; 
    private double ratio; 

    public ImageLayers() { 
     MouseAdapter ma = new MouseAdapter() { 
      private final Point clicked = new Point(); 

      @Override 
      public void mousePressed(MouseEvent mouseEvent) { 
       super.mousePressed(mouseEvent); 
       clicked.setLocation(mouseEvent.getPoint()); 
      } 

      @Override 
      public void mouseDragged(MouseEvent mouseEvent) { 
       super.mouseDragged(mouseEvent); 
       clicked.setLocation(mouseEvent.getX() - clicked.getX(), mouseEvent.getY() - clicked.getY()); 
       setCoords(rectBounds.getX() - clicked.getX() * ratio, rectBounds.getY() + clicked.getY() * ratio, rectBounds.getWidth()); 
       clicked.setLocation(mouseEvent.getPoint()); 
      } 

      @Override 
      public void mouseWheelMoved(MouseWheelEvent mouseWheelEvent) { 
       super.mouseWheelMoved(mouseWheelEvent); 
       System.out.println(mouseWheelEvent.toString()); 
       zoom(1.0 + mouseWheelEvent.getWheelRotation()/20.0, mouseWheelEvent.getPoint()); 
      } 
     }; 
     addMouseListener(ma); 
     addMouseMotionListener(ma); 
     addMouseWheelListener(ma); 
     addComponentListener(new ComponentAdapter() { 
      @Override 
      public void componentResized(ComponentEvent componentEvent) { 
       setTransform(); 
       repaint(); 
      } 
     }); 
     setCoords(118.0, -40.0, 35.0); 
    } 

    public void zoom(double val, Point point) { 
     Point2D point2 = new Point2D.Double(point.getX(), point.getY()); 
     point2 = rat.transform(point2, point2); 
     double x = point2.getX() - (point2.getX() - rectBounds.getX())/val; 
     double y = point2.getY() - (point2.getY() - rectBounds.getY())/val; 
     setCoords(x, y, rectBounds.getWidth()/val); 
    } 

    private void setTransform() { 

     Rectangle pos = getBounds(); 
     System.out.println("Getbounds returned " + pos); 
     if(pos.getHeight() == 0 || pos.getWidth() == 0) 
      return; 
     ratio = rectBounds.width/(double)pos.getWidth(); 
     rectBounds.height = rectBounds.width * (double)pos.getHeight()/(double)pos.getWidth(); 
     at = new AffineTransform(); 
     //at.translate(pos.getX(), pos.getY() + pos.getHeight()); 
     at.translate(0, pos.getHeight()); 
     at.scale((pos.getWidth())/rectBounds.getWidth(), -(pos.getWidth())/rectBounds.getWidth()); 
     at.translate(-rectBounds.getMinX(), -rectBounds.getMinY()); 
     System.out.println("Translation is: " + at.toString()); 

     rat = null; 
     try { 
      rat = at.createInverse(); 
     } catch(NoninvertibleTransformException e) { 
      e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 
     } 
    } 


    @Override 
    protected void paintComponent(Graphics graphics) { 
     super.paintComponent(graphics); 
     Graphics2D g = (Graphics2D)graphics; 
     g.setTransform(at); 
     grids.draw(g); 
    } 

    public void setCoords(double x, double y, double width) { 
     if(width <= 0) 
      width = 35; 
     System.out.println(String.format("setCoords(x=%f, y=%f, width=%f", x, y, width)); 
     rectBounds = new Rectangle2D.Double(x, y, width, width); 
     setTransform(); 
     grids = new TestDrawString(rectBounds); 
     repaint(); 
    } 
} 

public static void main(String args[]) { 
    JFrame frame = new JFrame("DrawStringTest"); 
    frame.setBounds(400, 400, 1000, 1000); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    ImageLayers iml = new ImageLayers(); 
    iml.setBackground(Color.blue); 
    iml.setCoords(118.0, -40.0, 35.0); 
    JPanel jp = new JPanel(); 
    jp.setLayout(new BorderLayout()); 
    JPanel buttonPanel = new JPanel(); 
    buttonPanel.add(new Button("+")); // COMMENT out this line to speed things up! 
    jp.add(buttonPanel, BorderLayout.SOUTH); 
    JSplitPane contentPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, jp, iml); 
    contentPane.setDividerLocation(200); 
    frame.add(contentPane); 
    frame.setVisible(true); 
} 

}

Faire glisser ou zoom (à l'aide molette de défilement) est extrêmement lent. Les données de profil VisualVM sont comme ci-dessous. Chaque appel à TestDrawString.draw() prend environ 400ms! Si, toutefois, je n'ajoute pas le bouton sur le panneau latéral, il accélère considérablement - chaque appel draw() prend moins de 1ms. Bizarre.

profile http://www.sr20.org/DrawString.png

+0

'Lent' comme dans: il est un peu en retard avant d'apparaître sur le panneau? – GGrec

+4

Vous faites des doubles boucles, cela ne va pas aider. Si vous le pouvez, tamponnez les résultats à quelque chose comme un 'BufferedImage' et peignez-les directement. – MadProgrammer

+1

S'il vous plaît modifier votre question pour inclure un [sscce] (http://sscce.org/) qui présente le problème que vous décrivez, et [profile] (http://stackoverflow.com/q/2064427/230513) pour être sûr ; alternativement, utilisez un 'JTextComponent'. – trashgod

Répondre

0

Merci à la suggestion éminemment sensible par trashgod pour créer un petit exemple autonome, j'ai pu comprendre le problème. Il s'agit de mélanger les composants AWT et Swing, en ajoutant spécifiquement un bouton au lieu d'un JButton.