Graphics2D.drawString非常慢

时间:2013-08-06 11:22:58

标签: java swing graphics2d

我正在使用Java Graphics2D将一些行和标签绘制到JPanel中。绘图代码运行得非常慢(drawString()似乎是最糟糕的,但我不再认为它是罪魁祸首,只是最繁忙的调用)。

执行环境是OSX 10.8.4,VM详细信息是:

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>

完整的自包含示例代码如下:

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

}

拖动或缩放(使用滚轮)非常慢。 VisualVM分析数据如下。每次调用TestDrawString.draw()大约需要400ms!但是,如果我没有将按钮添加到侧面板,它会显着加快 - 每次draw()调用都需要不到1ms。怪异。

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

1 个答案:

答案 0 :(得分:0)

感谢trashgod提出的一个非常明智的建议,即创建一个小型的独立示例,我能够找出问题所在。它归结为混合AWT和Swing组件,特别是添加一个Button而不是JButton。