在Java中绘制一个不断增长的大小的图像

时间:2013-01-08 15:50:21

标签: java image swing awt

我有一个应用程序,我想绘制一个随时间宽度增长的图像。具体来说,应用程序监听麦克风并每隔6毫秒计算一个频谱图,我想在它进来时绘制更新的频谱图。我一直在使用java.awt.image.BufferedImage绘制频谱图,但仅用于预先记录文件,所以我有一个固定的图像宽度。为流媒体应用程序执行此操作的最佳方法是什么,我不知道图像的宽度是先验的?

一种可能性是在右侧创建一个带有一个额外像素的新BufferedImage并复制数据,但这似乎效率低,每秒数百次。或者我可以从一个相对较大的宽度开始并保持右侧空白直到它填满,并且它的宽度加倍,类似于ArrayList如何分摊其大小 - 我只需要每秒复制数据几次,或每隔几秒钟,或左右。有更好的选择吗?

3 个答案:

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

结果如下:

Result

答案 1 :(得分:1)

可以缩放原始图像以获得所需的尺寸。

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

答案 2 :(得分:1)

你有1)gui在哪里绘制它,或2)你是否需要真正输出图像文件(或流)?

如果1) 只绘制所看到的内容。扩展JComponent,我通常使用JPanel并覆盖paintComponent并绘制可见的内容。 为了提高效率,请创建“瓷砖” - 例如。具有恒定宽度的BufferedImage并从传入数据创建它们并仅绘制到gui。

2)类似的东西,但你可以使用一个宽度相对较低的图像并为其绘制新数据。当满了,“追加”到目前为止创建的“左”图像(最初为空)。这样你经常修改小图像和大图像。 如果在填充整个右图像之前结束,则仅连接左边的填充部分。

您可以尝试逐渐增加右图像的尺寸(* X,例如* 1.5或* 2),以减少加入图像的数量,但需要花费更多的内存。

您可以将这些“图像”存储为字节(或整数)数组(表示2D的1D数组,但是按列存储,而不是按行存储以进行更有效的修改),这样您就可以更有效地存储它了你需要一些不同的每像素位数,因为某些颜色永远不会出现在结果图像中。

如果图像太大,请将其保存到磁盘并清除左侧图像,然后将它们连接在一起或单独使用它们。