2010-07-20 2 views
3

계속 previous question에서 계속해서 활성 표현과 Java의 텍스트 필드를 결합하는 최적의 방법을 찾고 있습니다. BufferStrategy, VolatileImage를 사용하거나 표준 AWT에서 update() 및 paint()를 재정 의하여 여러 옵션을 시도했지만 Swing을 사용하여 종료되었습니다.활성 렌더링 효율을 높이거나 활성 렌더링을 GUI 위젯과 결합하는 방법

누군가가 내 코드 예제를 기반으로 새로운 통찰력을 얻은 경우에 대비하여 여기에 현재 상태를 게시하고 비슷한 응용 프로그램에서 작업하는 다른 사람들이 내 결과에서 이익을 얻을 수도 있습니다.

대상이 세 업적 달성하는 것이다

  • 만 업데이트되는 배경 버퍼의 상단에 생기 객체 렌더링을 할 때, 렌더링 결과
  • 리사이즈 위에 필요한
  • 사용 텍스트 필드 문제가없는 윈도우

다음은 stackoverflower trashgod의 큰 도움으로 개발 된 데모 애플리케이션의 코드입니다.
두 노트 :

1) 애니메이션의 이전 단계에서 무효화 된 영역을 엄격하게 새로 고치면 시각적 오류가 발생하기 쉬워집니다. 이것은 이제 모든 프레임마다 전체 배경 버퍼를 다시 그려야 함을 의미합니다.

2) BufferedImage를 화면에 그리는 효율은 플랫폼에 따라 크게 달라집니다. Mac 구현은 하드웨어 가속을 적절히 지원하지 않는 것 같습니다. 따라서 창 크기에 따라 배경 이미지를 출력 창에 다시 칠하는 작업이 지루한 작업이됩니다. 5 MS, 35~40% : -

맥 OS 10.5 : : :
640 × 480 9 %
1920 X 1100 0.9 MS, 8

나는 나의 2.93 GHz의 이중 코어에 다음과 같은 결과를 아이맥을 발견

윈도우 XP :
640 × 480 : 0.05 MS,
1920 X 1100 0 % : 0.05 MS, 0 %

설명 :
화면 크기 : 프레임을 그리는 평균 시간, 응용 프로그램의 CPU 사용량.

내가 알 수있는 한, 아래 코드는 내 목표를 달성하는 가장 효율적인 방법입니다. 새로운 통찰력, 최적화 또는 테스트 결과는 대단히 환영합니다!

감사합니다, Mattijs

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

답변

2

나는 몇 가지 관찰을 가지고 :

  1. 귀하의 repaintDrawTime() 매우 읽을 수 있지만, 호스트 OS의 vagariesmicro-benchmark 및 대상이다 개선되었다. XP 결과가 해당 시스템의 인공물인지 궁금합니다. limited clock resolution. Windows 7 및 Ubuntu 10에서 매우 다른 결과가 나타납니다.

  2. 널 레이아웃을 사용하지 않는 경우 추가 패널이 필요하지 않습니다. JPanel의 기본 레이아웃은 FlowLayout이고 f.add(this)은 프레임의 기본값 인 BorderLayout의 가운데에 간단히 추가합니다.

  3. 반복되는 생성자 호출에는 많은 시간이 소요될 수 있습니다.

    는, 예를 유용 할 수있는 대안

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

    간단한 color lookup table으로

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

    교체를 고려

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

1) 포인터에 감사드립니다. 가장 신뢰할 수있는 벤치 마크로 시스템의 기본 Activity Monitor/Task Manager에서보고 한대로 CPU 사용량을 사용합니다. 이 데이터를 원래 게시물에 포함 시켰지만 여전히 Windows에서 Mac OS가 아닌 이미지를 다시 칠하기 위해 하드웨어 가속을 사용함을 나타냅니다. 2) 아, 실제로 JPanel을 추가하지 않으면 크기가 잘 조정됩니다. 3) 그리고 다시 말해서. 다행히도이 응용 프로그램은 데모 용이지만 실제 응용 프로그램에서는이를 염두에 두겠습니다. – Mattijs

1

나는이 방법에의 BufferedImage에 전달하는 점을 볼 수 없습니다 : 당신이 방법 본문에 이제까지 그것을 사용하는 것이 보이지 않는다

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

.

나는이 클래스의 그래픽 부분을 내 자신의 JPanel 생성자 클래스에 이식 할 수 있었고 내 게임의 그래픽을 많이 개선했지만, BufferedImage에서 BufferedImage로 전달하는 곳과 같은 메서드를 사용할 필요가 없었습니다. 논쟁은하지만 결코 그것을 사용하지 마십시오.

+0

동의합니다. 이전 버전에서 남은 것이 틀림 없습니다. 내 잘못, 그것을 지적 주셔서 감사합니다. – Mattijs

관련 문제