0

背景

我试图通过一些表盘惯性模拟等创建精美、流畅和快速的模拟仪表。如果可能的话,我想避免使用 OpenGL。

问题

我的 Java 代码比我预期的要慢得多。

我希望我的表盘从最小值(0)到最大值(1024,我可以改变这个,但我需要平滑度)的时间小于 0.5 秒。

我试图测量在 repaint 和 paintComponent 方法上花费的时间来发现问题。

在我的机器(Core Duo 2GHz,Windows 7)上,重绘大约需要 40us,paintComponent 需要 300us。

它似乎足够快(1/0.000340s = ~3000“运行”每秒)。

我认为视频卡是瓶颈,它会减慢我的代码,但我不知道如何处理它。

问题

如何让我的代码更快并尽可能保持动画流畅?

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;

import javax.swing.*;

public class Main extends JPanel {
   private static final Point2D CENTER = new Point2D.Double(PREF_W / 2.0,
         PREF_W / 2.0);
   private static final double RADIUS = PREF_W / 2.0;
   private static final Color LARGE_TICK_COLOR = Color.DARK_GRAY;
   private static final Color CENTER_HUB_COLOR = Color.DARK_GRAY;
   private static final Stroke LARGE_TICK_STROKE = new BasicStroke(4f);
   private static final Stroke LINE_TICK_STROKE = new BasicStroke(8f);
   private static final int LRG_TICK_COUNT = 18;
   private static final double TOTAL_LRG_TICKS = 24;
   private static final double LRG_TICK_OUTER_RAD = 0.9;
   private static final double LRG_TICK_INNER_RAD = 0.8;
   private static final int START_TICK = 10;
   private static final double CENTER_HUB_RADIUS = 10;
   private static final double DIAL_INNER_RAD = 0.00;
   private static final double DIAL_OUTER_RAD = 0.75;
   private static final Color DIAL_COLOR = Color.DARK_GRAY;
   private BufferedImage backgroundImg;


   private static final int PREF_W = 400; // 
   private static final int PREF_H = 400;

   private static final double INIT_VALUE = 0;
   public static final int MAX_VALUE = 1024; // resolution

   public static int delay = 1; // delay (ms) between value changes

   private double theta;
   private double cosTheta;
   private double sinTheta;
   private static long microtime;

   public Main() {
      setBackground(Color.white);

      backgroundImg = createBackgroundImg();
      setSpeed(INIT_VALUE);
   }

   public void setSpeed(double speed) {


     if (speed < 0) {
         speed = 0;
      } else if (speed > MAX_VALUE) {
         speed = MAX_VALUE;
      }
      this.theta = ((speed / MAX_VALUE) * LRG_TICK_COUNT * 2.0 + START_TICK)
            * Math.PI / TOTAL_LRG_TICKS;

      cosTheta = Math.cos(theta);
      sinTheta = Math.sin(theta);

      microtime = System.nanoTime()/1000;

      repaint();

      System.out.println("Repaint (us) = " + (System.nanoTime()/1000 - microtime));



   }

   private BufferedImage createBackgroundImg() {
      BufferedImage img = new BufferedImage(PREF_W, PREF_H,
            BufferedImage.TYPE_INT_ARGB);
      Graphics2D g2 = img.createGraphics();

      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setColor(LARGE_TICK_COLOR);
      g2.setStroke(LARGE_TICK_STROKE);

      for (double i = 0; i < LRG_TICK_COUNT; i++) {
         double theta = (i * 2.0 + START_TICK) * Math.PI / TOTAL_LRG_TICKS;
         double cosTheta = Math.cos(theta);
         double sinTheta = Math.sin(theta);

         int x1 = (int) (LRG_TICK_INNER_RAD * RADIUS * cosTheta + CENTER.getX());
         int y1 = (int) (LRG_TICK_INNER_RAD * RADIUS * sinTheta + CENTER.getY());
         int x2 = (int) (LRG_TICK_OUTER_RAD * RADIUS * cosTheta + CENTER.getX());
         int y2 = (int) (LRG_TICK_OUTER_RAD * RADIUS * sinTheta + CENTER.getY());

         g2.drawLine(x1, y1, x2, y2);
      }

      g2.setColor(CENTER_HUB_COLOR);

      int x = (int) (CENTER.getX() - CENTER_HUB_RADIUS);
      int y = (int) (CENTER.getY() - CENTER_HUB_RADIUS);
      int width = (int) (2 * CENTER_HUB_RADIUS);
      int height = width;
      g2.fillOval(x, y, width, height);


      g2.dispose();
      return img;
   }

   @Override
   protected void paintComponent(Graphics g) {

          System.out.println("Paint component (us) = " + (System.nanoTime()/1000 - microtime));

      super.paintComponent(g);
      if (backgroundImg != null) {
         g.drawImage(backgroundImg, 0, 0, this);
      }

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


      g.setColor(DIAL_COLOR);

      int x1 = (int) (DIAL_INNER_RAD * RADIUS * cosTheta + CENTER.getX());
      int y1 = (int) (DIAL_INNER_RAD * RADIUS * sinTheta + CENTER.getY());
      int x2 = (int) (DIAL_OUTER_RAD * RADIUS * cosTheta + CENTER.getX());
      int y2 = (int) (DIAL_OUTER_RAD * RADIUS * sinTheta + CENTER.getY());

      g.drawLine(x1, y1, x2, y2);

      microtime = System.nanoTime()/1000;


   }

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

   private static void createAndShowGui() {
      final Main mainPanel = new Main();

      JFrame frame = new JFrame("DailAnimation");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);

      new Timer(delay, new ActionListener() {
         double speed = 0;

         @Override
         public void actionPerformed(ActionEvent evt) {

            speed ++;
            if (speed > Main.MAX_VALUE) {
                speed = 0;
            }
            mainPanel.setSpeed(speed);

         }
      }).start();
   }

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

小代码说明:

有一个计时器,可以改变仪表值。定时器间隔在开始时由delay变量定义。

这是完整的,一个文件代码,你可以将它粘贴到你的IDE中并编译。

4

1 回答 1

0

您可以尝试以下方法:

  • 从 BufferedImage 切换到 VolatileImage
  • 预先计算您的 sin 和 cos 函数结果并将它们存储在数组中
  • 切换到主动绘画而不是调用重绘
于 2013-07-02T04:52:39.683 回答