【问题标题】:Continous double buffering solution not working连续双缓冲溶液不起作用
【发布时间】:2015-08-02 02:14:05
【问题描述】:

我正在尝试使用 AWT 在 paint() 方法中对包含多边形的图像进行双重缓冲。使用Image 对象进行缓冲过程,我将图像背景设置为黑色,将多边形绘制到图像上,然后将缓冲后的图像绘制到屏幕上。然后我调用repaint() 以再次渲染图像。

很遗憾,我在重新绘制图像时仍然收到伪影。我做错了什么?

编辑:附带说明,我使用的是 Java 8。 编辑 2:我在 paint() 中调用 repaint(),因为我需要持续缓冲图像。多边形旨在根据用户输入在屏幕上平移。

import java.applet.Applet;
import java.awt.*;

public class DoubleBuffer extends Applet {
    int xSize = 900;
    int ySize = 600;

    Image bufferImage;
    Graphics bufferG;

    @Override
    public void init() {
        this.setSize(xSize, ySize);

        //Double buffering related variables
        bufferImage = this.createImage(xSize, xSize);
        bufferG = bufferImage.getGraphics();
    }

    //BUFFERING DONE HERE
    @Override
    public void paint(Graphics g){
        //drawing images to external image first (buffering)
        bufferG.setColor(Color.BLACK);
        bufferG.fillRect(0,0,xSize,ySize);
        bufferG.setColor(Color.WHITE);
        bufferG.drawRect(100, 100, 100, 100);

        //draw the image and call repaint
        g.drawImage(bufferImage, 0, 0, this);
        repaint();
    }
}

【问题讨论】:

  • 有什么理由使用默认双缓冲的轻量级组件(例如JPanel)? (我建议不要paint 方法中调用repaint
  • 这听起来很奇怪,但由于我在工作中遵循的某些 API 限制,我一直坚持使用重量级组件(即只是窗口)(请多多包涵)。如果一切都失败了,我会继续使用轻量级组件。我即将开始进行更多研究,但是在paint() 中使用repaint() 有什么问题?我还应该在哪里重新渲染我的图像?
  • P.S.这是一项与教育相关的工作,其中图像渲染只占一小部分 - 遗憾的是,我在这方面的经验很少。
  • 您应该在paint 方法中渲染(或paintComponent 用于轻量级组件),但在任一方法中调用repaint 都会导致它一次又一次地连续绘制, 并且...只有在发生变化时才需要致电 repaint
  • 如果您希望它按时间间隔重绘,请使用 Timer,或者仅在用户交互/更改值时调用重绘。

标签: java awt java-8 doublebuffered


【解决方案1】:

问题是你没有覆盖update,这是响应repaint请求的方法。对于重量级组件,update 的默认实现将首先将组件清除为背景色(可能默认为白色),然后调用您的 paint 方法。

正如其他人所指出的,您不应该在 paint 方法中调用 repaint。你应该使用Timer

清理之后,整个类会是这样的:

public class DoubleBuffer extends Applet {
    int xSize = 900;
    int ySize = 600;

    Image bufferImage;
    Graphics bufferG;
    Timer timer=new Timer(200, ev->repaint());

    @Override
    public void init() {
        this.setSize(xSize, ySize);
    }

    @Override
    public void addNotify() {
        super.addNotify();
        //Double buffering related variables
        bufferImage = this.createImage(xSize, xSize);
        bufferG = bufferImage.getGraphics();
        timer.start();
    }

    @Override
    public void removeNotify() {
        super.removeNotify();
        bufferImage = null;
        bufferG = null;
        timer.stop();
    }


    //BUFFERING DONE HERE
    @Override
    public void paint(Graphics g){
        //drawing images to external image first (buffering)
        bufferG.setColor(Color.BLACK);
        bufferG.fillRect(0,0,xSize,ySize);
        bufferG.setColor(Color.WHITE);
        bufferG.drawRect(100, 100, 100, 100);

        //draw the image and call repaint
        g.drawImage(bufferImage, 0, 0, this);
    }

    @Override
    public void update(Graphics g) {
        // now not clearing the background as we expect paint() to fill the entire area
        this.paint(g);
    }
}

【讨论】:

  • 值得注意的是,正如我在问题的 cmets 中所述,只需从 actionPerformed()(由计时器触发)调用 repaint() 即可作为我自己的解决方案。无论如何,我还将 update() 覆盖,作为额外(离线)来源支持您的声明。
  • @c4goldsw:这可能是系统相关的副作用。由于时间不可预测,重复的填充操作可能会发生得如此之快,以至于您不会注意到它。在我的机器上,使用Timer 大大减少了闪烁,但并没有完全消除它。所以覆盖update 是必需的,我猜可能存在不覆盖update 的系统/Java 版本可能会产生更大的影响。
  • 我弄错了 - 当我第一次报告 copeg 的解决方案有效时,我让计时器以非常慢的速度执行(每秒一次)。增加计时器“滴答”导致伪影的速率,因此实际上需要覆盖和修改 update()。
  • 最后一个问题:在paint() 中调用repaint() 有什么问题? According to this website, "repaint() 只是请求 AWT 线程调用 update()。然后它立即返回"。这可能意味着下一次调用paint 可以在当前调用paint 之前执行并完成,但图像是在调用repaint() 之前绘制的。
  • 您无法控制如何安排 repaint。从paint 中调用它可能会导致任何事情,从 100% 的 CPU 消耗绘画频率到由于绘画已经在进行中而导致的完全无知。它可能碰巧在一个系统上工作,但没有注意到问题实际上会使事情变得更糟。
猜你喜欢
  • 2012-07-29
  • 2021-04-28
  • 2020-04-01
  • 2013-03-05
  • 1970-01-01
  • 1970-01-01
  • 2010-11-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多