从JFilechooser读取图像后,我试图逐个读取图像的像素,并在经过一些延迟后以顺序方式将其显示到JPanel。无法更新JPanel的背景。
public class ImageMain extends JFrame implements ActionListener {
/**
*
*/
private static final long serialVersionUID = 2916361361443483318L;
private JFileChooser fc = null;
private JMenuItem item1, item2;
private BufferedImage image = null;
private JPanel panel = null;
private int width = 0;
private int height = 0;
private BorderLayout card;
private Container contentPane;
//private int loopcount = 0;
//private int counter = 0;
public ImageMain() {
JFrame frame = new JFrame("Image Extraction Tool");
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = frame.getContentPane();
panel = new JPanel();
card = new BorderLayout();
panel.setLayout(card);
panel.setBackground(Color.white);
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Menu");
menuBar.add(menu);
item1 = new JMenuItem("Browse an image");
item2 = new JMenuItem("Exit");
item1.addActionListener(this);
item2.addActionListener(this);
menu.add(item1);
menu.add(item2);
frame.setJMenuBar(menuBar);
contentPane.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
ImageMain img = new ImageMain();
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == item1) {
if (fc == null)
fc = new JFileChooser();
int retVal = fc.showOpenDialog(null);
if (retVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
try {
image = ImageIO.read(file);
height = image.getHeight();
width = image.getWidth();
// final int[][] pixelData = new int[height * width][3];
// int[] rgb;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
System.out.println(i + " " + j);
Color c = new Color(image.getRGB(j, i));
panel.setBackground(c);
panel.invalidate();
panel.validate();
panel.repaint();
}
}
} catch (IOException e1) {
System.out.println("IO::" + e1.getMessage());
} catch (Exception e1) {
System.out.println("Exception::" + e1.getMessage());
}
}
}
if (e.getSource() == item2) {
System.exit(0);
}
}}
在ActionPerformed中,我通过读取RGB值获得了Color对象,然后我一直在向JApplet显示它们。如果有更好的方法来实现这一点,建议是受欢迎的。
提前谢谢。
主要问题是在事件调度线程的上下文中执行长时间运行的任务,该线程负责处理重新绘制请求。
@Override
public void actionPerformed(ActionEvent e) {
//...
// Nothing will be updated until after the
// actionPerformed method exists
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
System.out.println(i + " " + j);
Color c = new Color(image.getRGB(j, i));
panel.setBackground(c);
panel.invalidate();
panel.validate();
panel.repaint();
}
}
您遇到的另一个问题是,您只需要在EDT 的上下文中修改UI的状态
根据您的确切需求,您可以使用SwingWorker
,这将允许您在EDT上下文中更新UI的同时处理背景中的像素,但是,由于SwingWorker
合并了它的更新,您可能会错过颜色更改。
一个更好的解决方案可能是使用java.swing.Timer
,它允许您在指定的时间段触发更新,这些更新是在EDT的上下文中触发的。
有关更多详细信息,请参阅Swing中的并发。。。
更新示例
为了绘制像素,你需要一些东西来绘制它们。现在,你可以简单地将你想要绘制的每个像素添加到一个数组中,并在每次需要重新绘制组件时循环该数组,但这有点昂贵。。。
相反,有一个某种类型的后备缓冲区会更简单,在上面绘制像素,然后将缓冲区绘制到组件上,这应该会更快。
基本上,允许您提供预期图像的高度和宽度,然后根据需要提供每个像素。。
public class ImagePane extends JPanel {
private BufferedImage img;
public ImagePane() {
}
public void reset(int width, int height) {
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
revalidate();
}
public void reset() {
img = null;
revalidate();
}
public void setPixelAt(int x, int y, int pixel) {
img.setRGB(x, y, pixel);
}
@Override
public Dimension getPreferredSize() {
return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(), img.getHeight());
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (img != null) {
int x = (getWidth() - img.getWidth()) / 2;
int y = (getHeight() - img.getHeight()) / 2;
g2d.drawImage(img, x, y, this);
}
g2d.dispose();
}
}
查看"执行自定义绘画"以了解更多详细信息。。。
然后你需要一些方法来处理原始图像并更新图像面板。。。现在,根据更新的要求,我将使用SwingWorker
,原因是SwingWorker
可以缓存传递回EDT的内容,这允许后台线程继续处理和缓存输出,直到EDT(和系统)准备好处理它…
public class PixelExposerWorker extends SwingWorker<Void, Pixel> {
private final BufferedImage img;
private final ImagePane imagePane;
private final List<Point> points;
public PixelExposerWorker(BufferedImage img, ImagePane imagePane) {
this.img = img;
this.imagePane = imagePane;
points = new ArrayList<>(img.getWidth() * img.getHeight());
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
points.add(new Point(x, y));
}
}
}
@Override
protected void process(List<Pixel> chunks) {
System.out.println("Publish " + chunks.size());
for (Pixel pixel : chunks) {
imagePane.setPixelAt(pixel.getX(), pixel.getY(), pixel.getColor());
}
imagePane.repaint();
}
@Override
protected Void doInBackground() throws Exception {
int pixelCount = (int) (points.size() * 0.005);
while (!points.isEmpty()) {
for (int count = 0; count < pixelCount && !points.isEmpty(); count++) {
int index = (int) (Math.random() * (points.size() - 1));
Point p = points.remove(index);
Pixel pixel = new Pixel(p.x, p.y, img.getRGB(p.x, p.y));
publish(pixel);
}
Thread.yield();
}
return null;
}
}
基本上,这个SwingWorker
构建了一个"像素点"的List
,它使用列表从列表中随机删除点,生成一个虚拟Pixel
和publish
,返回EDT进行处理。工作人员一次处理大约0.5%的像素,这意味着工作总是试图将"一堆"像素发送回EDT,而不是一次发送一个。
最后,它将yield
允许其他线程运行(希望EDT自己更新)
650x975的图像需要大约1米和10秒才能完全渲染
完整的代码。。。
import core.util.StopWatch;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;
public class PixelShower implements ActionListener {
private static final long serialVersionUID = 2916361361443483318L;
private JFileChooser fc = null;
private JMenuItem item1, item2;
private BufferedImage image = null;
private ImagePane panel = null;
private int width = 0;
private int height = 0;
private BorderLayout card;
private Container contentPane;
public PixelShower() {
JFrame frame = new JFrame("Image Extraction Tool");
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = frame.getContentPane();
panel = new ImagePane();
card = new BorderLayout();
panel.setLayout(card);
panel.setBackground(Color.white);
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Menu");
menuBar.add(menu);
item1 = new JMenuItem("Browse an image");
item2 = new JMenuItem("Exit");
item1.addActionListener(this);
item2.addActionListener(this);
menu.add(item1);
menu.add(item2);
frame.setJMenuBar(menuBar);
contentPane.add(panel);
frame.setVisible(true);
}
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
PixelShower img = new PixelShower();
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == item1) {
if (fc == null) {
fc = new JFileChooser();
}
int retVal = fc.showOpenDialog(null);
if (retVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
try {
image = ImageIO.read(file);
panel.reset(image.getWidth(), image.getHeight());
PixelExposerWorker worker = new PixelExposerWorker(image, panel);
worker.execute();
} catch (IOException e1) {
e1.printStackTrace();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
if (e.getSource() == item2) {
System.exit(0);
}
}
public class ImagePane extends JPanel {
private BufferedImage img;
public ImagePane() {
}
public void reset(int width, int height) {
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
revalidate();
}
public void reset() {
img = null;
revalidate();
}
public void setPixelAt(int x, int y, int pixel) {
img.setRGB(x, y, pixel);
}
@Override
public Dimension getPreferredSize() {
return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(), img.getHeight());
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (img != null) {
int x = (getWidth() - img.getWidth()) / 2;
int y = (getHeight() - img.getHeight()) / 2;
g2d.drawImage(img, x, y, this);
}
g2d.dispose();
}
}
public class Pixel {
private int x;
private int y;
private int color;
public Pixel(int x, int y, int color) {
this.x = x;
this.y = y;
this.color = color;
}
public int getColor() {
return color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
public class PixelExposerWorker extends SwingWorker<Void, Pixel> {
private final BufferedImage img;
private final ImagePane imagePane;
private final List<Point> points;
public PixelExposerWorker(BufferedImage img, ImagePane imagePane) {
this.img = img;
this.imagePane = imagePane;
points = new ArrayList<>(img.getWidth() * img.getHeight());
for (int x = 0; x < img.getWidth(); x++) {
for (int y = 0; y < img.getHeight(); y++) {
points.add(new Point(x, y));
}
}
}
@Override
protected void process(List<Pixel> chunks) {
System.out.println("Publish " + chunks.size());
for (Pixel pixel : chunks) {
imagePane.setPixelAt(pixel.getX(), pixel.getY(), pixel.getColor());
}
imagePane.repaint();
}
@Override
protected Void doInBackground() throws Exception {
StopWatch sw = StopWatch.newInstance().start();
int pixelCount = (int) (points.size() * 0.005);
System.out.println("pixelCount = " + pixelCount + "; " + points.size());
while (!points.isEmpty()) {
StopWatch sw1 = StopWatch.newInstance().start();
for (int count = 0; count < pixelCount && !points.isEmpty(); count++) {
int index = (int) (Math.random() * (points.size() - 1));
Point p = points.remove(index);
Pixel pixel = new Pixel(p.x, p.y, img.getRGB(p.x, p.y));
publish(pixel);
}
Thread.yield();
}
System.out.println("Took " + sw.stop());
return null;
}
}
}