Java 中的 BufferedImage 问题
Problems with BufferedImage in Java
我正在制作一款游戏(如 Civilization),其中包含我想要渲染为图像的不同图块类型。我有 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);
}
}
我的问题是当我 运行 它没有正确显示。我得到这张图片:
在 window 的左上角闪烁。我应该看到的是类似于上图左上部分的图像(被海洋包围的陆地),只是大到足以填满 window。这里有一些 运行 可用的代码:(我不认为如果没有所需的图像,代码将 运行 但我希望能帮助大家获得图像。)
//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) {
}
}
我想知道可能是什么问题。提前致谢。
所以,我查看了您的代码,没有简单的说法,但它是一团糟,有复杂的问题,这使得很难找出任何一个问题的根源,除了说,他们都互相喂养。
先从画开始吧...
您正在直接在框架上绘画。由于多种原因,通常不鼓励这样做。
让我们从以下事实开始:JFrame
不是一个单独的组件,它是由许多复合组件组成的
这使得直接绘制在本质上是危险的,因为任何一个子组件都可以在不调用框架的 paint
方法的情况下绘制。
直接绘制到框架也意味着您在绘制时不考虑框架的 borders/decorations,它们被添加到 window 的可见区域,但是等等...
setSize(SCALE*WIDTH, SCALE*HEIGHT+22);
表明您已尝试对此进行补偿,但这是 "guess" 的工作,因为根据系统的配置,装饰可能会或多或少地占用 space
最后,顶级容器实际上并不是双缓冲的。
"But I'm painting to my own buffer" 你说 - 但你不是
您创建了一个 BufferdImage
并为其分配了 Graphics
上下文 t r
BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics r = B.getGraphics();
但是当你绘制任何东西时,你正在使用 g
,这是传递给 paint
方法的 Graphics
上下文
g.drawImage(img[0], x, y, this);
这也非常低效,因为每次调用 paint
时都会创建一个新的 BufferedImage
,这需要时间来创建,占用内存并给垃圾收集带来额外压力当本地对象几乎立即符合处理条件时进行处理
甚至不让我开始 "main paint loop"
你的下一个问题是你没有虚拟世界和现实世界的概念space。
您使用 t
制作了您的世界的虚拟地图,它保留了关于给定 x/y 坐标应使用哪个图块的信息,但您从未将其映射到现实世界,而是,你在相同的像素 x/y 位置绘制每个瓦片,这意味着它们现在重叠,瓦片有宽度和高度,这意味着它们在绘制到现实世界时需要偏移。
最后,我认为您的实际问题是关于缩放。有多种方法可以应用缩放,您可以在加载时预先缩放图块,这使您可以很好地控制 "how" 它们的缩放,但会将您锁定在一个比例中。
您可以改为维护从主列表生成的缩放图块列表,如果比例发生变化,可以更新该列表
或者您可以直接缩放 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();
}
}
}
我正在制作一款游戏(如 Civilization),其中包含我想要渲染为图像的不同图块类型。我有 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);
}
}
我的问题是当我 运行 它没有正确显示。我得到这张图片:
在 window 的左上角闪烁。我应该看到的是类似于上图左上部分的图像(被海洋包围的陆地),只是大到足以填满 window。这里有一些 运行 可用的代码:(我不认为如果没有所需的图像,代码将 运行 但我希望能帮助大家获得图像。)
//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) {
}
}
我想知道可能是什么问题。提前致谢。
所以,我查看了您的代码,没有简单的说法,但它是一团糟,有复杂的问题,这使得很难找出任何一个问题的根源,除了说,他们都互相喂养。
先从画开始吧...
您正在直接在框架上绘画。由于多种原因,通常不鼓励这样做。
让我们从以下事实开始:JFrame
不是一个单独的组件,它是由许多复合组件组成的
这使得直接绘制在本质上是危险的,因为任何一个子组件都可以在不调用框架的 paint
方法的情况下绘制。
直接绘制到框架也意味着您在绘制时不考虑框架的 borders/decorations,它们被添加到 window 的可见区域,但是等等...
setSize(SCALE*WIDTH, SCALE*HEIGHT+22);
表明您已尝试对此进行补偿,但这是 "guess" 的工作,因为根据系统的配置,装饰可能会或多或少地占用 space
最后,顶级容器实际上并不是双缓冲的。
"But I'm painting to my own buffer" 你说 - 但你不是
您创建了一个 BufferdImage
并为其分配了 Graphics
上下文 t r
BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics r = B.getGraphics();
但是当你绘制任何东西时,你正在使用 g
,这是传递给 paint
方法的 Graphics
上下文
g.drawImage(img[0], x, y, this);
这也非常低效,因为每次调用 paint
时都会创建一个新的 BufferedImage
,这需要时间来创建,占用内存并给垃圾收集带来额外压力当本地对象几乎立即符合处理条件时进行处理
甚至不让我开始 "main paint loop"
你的下一个问题是你没有虚拟世界和现实世界的概念space。
您使用 t
制作了您的世界的虚拟地图,它保留了关于给定 x/y 坐标应使用哪个图块的信息,但您从未将其映射到现实世界,而是,你在相同的像素 x/y 位置绘制每个瓦片,这意味着它们现在重叠,瓦片有宽度和高度,这意味着它们在绘制到现实世界时需要偏移。
最后,我认为您的实际问题是关于缩放。有多种方法可以应用缩放,您可以在加载时预先缩放图块,这使您可以很好地控制 "how" 它们的缩放,但会将您锁定在一个比例中。
您可以改为维护从主列表生成的缩放图块列表,如果比例发生变化,可以更新该列表
或者您可以直接缩放 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();
}
}
}