如何快速有效地设置 BufferedImage
的所有像素?透明化,以便我可以简单地为每一帧重新绘制我想要的 Sprite 图形?
我正在用 Java 设计一个简单的游戏引擎来更新背景和前景 BufferedImage
并将它们绘制成复合 VolatileImage
为了有效缩放,绘制到 JPanel
.这种可扩展模型允许我添加更多层并迭代每个绘图层。
我将我的应用程序简化为下面给出的一个类来演示我的问题。使用箭头键在图像上移动一个红色方 block 。挑战在于我想将更新游戏图形与将合成图形绘制到游戏引擎分离。我已经研究了这个问题的看似彻底的答案,但无法弄清楚如何将它们应用到我的应用程序中:
这里是没有正确清除像素的临界区。注释掉的部分来 self 已经阅读过的堆栈溢出答案,但它们将背景绘制为不透明的黑色或白色。我知道 foregroundImage
在我的实现中从透明像素开始,因为您可以看到 backgroundImage
的随机像素噪声应用程序开始时在红色 Sprite 后面。此时图像没有被清除,所以之前绘制的图像仍然存在。
/** Update the foregroundGraphics. */
private void updateGraphics(){
Graphics2D fgGraphics = (Graphics2D) foregroundImage.getGraphics();
// set image pixels to transparent
//fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
//fgGraphics.setColor(new Color(0,0,0,0));
//fgGraphics.clearRect(0, 0, width, height);
//fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
// draw again.
fgGraphics.setColor(Color.RED);
fgGraphics.fillRect(sx, sy, spriteSize, spriteSize);
fgGraphics.dispose();
}
这是我的整个示例代码:
/**
* The goal is to draw two BufferedImages quickly onto a scalable JPanel, using
* a VolatileImage as a composite.
*/
public class Example extends JPanel implements Runnable, KeyListener
{
private static final long serialVersionUID = 1L;
private int width;
private int height;
private Object imageLock;
private Random random;
private JFrame frame;
private Container contentPane;
private BufferedImage backgroundImage;
private BufferedImage foregroundImage;
private VolatileImage compositeImage;
private Graphics2D compositeGraphics;
private int[] backgroundPixels;
private int[] foregroundPixels;
// throttle the framerate.
private long prevUpdate;
private int frameRate;
private int maximumWait;
// movement values.
private int speed;
private int sx;
private int sy;
private int dx;
private int dy;
private int spriteSize;
/** Setup required fields. */
public Example(){
width = 512;
height = 288;
super.setPreferredSize(new Dimension(width, height));
imageLock = new Object();
random = new Random();
frame = new JFrame("BufferedImage Example");
frame.addKeyListener(this);
contentPane = frame.getContentPane();
contentPane.add(this, BorderLayout.CENTER);
// used to create hardware-accelerated images.
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
backgroundImage = gc.createCompatibleImage(width, height,Transparency.TRANSLUCENT);
foregroundImage = gc.createCompatibleImage(width, height,Transparency.TRANSLUCENT);
compositeImage = gc.createCompatibleVolatileImage(width, height,Transparency.TRANSLUCENT);
compositeGraphics = compositeImage.createGraphics();
compositeGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
compositeGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
backgroundPixels = ((DataBufferInt) backgroundImage.getRaster().getDataBuffer()).getData();
foregroundPixels = ((DataBufferInt) foregroundImage.getRaster().getDataBuffer()).getData();
//initialize the background image.
for(int i = 0; i < backgroundPixels.length; i++){
backgroundPixels[i] = random.nextInt();
}
// used to throttle frames per second
frameRate = 180;
maximumWait = 1000 / frameRate;
prevUpdate = System.currentTimeMillis();
// used to update sprite state.
speed = 1;
dx = 0;
dy = 0;
sx = 0;
sy = 0;
spriteSize = 32;
}
/** Renders the compositeImage to the Example, scaling to fit. */
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// draw the composite, scaled to the JPanel.
synchronized (imageLock) {
((Graphics2D) g).drawImage(compositeImage, 0, 0, super.getWidth(), super.getHeight(), 0, 0, width, height, null);
}
// force repaint.
repaint();
}
/** Update the BufferedImage states. */
@Override
public void run() {
while(true){
updateSprite();
updateGraphics();
updateComposite();
throttleUpdateSpeed();
}
}
/** Update the Sprite's position. */
private void updateSprite(){
// update the sprite state from the inputs.
dx = 0;
dy = 0;
if (Command.UP.isPressed()) dy -= speed;
if (Command.DOWN.isPressed()) dy += speed;
if (Command.LEFT.isPressed()) dx -= speed;
if (Command.RIGHT.isPressed()) dx += speed;
sx += dx;
sy += dy;
// adjust to keep in bounds.
sx = sx < 0 ? 0 : sx + spriteSize >= width ? width - spriteSize : sx;
sy = sy < 0 ? 0 : sy + spriteSize >= height ? height - spriteSize : sy;
}
/** Update the foregroundGraphics. */
private void updateGraphics(){
Graphics2D fgGraphics = (Graphics2D) foregroundImage.getGraphics();
// set image pixels to transparent
//fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
//fgGraphics.setColor(new Color(255, 255, 255, 255));
//fgGraphics.clearRect(0, 0, width, height);
//fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
// draw again.
fgGraphics.setColor(Color.RED);
fgGraphics.fillRect(sx, sy, spriteSize, spriteSize);
fgGraphics.dispose();
}
/** Draw the background and foreground images to the volatile composite. */
private void updateComposite(){
synchronized (imageLock) {
compositeGraphics.drawImage(backgroundImage, 0, 0, null);
compositeGraphics.drawImage(foregroundImage, 0, 0, null);
}
}
/** Keep the update rate around 60 FPS. */
public void throttleUpdateSpeed(){
try {
Thread.sleep(Math.max(0, maximumWait - (System.currentTimeMillis() - prevUpdate)));
prevUpdate = System.currentTimeMillis();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
/** Ignore key typed events. */
@Override
public void keyTyped(KeyEvent e) {}
/** Handle key presses. */
@Override
public void keyPressed(KeyEvent e) {
setCommandPressedFrom(e.getKeyCode(), true);
}
/** Handle key releases. */
@Override
public void keyReleased(KeyEvent e) {
setCommandPressedFrom(e.getKeyCode(), false);
}
/** Switch over key codes and set the associated Command's pressed value. */
private void setCommandPressedFrom(int keycode, boolean pressed){
switch (keycode) {
case KeyEvent.VK_UP:
Command.UP.setPressed(pressed);
break;
case KeyEvent.VK_DOWN:
Command.DOWN.setPressed(pressed);
break;
case KeyEvent.VK_LEFT:
Command.LEFT.setPressed(pressed);
break;
case KeyEvent.VK_RIGHT:
Command.RIGHT.setPressed(pressed);
break;
}
}
/** Commands are used to interface with key press values. */
public enum Command{
UP, DOWN, LEFT, RIGHT;
private boolean pressed;
/** Press the Command. */
public void press() {
if (!pressed) pressed = true;
}
/** Release the Command. */
public void release() {
if (pressed) pressed = false;
}
/** Check if the Command is pressed. */
public boolean isPressed() {
return pressed;
}
/** Set if the Command is pressed. */
public void setPressed(boolean pressed) {
if (pressed) press();
else release();
}
}
/** Begin the Example. */
public void start(){
try {
// create and display the frame.
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
Example e = new Example();
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
// start updating from key inputs.
Thread t = new Thread(this);
t.start();
}
catch (InvocationTargetException e) {
e.printStackTrace();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
/** Start the application. */
public static void main(String[] args){
Example e = new Example();
e.start();
}
}
编辑:
- 修复了初始化 backgroundPixels
的 for 循环中的拼写错误随机。
最佳答案
原来我在选择方法时犯了错误。我注意到我正在清除一个一像素宽的框,它是我的图形的轮廓。这是因为我不小心使用了 drawRect()
而不是 fillRect()
。更改我的代码后,它现在可以工作了。以下是我能够开始工作的示例。
使用 AlphaComposite.CLEAR
的示例(使用任何不透明颜色绘制):
// clear pixels
fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
fgGraphics.setColor(new Color(255,255,255,255));
fgGraphics.fillRect(0, 0, width, height);
fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
// draw new graphics
使用 AlphaComposite.SRC_OUT
的示例(使用任何带有 alpha 零的颜色绘制):
// clear pixels
fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT));
fgGraphics.setColor(new Color(255,255,255,0));
fgGraphics.fillRect(0, 0, width, height);
fgGraphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
// draw new graphics
关于java - 将 BufferedImage 的像素设置为透明,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31149206/