Java Swing peinture et Souris événement scintillement


J'ai une classe qui étend JPanel (ci-dessous), ce panneau se trouve dans un JScrollPane. Il écoute (lui-même) les événements de la souris et essaie de se repositionner (en faisant glisser) et de se redimensionner (sur la roue) pour simuler le mouvement et le zoom de la souris. Le panneau est également responsable de la sortie visuelle principale de mon application. Il stocke un BufferedImage qui est rendu dans la zone visible du JScrollPane (mais sur les graphiques du panneau). La taille et la forme de l'image sont maintenues pour correspondre au zone visible.

Mes problèmes sont en tant que tels;

1) Sur les événements de la souris, j'obtiens une quantité massive de scintillement et de baisses de performances 2) Si je remplace les méthodes paint ou paintComponent avec ma propre méthode de peinture, ce qui est souhaitable pour se débarrasser du scintillement et d'autres problèmes de peinture, j'obtiens toujours le même effet de scintillement et les graphiques tirés d'images chargées qui ont une zone transparente puis colore cette zone en noir. Lorsque j'appelle ma méthode de peinture manuellement sans remplacer la peinture et Méthodes paintComponent, je reçois toujours un scintillement mais les zones transparentes s'affichent correctement.

Je suis nouveau dans la peinture Swing et je fais évidemment quelque chose de mal, quelqu'un pourrait-il me diriger dans la bonne direction pour résoudre ce problème?

Merci

    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.");
        }
    }
Author: James, 2011-05-16

2 answers

En bref, recherchez la double mise en mémoire tampon.

La réponse plus longue...

Remplacer paintComponent. Créez un objet graphique hors écran. Faites de votre peinture sur cet objet. Copiez-le sur les objets graphiques passés dans la méthode paint.

Oh, et se débarrasser de toute la synchronisation. Vous n'en avez pas besoin.

 1
Author: Paul, 2011-05-16 13:44:38

Si vous ajoutez un setOpaque(true) sur la fenêtre, vous informez Swing que vous ferez toute la peinture (en particulier l'arrière-plan) vous-même. Cela peut déjà aider un peu.

MODIFIER

J'ai regardé un peu plus autour de moi et je pense que vous devriez remplacer paintComponent.

Vous pourriez avoir 2 images, et quatre références:

  • imageToPaint = null
  • imageToWriteTo = null
  • bufferImageOne initialisé à une taille appropriée BufferedImage
  • bufferImageTwo initialisé à BufferedImage de taille appropriée

Vous remplaceriez paintComponent pour dessiner l'arrière-plan, puis drawImage(imageToPaint) (si ce n'est pas null, ce qu'il ne devrait pas être)

Vous aurez un fil qui fait la peinture personnalisée d'imageToWriteTo. À la fin, il échange imageToPaint et imageToWriteTo.

Ensuite, vous appelez repaint(). Ces demandes repeindre, avec l'avantage supplémentaire que toutes les demandes de peintures sur la Balançoire la file d'attente est prise ensemble et donne une seule peinture. Pas de revalidation ou de synchronisation s'il vous plait. Ce repeindre est automatiquement effectué sur votre deuxième thread, le Thread de répartition des événements.

De cette façon, la mise à jour de l'image de simulation est découplée de la peinture réelle, et les mises à jour de peinture n'ont pas besoin d'attendre que la simulation termine le dessin. Cela peut résulter d'images légèrement obsolètes (un peu implicites dans l'utilisation de la mise en mémoire tampon) mais cela devrait donner de meilleurs résultats.

En bref, une écriture coûteuse est faite pour imageToWriteTo. La peinture se fait en utilisant imageToPaint. L'écriture coûteuse se termine par l'échange d'imageToWriteTo et d'imageToPaint.

 0
Author: extraneon, 2011-05-16 18:59:14