2013-11-23 62 views
0

從JFilechooser讀取圖像後,我試圖逐個讀取圖像的像素,並在按順序延遲一段時間後將其顯示到JPanel。無法更新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。如果有更好的方法來實現這一點,歡迎提出建議。

在此先感謝。

+0

'setBackground(...)'方法將整個JPanel的背景顏色設置爲傳入的顏色,它不會設置任何「逐個像素」的東西。此外,根據Swing的線程規則,將顯示的唯一顏色將是傳遞給'setBackground(...)'的最後一個顏色。 –

回答

2

主要問題是您在Event Dispatching Thread的上下文中執行長時間運行的任務,該任務負責處理重新繪製請求等。

@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上下文中觸發的指定時間段的更新。

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

Performing Custom Painting瞭解更多詳情請看...

然後你需要一些方法來處理原始圖像和更新圖像面板...現在,根據更新的要求,我會使用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建立的「像素點」 a List,它使用該列表中隨機從列表中刪除點,生成虛擬Pixelpublish回到EDT進行處理。工作人員一次處理大約0.5%的像素,這意味着工作總是試圖將一束「像素」發回到EDT,而不是一次一個。

最後,它將yield允許其他線程運行(與希望的EDT更新它自己)

的650x975的圖像需要大約1M和10S充分渲染器

並全面代碼...

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

    } 
} 
+0

我是新來的鞦韆。我想在我的解決方案中使用javax.swing.Timer。我需要在我的源代碼中更改什麼?提前致謝。 – user2803495

+0

你試圖實現什麼? – MadProgrammer

+0

我想要以幻燈片(全屏)方式顯示圖像像素(逐個像素)。謝謝 – user2803495