Мерцание событий Java Swing painting и Mouse - PullRequest
0 голосов
/ 16 мая 2011

У меня есть класс, который расширяет JPanel (ниже), эта панель находится внутри JScrollPane. Он прислушивается (к себе) к событиям мыши и пытается изменить положение (при перетаскивании) и масштабировать себя (на колесе), чтобы имитировать движение мыши и масштабирование. Панель также отвечает за основной визуальный вывод моего приложения. Он хранит BufferedImage, который отображается в видимой области JScrollPane (но на графике панели). Размер и форма изображения сохраняются в соответствии с видимой областью.

Мои проблемы как таковые;

1) На событиях мыши я получаю огромное количество мерцания и снижения производительности 2) Если я переопределю методы paint или paintComponent своим собственным методом рисования, который желательно избавить от мерцания и других проблем рисования, я все равно получу тот же эффект мерцания и графику, нарисованную из загруженных изображений, которые имеют прозрачную область, а затем цвет, который область черная. Когда я вызываю свой метод рисования вручную, не переопределяя методы paint и paintComponent, у меня все равно мерцание, но прозрачные области отображаются правильно.

Я новичок в живописи Swing и, очевидно, что-то делаю не так, может кто-нибудь указать мне правильное направление, чтобы это исправить?

Спасибо

    import jSim.simulation.Simulation;
    import java.awt.Color;
    import java.awt.Cursor;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Image;
    import java.awt.Point;
    import java.awt.Rectangle;
    import java.awt.Toolkit;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseWheelEvent;
    import java.awt.event.MouseWheelListener;
    import java.awt.image.BufferStrategy;
    import java.awt.image.BufferedImage;
    import javax.swing.JPanel;
    import javax.swing.JViewport;
    import javax.swing.event.MouseInputListener;

    public class SimPanel extends JPanel implements MouseWheelListener, MouseInputListener {
        //Simulation

        Simulation sim;
        //Viewer
        JViewport viewport;
        Dimension viewSize;
        BufferStrategy strat;
        //Drawing
        Image renderImage;
        Graphics2D g2d;
        boolean draw = true;
        double scale = 1.0;
        Object drawLock = new Object();
        //Mouse events
        int m_XDifference, m_YDifference;

        public SimPanel(JViewport viewport) {
            this.viewport = viewport;
            this.addMouseListener(this);
            this.addMouseMotionListener(this);
            this.addMouseWheelListener(this);

            //this.setup();
        }

        public SimPanel(Simulation sim, JViewport viewport) {
            this.sim = sim;
            this.viewport = viewport;
            this.addMouseListener(this);
            this.addMouseMotionListener(this);
            this.addMouseWheelListener(this);
            //this.setup();
        }

        //Used to initialise the buffered image once drawing begins
        private void setup() {
            synchronized (drawLock) {
                viewSize = viewport.getExtentSize();
                renderImage = new BufferedImage(viewSize.width, viewSize.height, BufferedImage.TYPE_INT_RGB);
                g2d = (Graphics2D) renderImage.getGraphics();
            }
        }

    //    @Override
    //    public void paint(Graphics g)
    //    {
    //        synchronized(drawLock) {
    //        //super.paintComponent(g);
    //        paintSimulation();
    //        }
    //    }
        //Paint the screen for a specific simulation
        public void paintSimulation(Simulation sim) {
            synchronized (drawLock) {
                setSimulation(sim);
                paintSimulation();
            }
        }

        //Paint the screen with the panels simulation
        public void paintSimulation() {
            synchronized (drawLock) {
                //if no image, then init
                if (renderImage == null) {
                    setup();
                }
                //clear the screen
                resetScreen();
                //draw the simulation if not null, to the image
                if (sim != null) {
                    sim.draw(this);
                }
                //paint the screen with the image
                paintScreen();
            }
        }

        private void resetScreen() {
            Dimension newSize = viewport.getExtentSize();
            if (viewSize.height != newSize.height || viewSize.width != newSize.width || renderImage == null) {
                //System.out.println("Screen Size Changed: " + viewSize + "   " + newSize);
                viewSize = newSize;
                renderImage = new BufferedImage(viewSize.width, viewSize.height, BufferedImage.TYPE_INT_RGB);
                g2d = (Graphics2D) renderImage.getGraphics();
            } else {
                g2d.setBackground(Color.DARK_GRAY);
                g2d.clearRect(0, 0, (int) (viewSize.width), (int) (viewSize.height));
            }
        }

        private void paintScreen() {
            Graphics g;
            Graphics2D g2;
            try {
                //g = viewport.getGraphics();
                g = this.getGraphics();
                g2 = (Graphics2D) g;
                if ((g != null) && (renderImage != null)) {
                    g2.drawImage(renderImage, (int) viewport.getViewPosition().getX(), (int) viewport.getViewPosition().getY(), null);
                }
                Toolkit.getDefaultToolkit().sync();  // sync the display on some systems
                g.dispose();
                g2.dispose();
                this.revalidate();
            } catch (Exception e) {
                System.out.println("Graphics context error: " + e);
            }
        }

        //Simulation makes calls to this method to draw items on the image
        public void draw(BufferedImage image, int x, int y, Color colour) {
            synchronized (drawLock) {
                Rectangle r = viewport.getViewRect();
                if (g2d != null && draw) {
                    Point p = new Point((int) (x * scale), (int) (y * scale));
                    if (r.contains(p)) {
                        if (scale < 1) {
                            Graphics2D g2 = (Graphics2D) image.getGraphics();
                            Image test = image.getScaledInstance((int) (image.getWidth(null) * scale), (int) (image.getHeight(null) * scale), Image.SCALE_FAST);
                            g2d.drawImage(test, (int) ((x * scale - r.x)), (int) ((y * scale - r.y)), null);
                        } else {
                            g2d.drawImage(image, x - r.x, y - r.y, null);
                        }
                    }
                }
            }
        }

        public void setDraw(boolean draw) {
            this.draw = draw;
        }

        public void setSimulation(Simulation sim) {
            synchronized (drawLock) {
                if (!(this.sim == sim)) {
                    this.sim = sim;
                }
            }
        }

        public void mouseWheelMoved(MouseWheelEvent e) {
            synchronized (drawLock) {
                updatePreferredSize(e.getWheelRotation(), e.getPoint());
            }
        }

        private void updatePreferredSize(int wheelRotation, Point stablePoint) {
            double scaleFactor = findScaleFactor(wheelRotation);
            if (scale * scaleFactor < 1 && scale * scaleFactor > 0.05) {
                scaleBy(scaleFactor);
                Point offset = findOffset(stablePoint, scaleFactor);
                offsetBy(offset);
                this.getParent().doLayout();
            }
        }

        private double findScaleFactor(int wheelRotation) {
            double d = wheelRotation * 1.08;
            return (d > 0) ? 1 / d : -d;
        }

        private void scaleBy(double scaleFactor) {
            int w = (int) (this.getWidth() * scaleFactor);
            int h = (int) (this.getHeight() * scaleFactor);
            this.setPreferredSize(new Dimension(w, h));
            this.scale = this.scale * scaleFactor;
        }

        private Point findOffset(Point stablePoint, double scaleFactor) {
            int x = (int) (stablePoint.x * scaleFactor) - stablePoint.x;
            int y = (int) (stablePoint.y * scaleFactor) - stablePoint.y;
            return new Point(x, y);
        }

        private void offsetBy(Point offset) {
            Point location = viewport.getViewPosition();
            //this.setLocation(location.x - offset.x, location.y - offset.y);
            viewport.setViewPosition(new Point(location.x + offset.x, location.y + offset.y));
        }

        public void mouseDragged(MouseEvent e) {
            synchronized (drawLock) {
                //Point p = this.getLocation();
                Point p = viewport.getViewPosition();
                int newX = p.x - (e.getX() - m_XDifference);
                int newY = p.y - (e.getY() - m_YDifference);
                //this.setLocation(newX, newY);
                viewport.setViewPosition(new Point(newX, newY));
                //this.getParent().doLayout();
            }
        }

        public void mousePressed(MouseEvent e) {
            synchronized (drawLock) {
                setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                m_XDifference = e.getX();
                m_YDifference = e.getY();
            }
        }

        public void mouseReleased(MouseEvent e) {
            synchronized (drawLock) {
                setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            }
        }

        public void mouseClicked(MouseEvent e) {
            //throw new UnsupportedOperationException("Not supported yet.");
        }

        public void mouseEntered(MouseEvent e) {
            //throw new UnsupportedOperationException("Not supported yet.");
        }

        public void mouseExited(MouseEvent e) {
            //throw new UnsupportedOperationException("Not supported yet.");
        }

        public void mouseMoved(MouseEvent e) {
            //throw new UnsupportedOperationException("Not supported yet.");
        }
    }

Ответы [ 2 ]

1 голос
/ 16 мая 2011

Короче, ищите двойную буферизацию.

Чем дольше ответ ...

Переопределить paintComponent. Создать внеэкранный графический объект. Сделай свою картину на этом объекте. Скопируйте это в графические объекты, переданные в метод рисования.

Ох, и избавься от всей синхронизации. Вам это не нужно.

0 голосов
/ 16 мая 2011

Если вы добавляете setOpaque (true) в область просмотра, вы сообщаете Swing, что все рисование (особенно фон) вы сделаете сами.Это может уже немного помочь.

РЕДАКТИРОВАТЬ

Я оглянулся немного больше и думаю, что вам следует переопределить paintComponent.

Вы могли быимеют 2 изображения и четыре ссылки:

  • imageToPaint = null
  • imageToWriteTo = null
  • bufferImageOne, инициализированный для соответствующего размера BufferedImage
  • bufferImageTwo, инициализированный длясоответствующего размера BufferedImage

Вы должны переопределить paintComponent, чтобы нарисовать фон, а затем drawImage (imageToPaint) (если оно не равно нулю, которого не должно быть)

У вас будетпоток, который делает пользовательскую живопись imageToWriteTo.В конце он меняет местами imageToPaint и imageToWriteTo.

Затем вы вызываете repaint ().Это требует перерисовки с дополнительным преимуществом, заключающимся в том, что все запросы на перерисовку в очереди Swing собираются вместе и приводят к единому рисованию.Нет повторной проверки или синхронизации, пожалуйста.Эта перекраска автоматически выполняется во втором потоке, нити диспетчеризации событий.

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

Короче говоря, дорогая запись выполняется для imageToWriteTo.Рисование выполняется с использованием imageToPaint.Дорогая запись заканчивается обменом imageToWriteTo и imageToPaint.

...