Пользовательский рендеринг AWT - захват плавного изменения размера и устранение мерцания изменения размера - PullRequest
6 голосов
/ 26 июля 2011

Я смотрю на это уже несколько месяцев, и пока это лучшее, что я придумал.

Структура (визуализация вне EDT) не обсуждается, так как наше приложение работаеттаким образом и не будет переписан.Приложение имеет модель макета и модель сценариев, которые интегрированы и управляют рендерингом, поэтому рендеринг должен выполняться вне модели рисования AWT.

То, к чему я пытаюсь прийти, является оптимальным и надежным способомвыполнить пользовательский рендеринг.

Следующий SSCCE работает довольно хорошо для нас.Однако, во время изменения размера кадра, у него есть 2 недостатка:

  • Иногда возникает мерцание, особенно при быстром изменении размера
  • Хак с "плавным изменением размера", который должен вызывать изменение размера (через checkSize здесь) из paint () вызов работает только для расширений.При уменьшении кадра он обычно не рендерится до тех пор, пока кнопка мыши не будет отпущена
  • Кроме того, но здесь это не так очевидно, он вызывает случайные исключения IllegalStateExceptions - можно ли просто перехватывать / игнорировать их?*

    Также полезно указать, является ли это оптимальным подходом для пользовательского пути рендеринга, который происходит за пределами EDT.Я попробовал больше всего, и сделал довольно обширные исследования.Эта комбинация (образ буфера, стратегия двойного буфера), кажется, работает лучше всего.

    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) { }
    }
    

Ответы [ 2 ]

3 голосов
/ 02 августа 2011

Вот код, который рендерится с внешним потоком, выполняющим всю работу.Это достигается за счет возможности рендеринга всего, что реализует интерфейс Renderable.Я проверил это с Swing и AWT (JFrame и Frame), и это работает без мерцания.Обратите внимание, что он мерцает, если вы реализуете на JRootPane и устанавливаете эту панель как корневую панель JFrame.Это связано с тем, как компонент буферизован, и может быть исправлено, если вы хотите использовать это.

Если это все еще не то, что вы искали, просто скажите, и я дам этоеще одинЭто на самом деле весело, так как прошло некоторое время с тех пор, как я работал с любым графическим интерфейсом Java.

В любом случае, вот, пожалуйста:

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();
}
2 голосов
/ 01 августа 2011

Этот ответ оставлен здесь для справки, но это не правильный ответ, потому что он отображается внутри потока EDT.

Вот рабочее исправление! : D В основном проблема в том, что ComponentResized не вызывается надлежащим образом, пока мышь не будет отпущена после сжатия. Кроме того, поскольку методы paint и checkSize синхронизированы, они могут исключать друг друга в редких случаях. Исправление заключается в переопределении метода validate в классе Frame. Этот метод всегда вызывается, если фрейм изменяет состояние, включая сжатие и рост. Таким образом, мы просто должны проверить размер в валидаторе и вообще можем полностью забыть об использовании метода ComponentResized.

Итак, вот рабочий код, который компилируется как есть. Я изменил некоторые имена переменных, чтобы улучшить мою читабельность.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.image.BufferStrategy;
import java.awt.Frame;

public class SmoothResize extends Frame {

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);
}

public SmoothResize() {
    render();
}

private Dimension old_size = new Dimension(0, 0);
private Dimension new_size = new Dimension(0, 0);

public void validate() {
    super.validate();
    new_size.width = getWidth();
    new_size.height = getHeight();
    if (old_size.equals(new_size)) {
        return;
    } else {
        render();
    }
}

public void paint(Graphics g) {
    validate();
}

public void update(Graphics g) {
    paint(g);
}

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

protected synchronized void render() {
    BufferStrategy strategy = getBufferStrategy();
    if (strategy == null) {
        return;
    }
    // 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 {
            Graphics draw = strategy.getDrawGraphics();
            Insets i = getInsets();
            int w = (int)(((double)(getWidth() - i.left - i.right))/2+0.5);
            int h = (int)(((double)(getHeight() - i.top - i.bottom))/2+0.5);
            draw.setColor(Color.YELLOW);
            draw.fillRect(i.left, i.top + h, w,h);
            draw.fillRect(i.left + w, i.top, w,h);
            draw.setColor(Color.BLACK);
            draw.fillRect(i.left, i.top, w, h);
            draw.fillRect(i.left + w, i.top + h, w,h);
            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());
   }

  }

Я надеюсь, что это работает для вас!

Также, в последнюю секунду редактирования, я изменил логическую троичную операцию для выбора вашей сокращения или расширения строки. Последнее сравнение было ненужным, потому что для рассматриваемого сравнения значения могут быть больше, меньше или равны друг другу. Нет другой возможности, которая бы не генерировала NullPointerException.

Пораженный текст больше не актуален, так как я полностью удалил весь этот метод. В качестве комментариев я указываю другие изменения, которые я внес в исходное сообщение.

...