Swing 主动渲染效率或如何将主动渲染与 gui 小部件结合起来 [英] Swing active rendering efficiency or how to combine active rendering with gui widgets

查看:68
本文介绍了Swing 主动渲染效率或如何将主动渲染与 gui 小部件结合起来的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

继续上一个问题,我一直在寻找将主动渲染与 Java 中的文本字段相结合的最佳方式.我尝试了几个选项,在标准 AWT 中使用 BufferStrategy、VolatileImage 或覆盖 update() 和 paint(),但我最终使用了 Swing.

Continuing from a previous question, I keep searching for the optimal way to combine active rendering with textfields in Java. I tried several options, using BufferStrategy, VolatileImage or overriding update() and paint() in standard AWT, but I ended up using Swing.

我在这里发布当前的事态,以防有人碰巧根据我的代码示例有新的见解,也许其他正在开发类似应用程序的人可能会从我的发现中受益.

I'm posting the current state of affairs here just in case someone happens to have new insights based on my code example, and perhaps others who are working on a similar app might benefit from my findings.

目标是完成这三个壮举:

The target is to accomplish these three feats:

  • 在仅在必要时更新的背景缓冲区顶部渲染动画对象
  • 在渲染结果之上使用文本字段
  • 调整窗口大小没有问题

以下是在stackoverflower trashgod的大力帮助下开发的演示应用程序的代码.
两个注意事项:

Below is the code of the demo application developed with the great help of stackoverflower trashgod.
Two notes:

1) 严格刷新动画中上一步无效的区域似乎很容易出现视觉错误,我放弃了.这意味着我现在每帧都重绘整个背景缓冲区.

1) Refreshing strictly the area that is invalidated by the previous step in the animation appears to be so much prone to visual errors that I gave up on it. This means I now redraw the entire background buffer every frame.

2) 将 BufferedImage 绘制到屏幕的效率在很大程度上取决于平台.Mac 实现似乎没有正确支持硬件加速,这使得将背景图像重新绘制到输出窗口成为一项繁琐的任务,当然取决于窗口的大小.

2) The efficiency of drawing a BufferedImage to screen is hugely dependent on the platform. The Mac implementation doesn't seem to support hardware acceleration properly, which makes repainting the background image to the output window a tedious task, depending of course on the size of the window.

我在 2.93 GHz 双核 iMac 上发现以下结果:

I found the following results on my 2.93 GHz dualcore iMac:

Mac 操作系统 10.5:
640 x 480:0.9 毫秒,8 - 9%
1920 x 1100:5 毫秒,35 - 40%

Mac OS 10.5:
640 x 480: 0.9 ms, 8 - 9%
1920 x 1100: 5 ms, 35 - 40%

Windows XP:
640 x 480:0.05 毫秒,0%
1920 x 1100:0.05 毫秒,0%

Windows XP:
640 x 480: 0.05 ms, 0%
1920 x 1100: 0.05 ms, 0%

图例:
屏幕尺寸:画框的平均时间,应用程序的CPU使用率.

Legend:
screen size: average time to draw a frame, CPU usage of the application.

据我所知,下面的代码是实现我的目标的最有效方式.非常欢迎任何新的见解、优化或测试结果!

As far as I can see, the code below is the most efficient way of accomplishing my goals. Any new insights, optimizations or test results are very welcome!

问候,马蒂斯

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;

public class SwingTest extends JPanel implements 
 ActionListener, 
 Runnable 
{
 private static final long serialVersionUID = 1L;

 private BufferedImage backgroundBuffer;
    private boolean repaintbackground = true;

    private static final int initWidth = 640;
    private static final int initHeight = 480;
    private static final int radius = 25;
    private final Timer t = new Timer(20, this);
    private final Rectangle rect = new Rectangle(); 

    private long totalTime = 0;
    private int frames = 0;
    private long avgTime = 0;

    public static void main(String[] args) {
        EventQueue.invokeLater(new SwingTest());
    }

    public SwingTest() {
        super(true);
        this.setPreferredSize(new Dimension(initWidth, initHeight));
        this.setLayout(null);
        this.setOpaque(false);
        this.addMouseListener(new MouseHandler());
    }

    @Override
    public void run() {
        JFrame f = new JFrame("SwingTest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.addComponentListener(new ResizeHandler());

/*      This extra Panel with GridLayout is necessary to make sure 
   our content panel is properly resized with the window.*/
        JPanel p = new JPanel(new GridLayout()); 
        p.add(this);
        f.add(p);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);

        createBuffer();     
        t.start();
    }    

    @Override
    public void actionPerformed(ActionEvent e) {
        this.repaint();
    }    

    @Override
    protected void paintComponent(Graphics g) {
     long start = System.nanoTime();
     super.paintComponent(g);

     if (backgroundBuffer == null) createBuffer();
     if (repaintbackground) {

/*   Repainting the background may require complex rendering operations, 
   so we don't want to do this every frame.*/       
      repaintBackground(backgroundBuffer);
            repaintbackground = false;
     }

/*  Repainting the pre-rendered background buffer every frame
       seems unavoidable. Previous attempts to keep track of the 
       invalidated area and repaint only that part of the background buffer 
       image have failed. */
     g.drawImage(backgroundBuffer, 0, 0, null);
     repaintBall(g, backgroundBuffer, this.getWidth(), this.getHeight());
     repaintDrawTime(g, System.nanoTime() - start);
    }

    void repaintBackground(BufferedImage buffer) {    
     Graphics2D g = buffer.createGraphics();
  int width = buffer.getWidth();
  int height = buffer.getHeight();

  g.clearRect(0, 0, width, height);
  for (int i = 0; i < 100; i++) {
   g.setColor(new Color(0, 128, 0, 100));
   g.drawLine(width, height, (int)(Math.random() * (width - 1)), (int)(Math.random() * (height - 1)));
  }
    }

    void repaintBall(Graphics g, BufferedImage backBuffer, int width, int height) {
     double time = 2* Math.PI * (System.currentTimeMillis() % 3300) / 3300.;
        rect.setRect((int)(Math.sin(time) * width/3 + width/2 - radius), (int)(Math.cos(time) * height/3 + height/2) - radius, radius * 2, radius * 2);

        g.setColor(Color.BLUE);
        g.fillOval(rect.x, rect.y, rect.width, rect.height);
    }

    void repaintDrawTime(Graphics g, long frameTime) {
     if (frames == 32) {avgTime = totalTime/32; totalTime = 0; frames = 0;}
     else {totalTime += frameTime; ++frames; }
     g.setColor(Color.white);
     String s = String.valueOf(avgTime / 1000000d + " ms");
        g.drawString(s, 5, 16);
    }

    void createBuffer() {
        int width = this.getWidth();
        int height = this.getHeight();

        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gs = ge.getDefaultScreenDevice();
        GraphicsConfiguration gc = gs.getDefaultConfiguration();
        backgroundBuffer = gc.createCompatibleImage(width, height, Transparency.OPAQUE);        

        repaintbackground = true;
    }    

    private class MouseHandler extends MouseAdapter {

        @Override
        public void mousePressed(MouseEvent e) {
            super.mousePressed(e);
            JTextField field = new JTextField("test");
            Dimension d = field.getPreferredSize();
            field.setBounds(e.getX(), e.getY(), d.width, d.height);
            add(field);
        }
    }

    private class ResizeHandler extends ComponentAdapter {

     @Override
     public void componentResized(ComponentEvent e) {
      super.componentResized(e);
      System.out.println("Resized to " + getWidth() + " x " + getHeight());
      createBuffer();
     }    
    }
}

推荐答案

我有几点看法:

  1. 你改进的 repaintDrawTime() 可读性很强,但它是一个 微基准 并受变幻莫测a> 主机操作系统.我不禁怀疑 XP 结果是否是该系统有限时钟分辨率的产物.我在 Windows 7 和 Ubuntu 10 上看到非常不同的结果.

  1. Your improved repaintDrawTime() is very readable, but it is a micro-benchmark and subject to the vagaries of the host OS. I can't help wondering if the XP results are an artifact of that system's limited clock resolution. I see very different results on Windows 7 and Ubuntu 10.

如果不使用空布局,则不需要额外的面板;JPanel 的默认布局是 FlowLayout,而 f.add(this) 只是将它添加到框架默认 BorderLayout 的中心.

If you don't use a null layout, you won't need the extra panel; the default layout for JPanel is FlowLayout, and f.add(this) simply adds it to the center of the frame's default BorderLayout.

重复的构造函数调用可能很耗时.

Repeated constructor invocations can be time consuming.

考虑更换

g.setColor(new Color(0, 128, 0, 100));

private static final Color color = new Color(0, 128, 0, 100);
...
g.setColor(color);

或者,一个简单的颜色查找表可能很有用,例如

Alternatively, a simple color lookup table, may be useful, e.g.

private final Queue<Color> clut = new LinkedList<Color>();

这篇关于Swing 主动渲染效率或如何将主动渲染与 gui 小部件结合起来的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆