2

私はBufferedImage定期的にピクセルを変更する必要がある部分で構成されている Swing UI を持っています。

を作成し、それを渡してそのメソッドJLabelを呼び出すのが一般的です。setIconJLabel

new ImageIcon(bufferedImage)

たとえば、これは、20,000 件以上の担当者を持つユーザーによって、ここで受け入れられた回答で BufferedImage が画面に表示される方法です。

画像を配置せずに bufferedImage を単一の色付きピクセルに設定する簡単な方法は?

だから私は同じことをしています: BufferedImage を含む ImageIcon に設定されたアイコンを持つ JLabel と私の質問はマルチスレッドと Swing の再描画に関連しています:BufferedImage変更が保証されるように内部のピクセルを変更するにはどうすればよいですか?ユーザーに表示するには?

BufferedImageEDT スレッドではないスレッドからを変更し、同期/ロック/メモリ バリアが使用されていない場合、変更が表示されることは保証されません。

EDT でピクセルを直接変更することはできますか?

ピクセルを変更したら、JPanelrepaintメソッドを呼び出す必要がありますか?

私の変更が常に表示されることが保証されますか? (ここでの「目に見える」とは、文字通り「目に見える」という意味であり、画面に表示される場合と「EDT に見える」場合の両方を意味します)。

私はむしろこれをシンプルに保ち、Swing 以外の API や 3D API などを使用したくありません。

4

2 に答える 2

2

質問を正しく理解していれば、この例では を取り、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メソッド内の) 最後の画像のみに関心があるか、更新の数を減らすことができますが、これは単なる概念実証です。

于 2013-03-26T02:41:39.247 に答える
2

EDT でピクセルを直接変更することはできますか?

いいえ。いくつかの代替案がここに記載されていますが、主な例では、オフスクリーン バッファを別のスレッドで時間の経過とともに進化するモデルとして扱います。は、更新が利用可能であるjavax.swing.Timerことをリスニング ビューに定期的に通知し、共有データへのアクセスを同期します。モデル内の任意のピクセルが変更される可能性があるため、ビュー全体がティックごとに再描画されます。

更新されたピクセルが幾何学的に局所化されている場合はdrawImage()、画面の一部だけを更新するバリエーションを試すことができます。ただし、不注意によるスケーリングは避けてください。

于 2013-03-26T04:30:41.823 に答える