我们看电视时,看到的屏幕称为OSD层,也就是说,只有在OSD层上显示图像我们才能看到。现在,我需要创建一个虚拟的、看不见但是可以在上面画图(比如说画点、线)的OSD层,我称之为offscreen(后台缓冲区)。这个offscreen存在于内存中,我们在上面画图,这个offscreen上面的东西可以显示在OSD层上,需要一个创建这个offscreen的函数,返回这个offscreen的句柄(整型指针)、宽度、高度、指向新建offscreen数据缓冲区的指针,该缓冲区是一个在函数外创建的offscreen的数据缓冲区,大小是offscreen的高度*宽度*每个像素点数据的大小。闪烁是图形编程的一个常见问题。需要多重复杂绘制操作的图形操作会导致呈现的图像闪烁或具有其他不可接受的外观。双缓冲的使用解决这些问题。双缓冲使用内存缓冲区来解决由多重绘制操作造成的闪烁问题。当启用双缓冲时,所有绘制操作首先呈现到内存缓冲区,而不是屏幕上的绘图图面。所有绘制操作完成后,内存缓冲区直接复制到与其关联的绘图图面。因为在屏幕上只执行一个图形操作,所以消除了由复杂绘制操作造成的图像闪烁。
在Java swing中,绘制的原理如下:
update() -> paint(Graphics g) -> paintComponent(Graphics g)
它们是逐渐调用的,Java swing内置有双缓冲,在paint(Graphics g)中。
假如我们的图像绘制在JPanel上,我们可以继承JPanel,然后复写里面的paintComponent(Graphics g)和update方法。我们可以在paintComponent上绘制图像,然后调用paint方法就可以调用paintComponent方法绘制图像。
双缓冲的实现:
复写JPanel类中的update()方法,新建一个图像缓存空间,然后把它的画笔拿过来,用paint方法把要绘制的东西绘制上去。再一次性把图像显示在JPanel控件上。
我在这里是用鼠标画线消除闪动的例子来说明双缓冲的使用。
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class DrawTrail
{
BufferedImage image = new BufferedImage(500, 500,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
private DrawCanvas canvas = new DrawCanvas();
private int preX = -1;
private int preY = -1;
private Image offScreenImage; //图形缓存
public void init()
{
g.fillRect(0, 0, 1600, 1000);
JFrame frame = new JFrame("测试画出鼠标的轨迹");
frame.setSize(600, 600);
frame.add(canvas);
frame.setLayout(null);
canvas.setBounds(0, 0, 500, 500);
frame.setVisible(true);
canvas.addMouseMotionListener(new MouseMotionAdapter()
{
@Override
public void mouseDragged(MouseEvent e)
{
if (preX > 0 && preY > 0)
{
g.setColor(Color.black);
g.drawLine(preX, preY, e.getX(), e.getY());
}
preX = e.getX();
preY = e.getY();
canvas.repaint();
}
});
frame.addWindowListener(new WindowAdapter()//添加窗口关闭处理函数
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}});
}
public static void main(String[] args)
{
DrawTrail dc = new DrawTrail();
dc.init();
}
class DrawCanvas extends Canvas
{
private static final long serialVersionUID = 1L;
@Override
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(image, 0, 0, null);
}
@Override
public void update(Graphics g)
{
if(offScreenImage == null)
offScreenImage = this.createImage(500, 500); //新建一个图像缓存空间,这里图像大小为800*600
Graphics gImage = offScreenImage.getGraphics(); //把它的画笔拿过来,给gImage保存着
paint(gImage); //将要画的东西画到图像缓存空间去
g.drawImage(offScreenImage, 0, 0, null); //然后一次性显示出来
}
}
}