我有一个应用程序,我想绘制一个随时间宽度增长的图像。具体来说,应用程序监听麦克风并每隔6毫秒计算一个频谱图,我想在它进来时绘制更新的频谱图。我一直在使用java.awt.image.BufferedImage
绘制频谱图,但仅用于预先记录文件,所以我有一个固定的图像宽度。为流媒体应用程序执行此操作的最佳方法是什么,我不知道图像的宽度是先验的?
一种可能性是在右侧创建一个带有一个额外像素的新BufferedImage并复制数据,但这似乎效率低,每秒数百次。或者我可以从一个相对较大的宽度开始并保持右侧空白直到它填满,并且它的宽度加倍,类似于ArrayList如何分摊其大小 - 我只需要每秒复制数据几次,或每隔几秒钟,或左右。有更好的选择吗?
答案 0 :(得分: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();
}
});
}
}
结果如下:
答案 1 :(得分:1)
可以缩放原始图像以获得所需的尺寸。
Image
有getScaledInstance(int width, int height, int hints)
方法
答案 2 :(得分:1)
你有1)gui在哪里绘制它,或2)你是否需要真正输出图像文件(或流)?
如果1) 只绘制所看到的内容。扩展JComponent,我通常使用JPanel并覆盖paintComponent并绘制可见的内容。 为了提高效率,请创建“瓷砖” - 例如。具有恒定宽度的BufferedImage并从传入数据创建它们并仅绘制到gui。
2)类似的东西,但你可以使用一个宽度相对较低的图像并为其绘制新数据。当满了,“追加”到目前为止创建的“左”图像(最初为空)。这样你经常修改小图像和大图像。 如果在填充整个右图像之前结束,则仅连接左边的填充部分。
您可以尝试逐渐增加右图像的尺寸(* X,例如* 1.5或* 2),以减少加入图像的数量,但需要花费更多的内存。
您可以将这些“图像”存储为字节(或整数)数组(表示2D的1D数组,但是按列存储,而不是按行存储以进行更有效的修改),这样您就可以更有效地存储它了你需要一些不同的每像素位数,因为某些颜色永远不会出现在结果图像中。
如果图像太大,请将其保存到磁盘并清除左侧图像,然后将它们连接在一起或单独使用它们。