java - 在 Swing 中刷新 BufferedImage 像素的惯用方法

标签 java multithreading swing pixel event-dispatch-thread

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

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

new ImageIcon(bufferedImage)

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

A simple way to setting a bufferedImage into a single colored pixel without placing a image into it?

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

我认为如果我修改 BufferedImage来自非 EDT 线程的线程,并且如果未使用同步/锁定/内存屏障,则无法保证更改将可见。

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

一旦我修改了像素,我应该调用 JPanelrepaint方法?

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

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

最佳答案

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

这是通过使用 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 方法中)或减少更新次数,但这只是概念证明。

关于java - 在 Swing 中刷新 BufferedImage 像素的惯用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15627912/

相关文章:

java - 如何使用静态 block 初始化spring hibernate查询功能

java - 如何使用 selenium webdriver 查找搜索结果

java - Firebase 数据顺序

java - java中的文件处理

c++ - 在多线程iocp服务器中将连接用户列表发送给新连接的用户

c++ - boost::thread 应该在无限循环中运行并等待没有互斥量的新输入

java - 如何获取JTable的RowSorter使用的排序键的列?

Java swing - 频繁改变框架的颜色

java - JPanel 不显示我的图形

java - 从 json 获取数组不起作用