2013-01-08 38 views
1

我有一個應用程序,我想繪製一個隨時間變化的寬度的圖像。具體來說,應用程序收聽麥克風並每6毫秒計算一次光譜圖,並且我想繪製更新後的光譜圖。我一直在使用java.awt.image.BufferedImage繪製光譜圖,但僅用於預先錄製的文件,所以我對圖像具有固定的寬度。對於流媒體應用程序來說,最好的方法是什麼?我不知道圖像的寬度。在Java中繪製一個越來越大的圖像

一種可能性就是創建一個新的BufferedImage,並在右邊添加一個像素並複製數據,但似乎效率低於每秒​​數百次。或者我可以從一個相對較大的寬度開始,並保持右側爲空,直到它填滿爲止,並在寬度增加一倍時,類似於ArrayList分配其大小的方式 - 我只需要每秒複製數據幾次,或每隔幾秒鐘一次左右。有更好的選擇嗎?

回答

2

我會結合使用你的建議和一個覆蓋整個圖像的子圖像並返回一個合適的首選大小的組件。

這裏是一個演示代碼顯示了我的意思是:

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.Rectangle; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.image.BufferedImage; 
import java.util.Random; 

import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.SwingUtilities; 
import javax.swing.Timer; 

public class TestRecording { 

    public static class MyPanel extends JPanel { 

     private BufferedImage buffer = new BufferedImage(3000, 100, BufferedImage.TYPE_INT_RGB); 

     private int width = 0; 

     private int lastY = 50; 

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

      if (width > 0) { 
       BufferedImage sub = buffer.getSubimage(0, 0, width, buffer.getHeight()); 
       g.drawImage(sub, 0, Math.max(0, (getHeight() - buffer.getHeight())/2), this); 
      } 

     } 

     @Override 
     public Dimension getPreferredSize() { 
      return new Dimension(width, 100); 
     } 

     protected void drawSomething() { 
      // Here need to handle growing image 
      Graphics g = buffer.getGraphics(); 
      g.setColor(Color.GREEN); 
      int y = new Random().nextInt(buffer.getHeight()); 
      g.drawLine(width, lastY, width + 1, y); 
      lastY = y; 
      width += 1; 
      Rectangle r = new Rectangle(); 
      r.x = getWidth(); 
        // Lame hack to auto-scroll to the end 
      scrollRectToVisible(r); 
      revalidate(); 
      repaint(); 
     } 
    } 

    protected void initUI() { 
     JFrame frame = new JFrame(TestRecording.class.getSimpleName()); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     final MyPanel p = new MyPanel(); 
     JScrollPane scrollpane = new JScrollPane(p); 
     frame.add(scrollpane); 
     frame.setSize(400, 200); 
     frame.setLocationRelativeTo(null); 
     frame.setVisible(true); 
     Timer t = new Timer(20, new ActionListener() { 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       p.drawSomething(); 
      } 
     }); 
     t.start(); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       new TestRecording().initUI(); 
      } 
     }); 
    } 
} 

這裏是結果:

Result

1

可能只是縮放原始圖像以獲得所需的大小。

ImagegetScaledInstance(int width, int height, int hints)方法

+0

我想要的圖像的內容是相同的大小,只需有額外的列來繪製更多的數據,是否getScaledInstance處理?我看了看,它看起來不像我可以提供的任何提示做我需要的東西。 – mattg

1

你有1)界面,你畫出來,或2)你需要真的輸出圖像文件(或流)?

if 1) 只畫出所看到的。擴展JComponent,我通常使用JPanel並覆蓋paintComponent並繪製可見的東西。 爲了提高效率,可以創建「tiles」 - 例如。具有恆定寬度的BufferedImage並從傳入的數據創建它們並僅將它們繪製爲gui。 2)類似的東西,但你可以使用一個寬度相對較低的圖像,並向其中繪製新的數據。完成後,將它「追加」到目前爲止創建的「左」圖像(最初爲空)。這樣你經常修改小圖像,而不是經常修改大圖像。 如果在填充整個右圖之前結束,則只用左邊連接填充部分。

您可以嘗試逐步增加右圖像的大小(* X,例如* 1.5或* 2),以減少使用更多內存所需的加入圖像的數量。

您可以將這些「圖像」存儲爲字節(或整數)數組(一維數組代表2D,但是按列存儲,而不是按行進行更有效的修改),這樣您可以更有效地存儲它知道每個像素需要一些不尋常的位數,因爲某些顏色永遠不會出現在結果圖像中。

如果圖片太大,請將其保存到磁盤並清除左側圖像,稍後將它們連接在一起或單獨使用。

+0

我不需要將圖像輸出到文件,只需將其顯示在屏幕上即可。我正在使用JScrollPane來顯示整個圖像,並且現在不用擔心它太大而不能適應內存。不過,我喜歡瓷磚的想法。例如,如果我有一系列將BufferedImages作爲圖標的JLabel,並將它們放在一個容器中彼此相鄰,它們是否會正確排列(例如,沒有間隙)? – mattg

+0

儘管如此,我還是需要回到以前的專欄並在譜圖上繪製標記。有了一張圖片,這非常簡單,但如果標記必須跟蹤列表中的哪個圖片需要引用,那會讓人頭疼。但是,我仍然喜歡你答案中的新想法,如果我沒有更好的想法,我會接受它。 – mattg

+0

排列:principialy它應該工作,但正如我所說的,我會在paintComponent中繪製這些圖像,並在繪製它們之後,在它們上面繪製標記。這樣你就更加負責繪畫。然後有兩種方法可以將人工繪製的圖像,仿射變換或繪畫移動到不同的座標。 – Alpedar