AWT定制渲染 - 捕捉顺利尺寸并消除闪烁调整大小 [英] AWT custom rendering - capture smooth resizes and eliminate resize flicker

查看:304
本文介绍了AWT定制渲染 - 捕捉顺利尺寸并消除闪烁调整大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在寻找这个数月,到目前为止,这是我想出最好的。

结构(渲染EDT以外)不起来辩论,因为我们的应用程序运行这种方式并不会被改写。应用程序有一个布局模型和集成驱动器和渲染脚本模型,因此必须在AWT漆模型之外进行渲染。

我试图在是执行自定义呈现最佳的和可靠的方式到达。

以下SSCCE工作相当好我们。然而,在帧尺寸调整,它有2个缺点:


  • 有偶尔闪烁,尤其是在快速的将

  • 从油漆()调用的平稳调整大小黑客是调用大小调整(通过checkSize这里)只行之有效的扩展。当减小帧通常不呈现直到鼠标按钮被释放

  • 同时,但不那么明显的在这里,它抛出偶尔IllegalStateExceptions - 确定就可以是简单地赶上/忽略这些

同样有用的是在此是否为定制渲染路径,是以EDT以外地方的最佳方法输入。我已经试过最多,做得相当广泛的研究。这种组合(后备缓冲图像,双缓冲策略)似乎工作最好的。

 进口java.awt.Color中;
进口java.awt.Dimension中;
进口java.awt.Frame中;
进口java.awt.Graphics;
进口java.awt.Insets中;
进口java.awt.Toolkit中;
进口java.awt.event.ComponentEvent;
进口java.awt.event.ComponentListener;
进口java.awt.event.MouseEvent中;
进口java.awt.event.MouseMotionListener;
进口java.awt.image.BufferStrategy;公共类SmoothResize扩展框架实现的ComponentListener,{的MouseMotionListener    公共SmoothResize(){
        addComponentListener(本);
        addMouseMotionListener(本);
    }    私人布尔时,SizeChanged = FALSE;
    私人尺寸旧=新的Dimension(0,0);
    私人同步无效checkSize(字符串源){
        INT宽度=的getWidth();
        INT高度=的getHeight();
        如果(old.width ==宽度放大器;&安培; old.height ==高)
            返回;
        SizeChanged将= TRUE;
        字符串类型=
            (old.width>宽度放大器;&安培; old.height>高度)? 缩水:
                (old.width<宽度放大器;&安培; old.height<高度)? 扩大:调整;
        的System.out.println(来源+报道+类型+:+的getWidth()+,+的getHeight());
        old.setSize(宽度,高度);
    }    公共无效的componentResized(ComponentEvent为arg0){checkSize(的componentResized); }
    公共无效的mouseMoved(的MouseEvent E){checkSize(的mouseMoved); }
    公共无效漆(图形G){checkSize(画图); }
    公共无效更新(图形G){漆(G); }    公共无效信息addNotify(){
        super.addNotify();
        createBufferStrategy(2);
    }    私人同步无效渲染(){
        BufferStrategy中的策略= getBufferStrategy();
        如果(战略== NULL ||时,SizeChanged!)回报;
        SizeChanged将= FALSE;
        //渲染单帧
        做{
            //以下循环确保了绘图缓冲器的内容
            //是一致的情况下,底层表面重建
            做{
                的System.out.println(渲染);
                图形绘制= strategy.getDrawGraphics();
                插图I =的getInsets();
                INT W =的getWidth() - i.left-i.right;
                INT H =的getHeight() - i.top-i.bottom;
                draw.setColor(Color.YELLOW);
                draw.fillRect(i.left,i.top +(H / 2),重量/ 2,H / 2);
                draw.fillRect(i.left +(W / 2),i.top,瓦特/ 2,H / 2);
                draw.setColor(Color.BLACK);
                draw.fillRect(i.left,i.top,瓦特/ 2,H / 2);
                draw.fillRect(i.left +(W / 2),i.top +(H / 2),重量/ 2,H / 2);
                draw.dispose();                //重复渲染如果绘图缓冲区的内容
                //被恢复
            }而(strategy.contentsRestored());            //显示缓冲区
            strategy.show();            //重复绘制如果绘制缓冲区已丢失
        }而(strategy.contentsLost());
    }    公共静态无效的主要(字串[] args){
        。Toolkit.getDefaultToolkit()setDynamicLayout(真);
        System.setProperty(sun.awt.noerasebackground,真);
        SmoothResize srtest =新SmoothResize();
        //srtest.setIgnoreRepaint(true);
        srtest.setSize(100,100);
        srtest.setVisible(真);
        而(真){
            srtest.render();
        }
    }    公共无效的componentHidden(ComponentEvent为arg0){}
    公共无效的componentMoved(ComponentEvent为arg0){}
    公共无效的componentShown(ComponentEvent为arg0){}    公共无效的mouseDragged(的MouseEvent E){}
}


解决方案

下面是code,与外螺纹做所有的工作呈现。它通过能够使任何实现了渲染界面做到这一点。我曾与两个Swing和AWT(的JFrame 框架),并将其与无闪烁的作品测试了这个。请注意,如果您实现到的JRootPane ,并设置该窗格的的JFrame 的根窗格它闪烁。这与该组件是如何缓冲做的,可能是固定的,如果这是你想怎么用这个。

如果这仍然不是你要找的,只是说我给它另一个去。这实际上是有趣的,因为它已经一段时间,因为我做了任何Java GUI的工作。

不管怎么说,在这里你去:

 进口java.awt.Color中;
进口java.awt.Dimension中;
进口java.awt.Frame中;
进口java.awt.Graphics;
进口java.awt.Toolkit中;
进口javax.swing.JFrame中;公共类SmoothResize扩展框架实现渲染{    公共静态无效的主要(字串[] args){
        。Toolkit.getDefaultToolkit()setDynamicLayout(真);
        System.setProperty(sun.awt.noerasebackground,真);
        SmoothResize srtest =新SmoothResize();
        RenderThread renderThread =新RenderThread(srtest);
        renderThread.start();
        srtest.setSize(100,100);
        srtest.setVisible(真);
    }    公共SmoothResize(){
    }    公共无效信息addNotify(){
        super.addNotify();
        createBufferStrategy(2);
    }    @覆盖
    公共尺寸的getSize(){
        返回新的尺寸(的getWidth()的getHeight());
    }    @覆盖
    公共图形acquireGraphics(){
        返回this.getGraphics();
    }
}类RenderThread继承Thread {    渲染目标;
    尺寸last_size =新的Dimension(Integer.MAX_VALUE的,Integer.MAX_VALUE的);    公共RenderThread(渲染D){
        如果(D == NULL){
            抛出新的NullPointerException(可绘制目标不能为空);
        }
        目标= D;    }    @覆盖
    公共无效的run(){
        而(真){
            渲染(假);
        }
    }    私人同步无效渲染(布尔力){
        尺寸大小;
        做{
            大小= target.getSize();
            如果(大小== NULL){
                返回;
            }            图形绘制= target.acquireGraphics();
            如果(画== NULL){
                返回;
            }
            draw.setPaintMode();
            INT W =(int)的(((双)(size.width))/ 2 + 0.5);
            INT H =(int)的(((双)(size.height))/ 2 + 0.5);
            draw.setColor(Color.YELLOW);
            draw.fillRect(0,H,W,H);
            draw.fillRect(W,0,W,H);
            draw.setColor(Color.BLACK);
            draw.fillRect(0,0,W,H);
            draw.fillRect(W,H,W,H);
            draw.dispose();
            //重复渲染如果目标改变大小
        }而(size.equals(target.getSize())!);
    }
}界面渲染{    公共图形acquireGraphics();    公共尺寸的getSize();
}

I've been looking at this for several months and so far this is the best I have come up with.

The structure (render outside of EDT) is not up for debate, as our application operates this way and will not be rewritten. The application has a layout model and a scripting model which are integrated and drive rendering, so the render must be performed outside of the AWT paint model.

What I am trying to arrive at is the optimal and reliable way to perform custom rendering.

The following SSCCE works fairly well for us. However, during frame resizes, it has 2 drawbacks:

  • There is occasional flicker, especially on rapid resizes
  • The "smooth resize" hack which is to invoke resize (via checkSize here) from a paint() call only works well for expansions. When reducing the frame it usually does not render until the mouse button is released
  • Also, but not so evident here, it does throw occasional IllegalStateExceptions - is it OK to simply catch/ignore these?

Also useful is input on whether this is the optimal approach for a custom render path that takes place outside of the EDT. I have tried most, and done fairly extensive research. This combination (backbuffer image, double buffer strategy) seems to work the best.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferStrategy;

public class SmoothResize extends Frame implements ComponentListener, MouseMotionListener {

    public SmoothResize() {
        addComponentListener(this);
        addMouseMotionListener(this);
    }

    private boolean sizeChanged = false;
    private Dimension old = new Dimension(0, 0);
    private synchronized void checkSize(String source) {
        int width = getWidth();
        int height = getHeight();
        if (old.width == width && old.height == height)
            return;
        sizeChanged = true;
        String type =
            (old.width > width && old.height > height) ? "shrink" :
                (old.width < width && old.height < height) ? "expand" : "resize";
        System.out.println(source + " reports " + type + ": "+getWidth()+", "+getHeight());
        old.setSize(width, height);
    }

    public void componentResized(ComponentEvent arg0) { checkSize("componentResized"); }
    public void mouseMoved(MouseEvent e) { checkSize("mouseMoved"); }
    public void paint(Graphics g) { checkSize("paint"); }
    public void update(Graphics g) { paint(g); }

    public void addNotify() {
        super.addNotify();
        createBufferStrategy(2);
    }

    private synchronized void render() {
        BufferStrategy strategy = getBufferStrategy();
        if (strategy==null || !sizeChanged) return;
        sizeChanged = false;
        // Render single frame
        do {
            // The following loop ensures that the contents of the drawing buffer
            // are consistent in case the underlying surface was recreated
            do {
                System.out.println("render");
                Graphics draw = strategy.getDrawGraphics();
                Insets i = getInsets();
                int w = getWidth()-i.left-i.right;
                int h = getHeight()-i.top-i.bottom;
                draw.setColor(Color.YELLOW);
                draw.fillRect(i.left, i.top+(h/2), w/2, h/2);
                draw.fillRect(i.left+(w/2), i.top, w/2, h/2);
                draw.setColor(Color.BLACK);
                draw.fillRect(i.left, i.top, w/2, h/2);
                draw.fillRect(i.left+(w/2), i.top+(h/2), w/2, h/2);
                draw.dispose();

                // Repeat the rendering if the drawing buffer contents 
                // were restored
            } while (strategy.contentsRestored());

            // Display the buffer
            strategy.show();

            // Repeat the rendering if the drawing buffer was lost
        } while (strategy.contentsLost());
    }

    public static void main(String[] args) {
        Toolkit.getDefaultToolkit().setDynamicLayout(true);
        System.setProperty("sun.awt.noerasebackground", "true");
        SmoothResize srtest = new SmoothResize();
        //srtest.setIgnoreRepaint(true);
        srtest.setSize(100, 100);
        srtest.setVisible(true);
        while (true) {
            srtest.render();
        }
    }

    public void componentHidden(ComponentEvent arg0) { }
    public void componentMoved(ComponentEvent arg0) { }
    public void componentShown(ComponentEvent arg0) { }

    public void mouseDragged(MouseEvent e) { }
}

解决方案

Here is code that renders with an outside Thread doing all the work. It does this by being able to render anything that implements the Renderable interface. I have tested this with both Swing and AWT (JFrame and Frame) and it works with no flickering. Note, it does flicker if you implement onto a JRootPane and set that pane as the JFrame's root pane. This has to do with how the component is buffered, and could be fixed if that is how you want to use this.

If this is still not what you were looking for, just say and I'll give it another go. This is actually fun, as it has been awhile since I've done any Java GUI work.

Anyways, here you go:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Toolkit;
import javax.swing.JFrame;

public class SmoothResize extends Frame implements Renderable {

    public static void main(String[] args) {
        Toolkit.getDefaultToolkit().setDynamicLayout(true);
        System.setProperty("sun.awt.noerasebackground", "true");
        SmoothResize srtest = new SmoothResize();
        RenderThread renderThread = new RenderThread(srtest);
        renderThread.start();
        srtest.setSize(100, 100);
        srtest.setVisible(true);
    }

    public SmoothResize() {
    }

    public void addNotify() {
        super.addNotify();
        createBufferStrategy(2);
    }

    @Override
    public Dimension getSize() {
        return new Dimension(getWidth(), getHeight());
    }

    @Override
    public Graphics acquireGraphics() {
        return this.getGraphics();
    }
}

class RenderThread extends Thread {

    Renderable target;
    Dimension last_size = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);

    public RenderThread(Renderable d) {
        if (d == null) {
            throw new NullPointerException("Drawable target cannot be null.");
        }
        target = d;

    }

    @Override
    public void run() {
        while (true) {
            render(false);
        }
    }

    private synchronized void render(boolean force) {
        Dimension size;
        do {
            size = target.getSize();
            if (size == null) {
                return;
            }

            Graphics draw = target.acquireGraphics();
            if (draw == null) {
                return;
            }
            draw.setPaintMode();
            int w = (int) (((double) (size.width)) / 2 + 0.5);
            int h = (int) (((double) (size.height)) / 2 + 0.5);
            draw.setColor(Color.YELLOW);
            draw.fillRect(0, h, w, h);
            draw.fillRect(w, 0, w, h);
            draw.setColor(Color.BLACK);
            draw.fillRect(0, 0, w, h);
            draw.fillRect(w, h, w, h);
            draw.dispose();
            // Repeat the rendering if the target changed size
        } while (!size.equals(target.getSize()));
    }
}

interface Renderable {

    public Graphics acquireGraphics();

    public Dimension getSize();
}

这篇关于AWT定制渲染 - 捕捉顺利尺寸并消除闪烁调整大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆