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:
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.
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".
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; } }