2

我有一个 Swing UI,其中一部分由BufferedImage我需要定期修改像素的部分组成。

创建一个并通过传递它JLabel来调用该setIcon方法似乎很常见:JLabel

new ImageIcon(bufferedImage)

例如,这是 20K+ 代表的用户在此处接受的答案中在屏幕上显示 BufferedImage 的方式:

将 bufferedImage 设置为单个彩色像素而不将图像放入其中的简单方法?

所以我也在做同样的事情:一个 JLabel ,它的 Icon 设置为 ImageIcon ,其中包含 BufferedImage ,我的问题与多线程和 Swing 重绘有关:我应该如何修改内部的像素BufferedImage以保证更改出现在用户面前?

我认为,如果我BufferedImage从一个不是 EDT 线程的线程修改,并且如果没有使用同步/锁定/内存屏障,那么没有任何东西可以保证更改将是可见的。

我可以直接在 EDT 上直接修改像素吗?

一旦我修改了像素,我应该调用JPanel'repaint方法吗?

这能保证我的更改始终可见吗?(这里的“可见”字面意思是“可见”,如在屏幕上可见和“对 EDT 可见”)。

我宁愿保持简单,也不想使用非 Swing API 或 3D API 等。

4

2 回答 2

2

Can I simply modify the pixels directly on the EDT?

No. Several alternatives are mentioned here, but the main example treats the offscreen buffer as a model that evolves over time in a separate thread. A javax.swing.Timer periodically notifies the listening view that an update is available, synchronizing access to the shared data. Because any pixel in the model may change, the entire view is redrawn at each tick.

If the updated pixels are geometrically localized, you can try a variation of drawImage() that updates just a portion of the screen; but try to avoid inadvertent scaling.

于 2013-03-26T04:30:41.823 回答
2

如果我正确理解了这个问题,则此示例采用 aBufferedImage并将该图像中的所有像素替换为红色像素。

这是通过使用SwingWorker. 基本上,这会复制原始图像并遍历像素数据,更新每个像素。然后,它通过复制该图像与 UI 重新同步该图像。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class PixelMe {

    public static void main(String[] args) {
        new PixelMe();
    }

    public PixelMe() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

        });
    }

    public BufferedImage createImage() {

        BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, 100, 100);
        g.dispose();

        return image;

    }

    public class TestPane extends JPanel {

        private JLabel label;
        private BufferedImage master;

        public TestPane() {
            setLayout(new BorderLayout());
            label = new JLabel(new ImageIcon(createImage()));
            add(label);

            JButton update = new JButton("Update");
            add(update, BorderLayout.SOUTH);
            update.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    BufferedImage image = (BufferedImage) ((ImageIcon)label.getIcon()).getImage();
                    new UpdateWorker(image, label).execute();
                }

            });
        }

    }

    public class UpdateWorker extends SwingWorker<BufferedImage, BufferedImage> {

        private BufferedImage copy;
        private JLabel target;

        public UpdateWorker(BufferedImage master, JLabel target) {
            this.target = target;
            copy = makeCopy(master);
        }

        public BufferedImage makeCopy(BufferedImage master) {
            BufferedImage image = new BufferedImage(master.getWidth(), master.getHeight(), master.getType());
            Graphics2D g = image.createGraphics();
            g.drawImage(master, 0, 0, null);
            g.dispose();
            return image;
        }

        @Override
        protected void process(List<BufferedImage> chunks) {
            target.setIcon(new ImageIcon(chunks.get(chunks.size() - 1)));
        }

        @Override
        protected BufferedImage doInBackground() throws Exception {
            int pixel = Color.RED.getRGB();
            for (int row = 0; row < copy.getHeight(); row++) {
                for (int col = 0; col < copy.getWidth(); col++) {
                    copy.setRGB(col, row, pixel);
                    publish(makeCopy(copy));
                }
            }
            return null;
        }
    }
}

应该注意的是,这是一个非常昂贵的示例,因为BufferedImage每次更改像素都会创建一个新示例。您可以建立一个图像池并使用它们,因为实际上只对最后一个图像感兴趣(在process方法内)或减少更新的数量,但这只是一个概念证明。

于 2013-03-26T02:41:39.247 回答