Java读取并加载图像

时间:2013-03-30 12:31:31

标签: java image load

我刚刚完成一个扫雷游戏,一切都完美无缺,除了一件事,将图像加载到游戏中的速度。我注意到如果我在游戏图像中有大量的单元格,鼠标点击单元格后加载速度非常慢,如果我的单元格数量较少,则速度会变快。是否有任何其他方式可以使加载图像比我使用的更快?这是我用来将图像加载到游戏中的方法:

   private void draw(Graphics g) {
        BufferedImage gRec =null, flag=null, mine=null, aCount0=null,
            aCount1=null,aCount2 =null,aCount3 =null,aCount4 =null,aCount5 =null,
            aCount6 =null,aCount7 =null,aCount8 = null;
        try {
           gRec = ImageIO.read(new File("/Users/msa_666/Desktop/blank.gif"));
                flag = ImageIO.read(new File("/Users/msa_666/Desktop/bombflagged.gif"));
                mine = ImageIO.read(new File("/Users/msa_666/Desktop/bombdeath.gif"));
                flag = ImageIO.read(new File("/Users/msa_666/Desktop/bombflagged.gif"));
                aCount0 = ImageIO.read(new File("/Users/msa_666/Desktop/open0.gif"));
                aCount1 = ImageIO.read(new File("/Users/msa_666/Desktop/open1.gif"));
                aCount2 = ImageIO.read(new File("/Users/msa_666/Desktop/open2.gif"));
                aCount3 = ImageIO.read(new File("/Users/msa_666/Desktop/open3.gif"));
                aCount4 = ImageIO.read(new File("/Users/msa_666/Desktop/open4.gif"));
                aCount5 = ImageIO.read(new File("/Users/msa_666/Desktop/open5.gif"));
                aCount6 = ImageIO.read(new File("/Users/msa_666/Desktop/open6.gif"));
                aCount7 = ImageIO.read(new File("/Users/msa_666/Desktop/open7.gif"));
                aCount8 = ImageIO.read(new File("/Users/msa_666/Desktop/open8.gif"));
                }
                 catch (IOException e) { 
              e.printStackTrace();
           } 


        if (getCovered() == true && getMarked () == false) {    // gray rectangle
           g.drawImage(gRec,getX(),getY(),w,h,null);

        }
        else if (getCovered()==true && getMarked() ==  true) {  //flag
           g.drawImage(flag,getX(),getY(),w,h,null);

        }
        else if (getCovered()== false && getMined()== true){        //mine
           g.drawImage(mine,getX(),getY(),w,h,null);

        }
        else if ( getCovered() == false && getMined() == false) {   // adjacency count image
            switch (getAdjCount()){
                case 0:
           g.drawImage(aCount0,getX(),getY(),w,h,null);
                break;
                case 1:
           g.drawImage(aCount1,getX(),getY(),w,h,null);
                break;
                case 2:
           g.drawImage(aCount2,getX(),getY(),w,h,null);
                break;
                case 3:
           g.drawImage(aCount3,getX(),getY(),w,h,null);
                break;

                case 4:
           g.drawImage(aCount4,getX(),getY(),w,h,null);
                break;
                case 5:
           g.drawImage(aCount5,getX(),getY(),w,h,null);
                break;
                case 6:
           g.drawImage(aCount6,getX(),getY(),w,h,null);
                break;
                case 7:
           g.drawImage(aCount7,getX(),getY(),w,h,null);
                break;
                case 8:
           g.drawImage(aCount8,getX(),getY(),w,h,null);
                break;


        }
     }
    }

这是鼠标监听器,点击它后重绘每个单元格:

   public void mouseClicked(MouseEvent e) {
      int sRow, sCol;
      sRow= e.getX() / cellHeight;
      sCol = e.getY()/ cellWidth;
      System.out.println(e.getX() +"," +sCol);
      System.out.println(e.getY()+","+sRow);
      if (e.getButton() == MouseEvent.BUTTON1) {
        if( cells[sRow][sCol].getMarked() == false)     
           uncoverCell(cells[sRow][sCol]);
          // cells[sRow][sCol].setCovered(false);
        System.out.println(cells[sRow][sCol].getMined());
        repaint();
     }
     else if (e.getButton() == MouseEvent.BUTTON2) {
     }
     else if (e.getButton() == MouseEvent.BUTTON3) {
        if (cells[sRow][sCol].getMarked() == false){
           cells[sRow][sCol].setMarked(true);
                       repaint();

        }
        else {
           cells[sRow][sCol].setMarked(false);          
           repaint();           
        }
     }

     if (allMinesMarked() && allNonMinesUncovered()){
        System.out.println("You Win");
     }
  }


  public void paintComponent(Graphics g) { 
     for ( int i=0 ; i <rowCount; i++ ) {
        for (int j=0; j<columnCount; j++) {
           cells[i][j].draw(g);
        }
     }

  }

2 个答案:

答案 0 :(得分:2)

你需要告诉我们:

  • draw(...)被叫到哪里?
  • 如何获取传递到draw方法参数的图形对象g?

我在这里猜测,因为我们没有所有相关代码,但看起来好像每次要显示图像时都要重新读取图像。如果是这样,请不要这样做。在程序开头只读取 一次 中的图像,然后使用图像或者更好的ImageIcons,在您需要时获得。

修改
感谢您发布更多代码,这实际上证实了我的怀疑:您每次重新绘制GUI时都会重新阅读图像文件。这非常低效,并且会使您的程序变慢。同样,您应该将图像读入您的程序一次,然后多次使用它们。

我自己从图像中创建ImageIcons,然后在JLabel中显示它们。当需要交换图像时,只需在JLabel上调用setIcon(...)即可。这样就没有必要弄乱paintComponent(...)

编辑2
例如(编译并运行它):

import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.*;

public class SwapIcons {
   private static final int CELL_SIDE_COUNT = 3;
   private ImageCell[] imageCells = new ImageCell[CELL_SIDE_COUNT * CELL_SIDE_COUNT];
   private JPanel mainPanel = new JPanel();

   public SwapIcons(final GetImages getImages) {
      mainPanel.setLayout(new GridLayout(CELL_SIDE_COUNT, CELL_SIDE_COUNT));
      mainPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));

      for (int i = 0; i < imageCells.length; i++) {
         imageCells[i] = new ImageCell(getImages);
         mainPanel.add(imageCells[i].getImgLabel());
      }
   }

   public JComponent getMainComponent() {
      return mainPanel;
   }

   private static void createAndShowGui(GetImages getImages) {
      SwapIcons swapIcons = new SwapIcons(getImages);

      JFrame frame = new JFrame("Click on Icons to Change");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(swapIcons.getMainComponent());
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      try {
         final GetImages getImages = new GetImages();
         SwingUtilities.invokeLater(new Runnable() {
            public void run() {
               createAndShowGui(getImages);
            }
         });
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

class ImageCell {
   private JLabel imgLabel = new JLabel();
   private GetImages getImages;
   private int iconIndex = 0;

   public ImageCell(final GetImages getImages) {
      this.getImages = getImages;
      imgLabel.setIcon(getImages.getIcon(0));
      imgLabel.addMouseListener(new MyMouseListener());
   }

   public JLabel getImgLabel() {
      return imgLabel;
   }

   private class MyMouseListener extends MouseAdapter {
      @Override
      public void mousePressed(MouseEvent e) {
         iconIndex++;
         iconIndex %= getImages.getIconListSize();
         imgLabel.setIcon(getImages.getIcon(iconIndex));
      }
   }
}

// Simply gets a SpriteSheet and subdivides it into a List of ImageIcons
class GetImages {
   private static final String SPRITE_PATH = "http://th02.deviantart.net/"
         + "fs70/PRE/i/2011/169/0/8/blue_player_sprite_sheet_by_resetado-d3j7zba.png";
   public static final int SPRITE_ROWS = 6;
   public static final int SPRITE_COLS = 6;
   public static final int SPRITE_CELLS = 35;

   private List<ImageIcon> iconList = new ArrayList<ImageIcon>();

   public GetImages() throws IOException {
      URL imgUrl = new URL(SPRITE_PATH);
      BufferedImage mainImage = ImageIO.read(imgUrl);

      for (int i = 0; i < SPRITE_CELLS; i++) {
         int row = i / SPRITE_COLS;
         int col = i % SPRITE_COLS;
         int x = (int) (((double) mainImage.getWidth() * col) / SPRITE_COLS);
         int y = (int) ((double) (mainImage.getHeight() * row) / SPRITE_ROWS);
         int w = (int) ((double) mainImage.getWidth() / SPRITE_COLS);
         int h = (int) ((double) mainImage.getHeight() / SPRITE_ROWS);
         BufferedImage img = mainImage.getSubimage(x, y, w, h);
         ImageIcon icon = new ImageIcon(img);
         iconList.add(icon);
      }
   }

   // get the Icon from the List at index position
   public ImageIcon getIcon(int index) {
      if (index < 0 || index >= iconList.size()) {
         throw new ArrayIndexOutOfBoundsException(index);
      }

      return iconList.get(index);
   }

   public int getIconListSize() {
      return iconList.size();
   }

}

答案 1 :(得分:0)

Hovercraft Full Of Eels答案很好并且会起作用。 并且适用于独立应用程序,但对于applet或web start应用程序可以通过使用一个大图像然后将其部分复制到可见的图形对象进一步优化,在java.awt.Graphics对象中思考网格和使用函数(来自javadoc):

public abstract boolean drawImage(Image img,                 int dx1,                 int dy1,                 int dx2,                 int dy2,                 int sx1,                 int sy1,                 int sx2,                 int sy2,                 ImageObserver观察者)

绘制与当前可用的指定图像一样多的指定区域,即时缩放它以适合目标可绘制表面的指定区域。透明像素不会影响已存在的任何像素。

此方法在所有情况下都会立即返回,即使要绘制的图像区域尚未针对当前输出设备进行缩放,抖动和转换。如果当前输出表示尚未完成,则drawImage返回false。随着更多图像变得可用,加载图像的过程会通知指定的图像观察者。

此方法始终使用图像的未缩放版本来渲染缩放的矩形,并在运行中执行所需的缩放。它不会为此操作使用图像的缓存缩放版本。执行从源到目的地的图像缩放,使得源矩形的第一坐标被映射到目标矩形的第一坐标,并且第二源坐标被映射到第二目标坐标。子图像根据需要进行缩放和翻转,以保留这些映射。

参数:     img - 要绘制的指定图像。如果img为null,则此方法不执行任何操作。     dx1 - 目标矩形的第一个角的x坐标。     dy1 - 目标矩形的第一个角的y坐标。     dx2 - 目标矩形的第二个角的x坐标。     dy2 - 目标矩形的第二个角的y坐标。     sx1 - 源矩形第一个角的x坐标。     sy1 - 源矩形的第一个角的y坐标。     sx2 - 源矩形第二个角的x坐标。     sy2 - 源矩形第二个角的y坐标。     observer - 在缩放和转换更多图像时要通知的对象。 返回:     如果图像像素仍在变化,则为false;是的,不然。

这样做更好,因为通过互联网建立新连接并下载图像需要几秒钟,因此如果您有一张主图像,其中包含大表中的所有子图像,那么下载,加载和渲染的总时间将为少一些。从一个区域复制的额外逻辑可能是.1KB的jar文件空间:)