Dessiner avec Swing (2): un programme de dessin simple

Dessiner avec Swing (2): un programme de dessin simple

Introduction

Cet exemple de programme permet le dessin de lignes colorées ayant différentes épaisseurs. Le dessin se fait à main libre en tirant avec la souris. Par cet exemple nous apprenons:

  • comment des repaint multiples sont réunis
  • comment nous pouvons restreindre la zone à dessiner

Réunir des repaint multiples

Chaque ligne dessinée se compose d'une suite de cercles (Graphics#fillOval), qui sont déterminés par interpolation entre deux points de souris. Avec ça, repaint(x,y,brushSize,brushSize) est appelé à plusieurs reprises dans une boucle:

    @Override
    public void mouseDragged(final MouseEvent e) {
        double xDelta = e.getX() - lastPoint.getX();
        double yDelta = e.getY() - lastPoint.getY();
        double delta = Math.max(Math.abs(xDelta), Math.abs(yDelta));
        double xIncrement = xDelta / delta;
        double yIncrement = yDelta / delta;
        double xStart = lastPoint.getX();
        double yStart = lastPoint.getY();
        for (int i = 0; i < delta; i++) {
            Point interpolated = new Point((int) xStart, (int) yStart);
            draw(interpolated);
            xStart += xIncrement;
            yStart += yIncrement;
        }
        draw(e.getPoint());
        lastPoint = e.getPoint();
    }
 
    private void draw(final Point start) {
        int brushSize = drawingPanel.getBrushSize();
        int x = start.x - (brushSize / 2) + 1;
        int y = start.y - (brushSize / 2) + 1;
        drawingPanel.getG2d().fillOval(x, y,brushSize, brushSize);
        drawingPanel.repaint(x, y,brushSize, brushSize);
    }

Quand des repaint() multiples sont appelé sur un composant avant que le premier appel soit traité, le RepaintManager de Swing peut réunir les appels multiples en un seul appel. Quand des appels multiples sont réunis, le rectangle "clip" résultant est égal à la réunion des rectangles qui sont contenus dans les appels réunis.

Restreindre la zone à dessiner

Pendant que Swing essaie de rendre le processus de dessin aussi performant que possible, l'implémentation de la méthode paintComponent() peut elle-même avoir un impact considérable sur la performance globale. Nous pouvons influencer ce processus, en utilisant les informations "clip" pour restreindre la zone à dessiner.

Si ton composant est simple -- par exemple, si c'est un bouton -- alors ce n'est pas la peine
de traiter le graphique pour dessiner uniquement la partie qui chevauche le rectangle "clip"; il est préférable de dessiner le composant intégralement et laisser agir les optimisations standard. Mais quand tu as construit un composant avec sortie compliquée, comme un composant de texte, alors il est nécessaire que ton code utilise les informations "clip" pour restreindre la zone à dessiner. La méthode Graphics#getClipBounds nous donne accès au rectangle "clip":

    @Override
    public void paintComponent(final Graphics g) {
        super.paintComponent(g);
        // initialises the image with the first paint
        // or checks the image size with the current panelsize
        if (image == null || image.getWidth(this) < getSize().width || image.getHeight(this) < getSize().height) {
            resetImage();
        }
        Rectangle r = g.getClipBounds();
        g.drawImage(image, r.x, r.y, r.width + r.x, r.height + r.y, r.x, r.y, r.width + r.x, r.height + r.y, null);
    }

L'appel à drawImage(..) avec les paramètres indiqués ne redessine que la partie de l'image qui chevauche le rectangle "clip".

Code source intégral

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
 
public class DrawOnImage extends JFrame implements ActionListener {
 
    private DrawingPanel drawingPanel;
    private JPanel buttonPanel;
    private JButton clearButton,  upSize,  downSize;
 
    public DrawOnImage() {
        super("DrawOnImage");
        drawingPanel = new DrawingPanel();
        buttonPanel = new JPanel();
        buttonPanel.setLayout(new GridLayout(2, 0, 2, 2));
        addButton(Color.BLACK);
        addButton(Color.BLUE);
        addButton(Color.GREEN);
        upSize = addButton(null);
        upSize.setText("+");
        addButton(Color.RED);
        addButton(Color.ORANGE);
        clearButton = addButton(null);
        clearButton.setText("Clear");
        downSize = addButton(null);
        downSize.setText("-");
        getContentPane().add(new JScrollPane(drawingPanel));
        getContentPane().add(buttonPanel, BorderLayout.SOUTH);
    }
 
    private JButton addButton(final Color color) {
        JButton button = new JButton();
        button.setBackground(new Color(230, 240, 250));
        button.setBorder(BorderFactory.createEtchedBorder());
        if (color != null) {
            button.setForeground(Color.WHITE);
            button.setBackground(color);
        }
        button.setText("Paint");
        buttonPanel.add(button);
        button.addActionListener(this);
        return (button);
    }
 
    public void actionPerformed(final ActionEvent e) {
        String s = e.getActionCommand();
        if (s.equals("Paint")) {
            JButton button = (JButton) e.getSource();
            drawingPanel.setPaintColor(button.getBackground());
        } else if (s.equals("Clear")) {
            drawingPanel.clearPaint();
        } else if (s.equals("+")) {
            drawingPanel.increaseBrushSize();
        } else {
            drawingPanel.decreaseBrushSize();
        }
    }
 
    public static void main(final String *  args) {
        Runnable gui = new Runnable() {
 
            @Override
            public void run() {
                JFrame frame = new DrawOnImage();
                frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        };
        //GUI must start on EventDispatchThread:
        SwingUtilities.invokeLater(gui);
    }
}
import java.awt.Point;
import java.awt.event.MouseEvent;
import javax.swing.event.MouseInputAdapter;
 
class MouseHandler extends MouseInputAdapter {
 
    private DrawingPanel drawingPanel;
    private Point lastPoint;
 
    MouseHandler(DrawingPanel drawingPanel) {
        this.drawingPanel = drawingPanel;
    }
 
    @Override
    public void mousePressed(final MouseEvent e) {
        lastPoint = e.getPoint();
        draw(lastPoint);
    }
 
    @Override
    public void mouseDragged(final MouseEvent e) {
        double xDelta = e.getX() - lastPoint.getX();
        double yDelta = e.getY() - lastPoint.getY();
        double delta = Math.max(Math.abs(xDelta), Math.abs(yDelta));
        double xIncrement = xDelta / delta;
        double yIncrement = yDelta / delta;
        double xStart = lastPoint.getX();
        double yStart = lastPoint.getY();
        for (int i = 0; i < delta; i++) {
            Point interpolated = new Point((int) xStart, (int) yStart);
            draw(interpolated);
            xStart += xIncrement;
            yStart += yIncrement;
        }
        draw(e.getPoint());
        lastPoint = e.getPoint();
    }
 
    private void draw(final Point start) {
        int brushSize = drawingPanel.getBrushSize();
        int x = start.x - (brushSize / 2) + 1;
        int y = start.y - (brushSize / 2) + 1;
        drawingPanel.getG2d().fillOval(x, y,brushSize, brushSize);
        drawingPanel.repaint(x, y,brushSize, brushSize);
    }
}
import java.awt.*;
import javax.swing.*;
 
class DrawingPanel extends JComponent {
 
    private Image image;
    private Graphics2D g2d;
    private int brushSize = 5;
 
    public DrawingPanel() {
        super();
        setPreferredSize(new Dimension(300, 300));
        MouseHandler mh = new MouseHandler(this);
        addMouseListener(mh);
        addMouseMotionListener(mh);
    }
 
    @Override
    public Dimension getPreferredSize() {
        Dimension size = super.getPreferredSize();
        if (image != null) {
            size.width = image.getWidth(this);
            size.height = image.getHeight(this);
        }
        return size;
    }
 
    public void setPaintColor(final Color color) {
        g2d.setColor(color);
    }
 
    public void clearPaint() {
        g2d.setColor(Color.white);
        g2d.fillRect(0, 0, getWidth(), getHeight());
        repaint();
        g2d.setColor(Color.black);
    }
 
    public void increaseBrushSize() {
        brushSize += 2;
    }
 
    public void decreaseBrushSize() {
        brushSize -= 2;
        if (brushSize <= 0) {
            brushSize = 1;
        }
    }
 
    @Override
    public void paintComponent(final Graphics g) {
        super.paintComponent(g);
        // initialises the image with the first paint
        // or checks the image size with the current panelsize
        if (image == null || image.getWidth(this) < getSize().width || image.getHeight(this) < getSize().height) {
            resetImage();
        }
        Rectangle r = g.getClipBounds();
        g.drawImage(image, r.x, r.y, r.width + r.x, r.height + r.y, r.x, r.y, r.width + r.x, r.height + r.y, null);
    }
 
    private void resetImage() {
        Image saveImage = image;
        Graphics2D saveG2d = g2d;
        image = createImage(getWidth(), getHeight());
        g2d = (Graphics2D) image.getGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(Color.white);
        g2d.fillRect(0, 0, getWidth(), getHeight());
        g2d.setColor(Color.black);
        if (saveG2d != null) {
            g2d.setColor(saveG2d.getColor());
            g2d.drawImage(saveImage, 0, 0, this);
        }
    }
 
    public Graphics2D getG2d() {
        return g2d;
    }
 
    public int getBrushSize() {
        return brushSize;
    }
}
A voir également
Ce document intitulé « Dessiner avec Swing (2): un programme de dessin simple » issu de CodeS SourceS (codes-sources.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.
Rejoignez-nous