2012-10-10 57 views
0

作爲大學項目的一部分,我正在開發一個用於註釋圖像的Java swing程序。該程序的特點之一是能夠在圖像的某些區域繪製多邊形,然後可以用標題標註這些多邊形。在繪製幾個多邊形後Java繪圖程序變慢

繪製多邊形時,每次單擊都會在圖像上繪製一個新的綠色頂點,並通過繪製一條線將此頂點鏈接到上一個頂點。還有一個預覽行是在用戶移動鼠標時繪製的,以便他們可以看到下一次單擊將添加到多邊形的形狀。

我遇到的問題是,一旦用戶繪製了一個多邊形,總體程序性能就會顯着下降。預覽線的繪製變得令人難以置信地緊張,難以使用。

負責的這部分程序代碼是在文件ImagePanel.java:

package hci; 

import javax.imageio.ImageIO; 
import javax.swing.JPanel; 
import javax.swing.JOptionPane; 

import java.awt.Color; 
import java.awt.BasicStroke; 
import java.awt.Stroke; 
import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Image; 
import java.awt.Polygon; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseListener; 
import java.awt.event.MouseMotionListener; 
import java.awt.image.BufferedImage; 
import java.awt.geom.Point2D; 
import java.io.File; 
import java.util.ArrayList; 

import hci.utils.*; 

public class ImagePanel extends JPanel implements MouseListener, MouseMotionListener { 

    private static final long serialVersionUID = 1L; 
    BufferedImage image = null; 
    CaptionedPolygon currentPolygon = null; 
    ArrayList<CaptionedPolygon> polygonsList = null; 
    Point mousePos; 
    public static final int FIRST_NODE_SIZE = 15; 

    public ImagePanel() { 
     currentPolygon = new CaptionedPolygon(); 
     polygonsList = new ArrayList<CaptionedPolygon>(); 
     mousePos = new Point(0,0); 

     this.setVisible(true); 

     Dimension panelSize = new Dimension(800, 600); 
     this.setSize(panelSize); 
     this.setMinimumSize(panelSize); 
     this.setPreferredSize(panelSize); 
     this.setMaximumSize(panelSize); 

     addMouseListener(this); 
     addMouseMotionListener(this); 
    } 

    public ImagePanel(String imageName) throws Exception{ 
     this(); 
     image = ImageIO.read(new File(imageName)); 
     if (image.getWidth() > 800 || image.getHeight() > 600) { 
      int newWidth = image.getWidth() > 800 ? 800 : (image.getWidth() * 600)/image.getHeight(); 
      int newHeight = image.getHeight() > 600 ? 600 : (image.getHeight() * 800)/image.getWidth(); 
      System.out.println("SCALING TO " + newWidth + "x" + newHeight); 
      Image scaledImage = image.getScaledInstance(newWidth, newHeight, Image.SCALE_FAST); 
      image = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB); 
      image.getGraphics().drawImage(scaledImage, 0, 0, this); 
     } 
    } 

    public void ShowImage(Graphics g) { 

     if (image != null) { 
      g.drawImage(
        image, 0, 0, null); 
     } 
    } 

    @Override 
    public void paintComponent(Graphics g) { 
     super.paintComponent(g); 

     //display image 
     ShowImage(g); 
     drawPreviewLine(g); 

     //display all the completed polygons 
     for(CaptionedPolygon polygon : polygonsList) { 
      fillPolygon(polygon, g); 
      drawPolygon(polygon, g); 
      finishPolygon(polygon, g); 
     } 

     //display current polygon 
     drawPolygon(currentPolygon, g); 


    } 

    public void drawPreviewLine(Graphics g){ 
     if (currentPolygon.points.size() > 0 && mousePos != null){ 
      Point currentPoint = currentPolygon.points.get(currentPolygon.points.size() - 1); 
      g.setColor(Color.GREEN); 
      g.drawLine(currentPoint.getX(), currentPoint.getY(), mousePos.getX(), mousePos.getY()); 
     } 
    } 

    public void fillPolygon(CaptionedPolygon polygon, Graphics g){ 
     Color fillColor = new Color((float)0.0,(float)1.0,(float)0.0, (float)0.3); 
     Polygon polyToDraw = new Polygon(); 
     for (Point point : polygon.points){ 
      polyToDraw.addPoint(point.getX(), point.getY()); 
     } 
     g.setColor(fillColor); 
     g.fillPolygon(polyToDraw); 
    } 

    public void drawPolygon(CaptionedPolygon polygon, Graphics g) { 
     for(int i = 0; i < polygon.points.size(); i++) { 
      int sizeModifier = 0; 
      Graphics2D g2 = (Graphics2D)g; 
      g2.setColor(Color.GREEN); 
      g2.setStroke(new BasicStroke(1)); 
      Point currentVertex = polygon.points.get(i); 


      if(currentPolygon.equals(polygon) && i == 0){ //First point of the current polygon 

       //Enlarge circle drawn if mouse hovers over point 
       if(pointWithinCircle(mousePos.getX(), mousePos.getY(), currentVertex.getX(), currentVertex.getY(), (FIRST_NODE_SIZE + 2)/2)){ 
        sizeModifier = 3; 
       } 

       int nodeSize = FIRST_NODE_SIZE + sizeModifier; 

       g2.setColor(Color.WHITE); 
       g2.fillOval(currentVertex.getX() - nodeSize/2 , currentVertex.getY() - nodeSize/2, nodeSize, nodeSize); 
       g2.setStroke(new BasicStroke(2)); 
       g2.setColor(Color.GREEN); 
       g2.drawOval(currentVertex.getX() - nodeSize/2 , currentVertex.getY() - nodeSize/2, nodeSize, nodeSize); 
      } 
      else if (i != 0){ //Some arbitary middle point 
       Point prevVertex = polygon.points.get(i - 1); 
       g2.drawLine(prevVertex.getX(), prevVertex.getY(), currentVertex.getX(), currentVertex.getY()); 
       g2.fillOval(currentVertex.getX() - 5, currentVertex.getY() - 5, 10, 10); 
      } 
      else{ //First point of some non current polygon 
       g2.fillOval(currentVertex.getX() - 5, currentVertex.getY() - 5, 10, 10); 
      } 
     } 
    } 

    public void finishPolygon(CaptionedPolygon polygon, Graphics g) { 
     //if there are less than 3 vertices than nothing to be completed 
     if (polygon.points.size() >= 3) { 
      Point firstVertex = polygon.points.get(0); 
      Point lastVertex = polygon.points.get(polygon.points.size() - 1); 

      g.setColor(Color.GREEN); 
      g.drawLine(firstVertex.getX(), firstVertex.getY(), lastVertex.getX(), lastVertex.getY()); 
     } 
    } 

    public void addNewPolygon() { 
     //finish the current polygon if any 
     if (currentPolygon.points.size() > 0) { 
      currentPolygon.caption = JOptionPane.showInputDialog(this, "Please enter a caption for this area") ; 
      polygonsList.add(currentPolygon); 
     } 

     currentPolygon = new CaptionedPolygon(); 
     repaint(); 
    } 

    public boolean pointWithinCircle(int targetX, int targetY, int circleCentX, int circleCentY, double circleRadius){ 
     Point2D.Double mousePoint = new Point2D.Double(targetX,targetY); 
     Point2D.Double firstNodePoint = new Point2D.Double(circleCentX, circleCentY); 
     return (mousePoint.distance(firstNodePoint) <= circleRadius); 
    } 

    @Override 
    public void mouseClicked(MouseEvent e) { 
    } 

    @Override 
    public void mouseEntered(MouseEvent arg0) { 
    } 

    @Override 
    public void mouseExited(MouseEvent arg0) { 
    } 

    @Override 
    public void mousePressed(MouseEvent e) { 
     int x = e.getX(); 
     int y = e.getY(); 

     //check if the cursor is within image area 
     if (x > image.getWidth() || y > image.getHeight()) { 
      return; 
     } 

     //Clicking the left button will either add a new vertex or finish off a polygon 
     if (e.getButton() == MouseEvent.BUTTON1) { 
      if (currentPolygon.points.size() > 0){ 

       if(pointWithinCircle(x, y, currentPolygon.points.get(0).getX(), currentPolygon.points.get(0).getY(), FIRST_NODE_SIZE + 2)){ 
        addNewPolygon(); 
       } 
       else{ 
        currentPolygon.points.add(new Point(x,y)); 
        System.out.println(x + " " + y); 
        repaint(); 
       } 
      } 
      else{ 
       currentPolygon.points.add(new Point(x,y)); 
       System.out.println(x + " " + y); 
       repaint(); 
      } 
     } 
    } 

    @Override 
    public void mouseReleased(MouseEvent arg0) { 
    } 

    public void mouseDragged(MouseEvent e){ 
    } 

    public void mouseMoved(MouseEvent e){ 
     mousePos.setX(e.getX()); 
     mousePos.setY(e.getY()); 
     repaint(); 
    } 

} 

我都試過,現在在幾個不同的操作系統和筆記本電腦上運行該程序,並放緩它們都是顯而易見的。這表明這是我的代碼問題,而不是運行它的問題。

我有一種感覺,我的問題與我稱爲repaint()方法的次數過多有關。我還沒有真正看到過使用Java的swing和圖形庫來實現繪圖功能的最佳方式,因此我希望能夠提供關於一般實踐的建議以及直接解決此問題的方法。

回答

3

它看起來像你直接繪製到幀緩衝區。這在每次繪製任何東西時都很慢,JVM必須進行系統調用來更新屏幕上的圖像。

如果您在JVM內存中的單個框架上執行所有繪製操作,並且只有在整個圖像準備就緒時才輸出到系統,則會看到更好的渲染速度。 (這是什麼樣的你已經在你的背景圖片image做,事實上你可以重複使用的圖形對象你已經建立,但不使用)

所以,你需要創建畫畫的畫布;

BufferedImage canvas = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); 

然後搶Graphics對象,你將用來繪製到畫布上;

Graphics cg = canvas.getGraphics(); 

把你所有的繪圖操作上cg,然後在您的paintComponent(Graphics g)功能,只畫canvas在使用單一電話的組件;

g.drawImage(canvas, 0, 0, null); 

爲了獲得更好的性能,你應該畫一個VolatileImage而不是BufferedImage。但是BufferedImage使用起來更容易,而且對於您的目的而言,它表現得很好。

+0

謝謝,正是我所需要的 –

+0

@lynks我知道這是舊的..如果你仍然在這裏:這是所謂的「雙緩衝」?如果是這樣,爲了獲得流暢的動畫,需要多長時間將緩衝圖像繪製到屏幕上?謝謝 :) –