Java中的BufferedImage问题

时间:2017-09-10 00:32:01

标签: java image bufferedimage

我正在制作一个游戏(如“文明”),它具有我想要呈现为图像的不同图块类型。我有9个不同的16x16 png图像加载(称为Con1,Con2等),这里是我的图像加载代码:(img []是我的BufferedImage数组)

public void loadImages(){
        for(int i = 0; i < 9; i++){
            try {
                img[i] = ImageIO.read(this.getClass().getResource("Con"+i+".png"));
            }catch (Exception ex) {
                System.out.println("Missing Image");
                ex.printStackTrace();
            }
        }
    }
然后我用这段代码绘制这些图像:(t [] []是我的瓦片类型数组)

public void paint(Graphics g){
        if(loop){
            BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
            Graphics r = B.getGraphics();
            for (int x = 0; x < WIDTH; x++){
                for (int y = 0; y < HEIGHT; y++){
                    if(i[x][y] == 0){
                        if (t[x][y] == 0){
                            g.drawImage(img[0], x, y, this);
                        }
                        else if(t[x][y] == 1){
                            g.drawImage(img[1], x, y, this);
                        }
                        else if(t[x][y] == 3){
                            g.drawImage(img[3], x, y, this);
                        }
                        else if(t[x][y] == 4){
                            g.drawImage(img[4], x, y, this);
                        }
                        else if(t[x][y] == 5){
                            g.drawImage(img[5], x, y, this);
                        }
                    }
                    r.fillRect(x*SCALE, y*SCALE, SCALE, SCALE);
                }
            }

            g.drawImage(B, 0, 22, this);
        }
    }

我的问题是它在运行时没有正确显示。我得到这个图片:

enter image description here

在窗口的左上角闪烁。我应该看到的是一张类似于上面左侧部分(海洋环绕的陆地)的图像,除了大到足以填满窗户。这里有一些可运行的代码:(我不认为代码会在没有所需图像的情况下运行,但我会很感激帮助您获取所有图像。)

//imports
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame;

public class MCVCon extends JFrame implements KeyListener, MouseListener{
    //setting up variables
    public BufferedImage[] img = new BufferedImage[9];
    private final int WIDTH = 50, HEIGHT = 50;
    private boolean loop = false;
    private int SCALE = 16;
    int t[][] = new int[WIDTH][HEIGHT]; //terrain type (since I took out the terrain generation it is set to 0 or ocean)
    public MCVCon(){
        //creating the window
        super("Conqueror");
        setSize(SCALE*WIDTH, SCALE*HEIGHT+22);
        setVisible(true);
        requestFocusInWindow();
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        loadImages();
        loop = true;
        while(true){
            this.repaint();
            //delay for repaint
            try{
                Thread.sleep(50);
            }
            catch(Exception ex){
                ex.printStackTrace();
            }
        }
    }
    //load images
    public void loadImages(){
        for(int i = 0; i < 9; i++){
            try {
                img[i] = ImageIO.read(this.getClass().getResource("Con"+i+".png"));
            }catch (Exception ex) {
                System.out.println("Missing Image");
                ex.printStackTrace();
            }
        }
    }
    //paint the images
    public void paint(Graphics g){
        if(loop){
            BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
            Graphics r = B.getGraphics();
            for (int x = 0; x < WIDTH; x++){
                for (int y = 0; y < HEIGHT; y++){
                    if (t[x][y] == 0){
                        g.drawImage(img[0], x, y, this);
                    }
                    else if(t[x][y] == 1){
                        g.drawImage(img[1], x, y, this);
                    }
                    r.fillRect(x*SCALE, y*SCALE, SCALE, SCALE);
                }
            }

            g.drawImage(B, 0, 22, this);
        }
    }
    //run the program
    public static void main(String[] args) {
        new MCVCon();
    }
    //necessary overrides
    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }
}

我想知道问题可能是什么。提前谢谢。

1 个答案:

答案 0 :(得分:1)

所以,我看看你的代码,没有简单的方法可以说,但这是一个混乱,复杂的问题,这将很难隔离任何一个问题的起源,除了说,他们都互相喂食。

让我们从绘画开始......

你直接在画框上画画。出于多种原因,通常不鼓励这样做。

让我们从JFrame不是单个组件的事实开始,它由多个复合组件组成

JRootPane

这使得直接绘制本身就很危险,因为任何一个子组件都可以在没有调用框架的paint方法的情况下进行绘制。

直接绘制到一个框架也意味着你在不考虑框架的边框/装饰的情况下进行绘画,这些边框/装饰被添加到窗口的可见区域,但是等待......

setSize(SCALE*WIDTH, SCALE*HEIGHT+22);

建议你试图弥补这一点,但这是“猜测”工作,因为装饰可能会占用更多或更少的空间,具体取决于系统的配置

最后,顶级容器实际上并不是双缓冲的。

“但我正在画自己的缓冲区”你说 - 但你不是

您创建了BufferdImage并指定了Graphics上下文t r

BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics r = B.getGraphics();

但是当您绘制任何内容时,您使用的是g,这是传递给Graphics方法的paint上下文

g.drawImage(img[0], x, y, this);

这也是非常低效的,因为每次调用BufferedImage时你都会创建一个新的paint,这需要时间来创建,占用内存并给垃圾收集过程带来额外的压力。本地对象几乎可以立即获得处置

甚至不让我开始“主要的油漆循环”

您遇到的下一个问题是,您没有虚拟和现实世界空间的概念。

使用t创建一个虚拟的世界地图,它可以维护有关给定x / y坐标应使用哪个图块的信息,但是您永远不会将其映射到现实世界,而是绘制每个图块瓦片恰好位于相同的像素x / y位置,这意味着它们现在重叠,瓦片具有宽度和高度,这意味着它们在绘制到现实世界时需要偏移。

最后,我认为这是你的实际问题,是关于扩展。您可以通过多种方式应用缩放,可以在加载时预先缩放切片,这样可以大大控制缩放的“方式”,但会将您锁定为单个缩放。

您可以改为维护从主列表生成的缩放图块列表,如果比例更改,则可以更新

或者您可以直接缩放Graphics上下文。

现在,我根据您的代码创建了一个简单的示例,对上述大部分内容进行了修正。它创建了一系列10x10像素的随机彩色矩形,而不是瓷砖,但概念是相同的。

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class MCVCon extends JFrame {
    //setting up variables

    public MCVCon() {
        //creating the window
        super("Conqueror");
        add(new GamePane());
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                MCVCon frame = new MCVCon();
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
    //necessary overrides

    public class GamePane extends JPanel {

        public BufferedImage[] img = new BufferedImage[9];
        private final int width = 5, height = 5;
        private int scale = 16;
        int t[][] = new int[width][height]; //terrain type (since I took out the terrain generation it is set to 0 or ocean)
        Color[] colors = new Color[]{
            Color.RED,
            Color.BLUE,
            Color.CYAN,
            Color.DARK_GRAY,
            Color.GRAY,
            Color.GREEN,
            Color.LIGHT_GRAY,
            Color.MAGENTA,
            Color.ORANGE,
            Color.PINK,
            Color.YELLOW
        };
        int tileHeight = 10;
        int tileWidth = 10;

        public GamePane() {
            loadImages();

            Random rnd = new Random();
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    int value = rnd.nextInt(9);
                    System.out.println(value + "- " + colors[value]);
                    t[x][y] = value;
                }
            }

            Timer timer = new Timer(50, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(width * tileWidth * scale, height * tileHeight * scale);
        }

        public void loadImages() {
            for (int i = 0; i < 9; i++) {
                try {
                    img[i] = new BufferedImage(tileWidth, tileHeight, BufferedImage.TYPE_INT_RGB);
                    Graphics2D g2d = img[i].createGraphics();
                    g2d.setColor(colors[i]);
                    g2d.fill(new Rectangle(0, 0, tileWidth, tileHeight));
                    g2d.dispose();
                } catch (Exception ex) {
                    System.out.println("Missing Image");
                    ex.printStackTrace();
                }
            }
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setTransform(AffineTransform.getScaleInstance(scale, scale));
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    g2d.drawImage(img[t[x][y]], x * tileWidth, y * tileHeight, this);
                }
            }
            g2d.dispose();
        }

    }
}