Draw an image of growing size in Java

佐手、 提交于 2020-01-05 17:38:12

问题


I have an application where I want to draw an image that grows in width over time. Specifically, the application listens to the microphone and computes a spectrogram every 6 milliseconds, and I want to draw the updated spectrogram when it comes in. I've been using java.awt.image.BufferedImage to draw the spectrograms, but only for pre-recorded files, so I have a fixed width for the image. What's the best way to do this for a streaming application, where I don't know a priori the width of the image?

One possibility is to just create a new BufferedImage with one extra pixel on the right and copy the data over, but that seems inefficient to do hundreds of times per second. Or I could start with a relatively large width and keep the right side blank until it fills up, and double the width when it does, similar to how an ArrayList amortizes its size - I would only have to copy the data a few times per second, or once every few seconds, or so. Is there a better option?


回答1:


I would use a combination of what you suggest and an overriding component that only paints a subimage of the total image and returns an appropriate preferred size.

Here is a demo code which shows what I mean:

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

And here is the result:




回答2:


May be just scale the original image to get desired size.

Image has getScaledInstance(int width, int height, int hints) method




回答3:


Do you have 1) gui where you draw it, or 2) do you need to realy output image file (or stream)?

if 1) Draw only what is seen. Extend JComponent, I usually use JPanel and override paintComponent and paint what is visible. To make it more efficient, create "tiles" - list of eg. BufferedImage of constant width and create them from incoming data and draw to gui only them.

2) Something similar, but you can use one image with relatively low width and draw new data to it. When full, "append" it to so far created "left" image (initially empty). This way you frequently modify small image and big not so frequently. If end comes before filling whole right image, join only filled part with left.

You could try to gradually increase size of right image (*X, eg *1.5 or *2) to reduce number of joining images at cost of using more memory.

You could store those "images" as byte(or int) array (1D array representing 2D, but store it by columns, not by rows for more efficient modifications), this way you can store it bit more efficiently if you know that you need some unusual ammount of bits per pixels, because some colors will never be in result image.

If image gets too big, save it to disk and clear your left image and later join them together or use them separately.



来源:https://stackoverflow.com/questions/14218840/draw-an-image-of-growing-size-in-java

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!