Java:如何在Swing中进行双缓冲?

Java:如何在Swing中进行双缓冲?,java,swing,doublebuffered,Java,Swing,Doublebuffered,编辑两个 为了防止尖刻的评论和一行答案漏掉要点:如果它像调用setDoubleBuffered(true)一样简单,那么我如何访问当前的脱机缓冲区,以便开始处理BuffereImage的底层像素数据缓冲区 我花了时间写了一段运行的代码(看起来也很有趣),所以我非常感谢回答我的问题并解释它是如何工作的,而不是一句话和尖刻的评论 这是一段工作代码,它在JFrame上反弹一个正方形。我想知道转换这段代码的各种方法,以便它使用双缓冲 请注意,我清除屏幕并重新绘制正方形的方式并不是最有效的,但这并不是这个

编辑两个

为了防止尖刻的评论和一行答案漏掉要点:如果它像调用setDoubleBuffered(true)一样简单,那么我如何访问当前的脱机缓冲区,以便开始处理BuffereImage的底层像素数据缓冲区

我花了时间写了一段运行的代码(看起来也很有趣),所以我非常感谢回答我的问题并解释它是如何工作的,而不是一句话和尖刻的评论

这是一段工作代码,它在JFrame上反弹一个正方形。我想知道转换这段代码的各种方法,以便它使用双缓冲

请注意,我清除屏幕并重新绘制正方形的方式并不是最有效的,但这并不是这个问题的真正含义(在某种程度上,为了本例的缘故,它比较慢)

基本上,我需要不断地修改BuffereImage中的很多像素(为了有某种动画),我不想看到由于屏幕上的单个缓冲而产生的视觉伪影

我有一个JLabel,它的图标是一个包裹着BuffereImage的ImageIcon。我想修改BuffereImage

必须做些什么才能使其成为双缓冲

我知道,当我在“图2”上画图时,不知何故会显示“图1”。但一旦我画完“图像2”,我如何“快速”用“图像2”替换“图像1”

这是我应该手动做的事情吗,比如说,我自己交换JLabel的ImageIcon

我是否应该始终在同一个BuffereImage中绘制,然后在JLabel的ImageIcon的BuffereImage中快速“blit”该BuffereImage的像素?(我想没有,我也不明白我怎么能将其与显示器的“垂直空行”进行“同步”[或平板屏幕中的等效内容:我的意思是,在不干扰显示器刷新像素的情况下进行“同步”,以防剪切])

“重新油漆”的订单呢?是我自己触发的吗?我到底应该在什么时候调用repaint()或其他什么

最重要的要求是,我应该直接在图像的像素数据缓冲区中修改像素

import javax.swing.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;

public class DemosDoubleBuffering extends JFrame {

    private static final int WIDTH  = 600;
    private static final int HEIGHT = 400;

    int xs = 3;
    int ys = xs;

    int x = 0;
    int y = 0;

    final int r = 80;

    final BufferedImage bi1;

    public static void main( final String[] args ) {
        final DemosDoubleBuffering frame = new DemosDoubleBuffering();
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing( WindowEvent e) {
                System.exit(0);
            }
        });
        frame.setSize( WIDTH, HEIGHT );
        frame.pack();
        frame.setVisible( true );
    }

    public DemosDoubleBuffering() {
        super( "Trying to do double buffering" );
        final JLabel jl = new JLabel();
        bi1 = new BufferedImage( WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB );
        final Thread t = new Thread( new Runnable() {
            public void run() {
                while ( true ) {
                    move();
                    drawSquare( bi1 );
                    jl.repaint();
                    try {Thread.sleep(10);} catch (InterruptedException e) {}
                }
            }
        });
        t.start();
        jl.setIcon( new ImageIcon( bi1 ) );
        getContentPane().add( jl );
    }

    private void drawSquare( final BufferedImage bi ) {
        final int[] buf = ((DataBufferInt) bi.getRaster().getDataBuffer()).getData();
        for (int i = 0; i < buf.length; i++) {
            buf[i] = 0xFFFFFFFF;    // clearing all white
        }
        for (int xx = 0; xx < r; xx++) {
            for (int yy = 0; yy < r; yy++) {
                buf[WIDTH*(yy+y)+xx+x] = 0xFF000000;
            }
        }
    }

    private void move() {
        if ( !(x + xs >= 0 && x + xs + r < bi1.getWidth()) ) {
            xs = -xs;
        }
        if ( !(y + ys >= 0 && y + ys + r < bi1.getHeight()) ) {
            ys = -ys;
        }
        x += xs;
        y += ys;
    }

}
import javax.swing.*;
导入java.awt.event.WindowAdapter;
导入java.awt.event.WindowEvent;
导入java.awt.image.buffereImage;
导入java.awt.image.DataBufferInt;
公共类DemosDoubleBuffering扩展JFrame{
专用静态最终整数宽度=600;
专用静态最终内部高度=400;
int-xs=3;
int ys=xs;
int x=0;
int y=0;
最终整数r=80;
最终缓冲图像bi1;
公共静态void main(最终字符串[]args){
最终DemosDoubleBuffering帧=新DemosDoubleBuffering();
frame.addWindowListener(新的WindowAdapter(){
公共无效窗口关闭(WindowEvent e){
系统出口(0);
}
});
框架尺寸(宽度、高度);
frame.pack();
frame.setVisible(true);
}
公共DemosDoubleBuffering(){
超级(“尝试进行双缓冲”);
最终JLabel jl=新JLabel();
bi1=新的BuffereImage(宽度、高度、BuffereImage.TYPE_INT_ARGB);
最终线程t=新线程(新可运行(){
公开募捐{
while(true){
move();
drawSquare(bi1);
jl.repaint();
试试{Thread.sleep(10);}catch(InterruptedException e){}
}
}
});
t、 start();
jl.设置图标(新图像图标(bi1));
getContentPane().add(jl);
}
专用void drawSquare(最终缓冲区图像bi){
final int[]buf=((DataBufferInt)bi.getRaster().getDataBuffer()).getData();
对于(int i=0;i=0&&x+xs+r=0&&y+ys+r
编辑


这不是一个全屏Java应用程序,而是一个普通的Java应用程序,运行在它自己的(有点小)窗口中。

通常我们使用适合Java动画的Canvas类。Anyhoo,下面是实现双缓冲的方法:

class CustomCanvas extends Canvas {
  private Image dbImage;
  private Graphics dbg; 
  int x_pos, y_pos;

  public CustomCanvas () {

  }

  public void update (Graphics g) {
    // initialize buffer
    if (dbImage == null) {

      dbImage = createImage (this.getSize().width, this.getSize().height);
      dbg = dbImage.getGraphics ();

    }

    // clear screen in background
    dbg.setColor (getBackground ());
    dbg.fillRect (0, 0, this.getSize().width, this.getSize().height);

    // draw elements in background
    dbg.setColor (getForeground());
    paint (dbg);

    // draw image on the screen
    g.drawImage (dbImage, 0, 0, this); 
  }

        public void paint (Graphics g)
 {

        g.setColor  (Color.red);



        g.fillOval (x_pos - radius, y_pos - radius, 2 * radius, 2 * radius);

    }
}
现在,您可以从线程更新x_pos和y_pos,然后在canvas对象上调用“repaint”。同样的技术也适用于JPanel。

--编辑为每像素地址设置----

blow项解决了双缓冲问题,但在如何将像素放入
buffereImage
中也存在问题

如果你打电话

WriteableRaster raster = bi.getRaster()
buffereImage
上,它将返回一个
WriteableRaster
。从那里你可以使用

int[] pixels = new int[WIDTH*HEIGHT];
// code to set array elements here
raster.setPixel(0, 0, pixels);
请注意,您可能希望优化代码,以避免为每次渲染实际创建新数组。此外,您可能希望优化数组清除代码,使其不使用for循环

Arrays.fill(pixels, 0xFFFFFFFF);
可能会比将背景设置为白色的循环效果更好

----回应后编辑----

关键在于JFrame的原始设置和运行渲染循环

首先,您需要告诉SWING在需要时停止光栅化;因为,在绘制完要完全交换的缓冲图像后,您将告诉它。用JFrame的

setIgnoreRepaint(true);
然后您需要创建一个缓冲区策略。基本上它是特定的
createBufferStrategy(2);
final BufferStrategy bufferStrategy = getBufferStrategy();
...
  move();
  drawSqure(bi1);
  Graphics g = bufferStrategy.getDrawGraphics();
  g.drawImage(bi1, 0, 0, null);
  g.dispose();
  bufferStrategy.show();
...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import java.awt.image.BufferedImage;

/** @see http://stackoverflow.com/questions/4430356 */
public class DemosDoubleBuffering extends JPanel implements ActionListener {

    private static final int W = 600;
    private static final int H = 400;
    private static final int r = 80;
    private int xs = 3;
    private int ys = xs;
    private int x = 0;
    private int y = 0;
    private final BufferedImage bi;
    private final JLabel jl = new JLabel();
    private final Timer t  = new Timer(10, this);

    public static void main(final String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new DemosDoubleBuffering());
                frame.pack();
                frame.setVisible(true);
            }
        });
    }

    public DemosDoubleBuffering() {
        super(true);
        this.setLayout(new GridLayout());
        this.setPreferredSize(new Dimension(W, H));
        bi = new BufferedImage(W, H, BufferedImage.TYPE_INT_ARGB);
        jl.setIcon(new ImageIcon(bi));
        this.add(jl);
        t.start();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        move();
        drawSquare(bi);
        jl.repaint();
    }

    private void drawSquare(final BufferedImage bi) {
        Graphics2D g = bi.createGraphics();
        g.setColor(Color.white);
        g.fillRect(0, 0, W, H);
        g.setColor(Color.blue);
        g.fillRect(x, y, r, r);
        g.dispose();
    }

    private void move() {
        if (!(x + xs >= 0 && x + xs + r < bi.getWidth())) {
            xs = -xs;
        }
        if (!(y + ys >= 0 && y + ys + r < bi.getHeight())) {
            ys = -ys;
        }
        x += xs;
        y += ys;
    }
}
public class PixelBufferComponent extends JComponent {

    private BufferedImage bufferImage;

    public PixelBufferComponent(int width, int height) {
        bufferImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        setPreferredSize(new Dimension(width, height));
    }

    public void paintComponent(Graphics g) {
        g.drawImage(bufferImage, 0, 0, null);
    }

}