• 追加された行はこの色です。
  • 削除された行はこの色です。
TITLE:JPanelにマウスで自由曲線を描画
#navi(../)
RIGHT:Posted by [[terai]] at 2005-12-19
*JPanelにマウスで自由曲線を描画 [#n9266a0a]
---
category: swing
folder: PaintPanel
title: JPanelにマウスで自由曲線を描画
tags: [JPanel, MouseListener, MouseMotionListener]
author: aterai
pubdate: 2005-12-19T14:19:26+09:00
description: マウスをドラッグしてパネル上に自由曲線を描画します。
image: https://lh4.googleusercontent.com/_9Z4BYR88imo/TQTQ0y2U6WI/AAAAAAAAAgM/AAHllQ3_VHw/s800/PaintPanel.png
---
* 概要 [#summary]
マウスをドラッグしてパネル上に自由曲線を描画します。

-&jnlp;
-&jar;
-&zip;
#download(https://lh4.googleusercontent.com/_9Z4BYR88imo/TQTQ0y2U6WI/AAAAAAAAAgM/AAHllQ3_VHw/s800/PaintPanel.png)

#screenshot
* サンプルコード [#sourcecode]
#code(link){{
class PaintPanel extends JPanel {
  private static final Stroke STROKE = new BasicStroke(
      3f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
  private transient List<Shape> list;
  private transient Path2D path;
  private transient MouseAdapter handler;

**サンプルコード [#jb2b9b9e]
#code{{
class PaintPanel extends JPanel implements MouseMotionListener, MouseListener {
  private Point startPoint = new Point(-1,-1);
  private Point endPoint   = new Point(-1,-1);
  public PaintPanel() {
    super();
    addMouseMotionListener(this);
    addMouseListener(this);
  }
  public void paintComponent(Graphics g) {
    //super.paintComponent(g);
    Graphics2D g2d = (Graphics2D)g;
    g2d.setStroke(new BasicStroke(3.0F));
    g2d.setPaint(Color.BLACK);
    g2d.drawLine(startPoint.x, startPoint.y,
                 endPoint.x,   endPoint.y);
    startPoint = endPoint;
  }
  public void mouseDragged(MouseEvent e) {
    endPoint = e.getPoint();
    repaint();
  }
  public void mousePressed(MouseEvent e) {
    startPoint = e.getPoint();
  }
  public void mouseMoved(MouseEvent e) {}
  public void mouseExited(MouseEvent e) {}
  public void mouseEntered(MouseEvent e) {}
  public void mouseReleased(MouseEvent e) {}
  public void mouseClicked(MouseEvent e) {}
}
}}
  @Override public void updateUI() {
    removeMouseMotionListener(handler);
    removeMouseListener(handler);
    super.updateUI();
    handler = new MouseAdapter() {
      @Override public void mousePressed(MouseEvent e) {
        path = new Path2D.Double();
        list.add(path);
        Point p = e.getPoint();
        path.moveTo(p.x, p.y);
        repaint();
      }

**解説 [#t35e18f2]
上記のサンプルでは、パネル上でマウスがドラッグされている場合、その軌跡を短い直線でつなぎ合わせて描画することで、お絵かきしています。

- マウスがクリックされた場所を始点にする
- ドラッグされた時の位置を終点にしてパネルをrepaint
- paintComponentをオーバーライドして、上記の始点、終点で直線を描画
- 次の直線のための始点を現在の終点に変更

//**参考リンク
**コメント [#j1077dd9]
- マウス右ボタンをドラッグで消しゴム…のテスト -- [[terai]] &new{2010-01-12 (火) 16:16:59};
#code{{
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.swing.*;

class PaintPanel2 extends JPanel implements MouseMotionListener, MouseListener {
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      public void run() {
        createAndShowGUI();
      @Override public void mouseDragged(MouseEvent e) {
        Point p = e.getPoint();
        path.lineTo(p.x, p.y);
        repaint();
      }
    });
    };
    addMouseMotionListener(handler);
    addMouseListener(handler);
    list = new ArrayList<Shape>();
  }
  public static void createAndShowGUI() {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.getContentPane().add(new PaintPanel2());
    frame.setSize(320, 240);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  private final Polygon polygon = new Polygon();
  private Point startPoint = new Point(-1,-1);
  private BufferedImage offImage = null;
  private BufferedImage backImage = null;
  private TexturePaint texture  = makeTexturePaint();
  private int[] pix = new int[320 * 240];
  public PaintPanel2() {
    super();
    addMouseMotionListener(this);
    addMouseListener(this);
    offImage = new BufferedImage(320, 240, BufferedImage.TYPE_4BYTE_ABGR);
    PixelGrabber pg = new PixelGrabber(offImage, 0, 0, 320, 240, pix, 0, 320);
    try {
      pg.grabPixels();
    } catch (Exception e) {
      e.printStackTrace();
    }
    backImage = new BufferedImage(320, 240, BufferedImage.TYPE_4BYTE_ABGR);
    Graphics2D g2 = backImage.createGraphics();
    g2.setPaint(texture);
    g2.fillRect(0,0,320,240);
    g2.dispose();
  }

  private static BufferedImage makeBGImage() {
    Color color = new Color(200,150,100,50);
    int cs = 6, sz = cs*cs;
    BufferedImage img = new BufferedImage(sz,sz,BufferedImage.TYPE_4BYTE_ABGR);
    Graphics2D g2 = img.createGraphics();
    g2.setPaint(color);
    g2.fillRect(0,0,sz,sz);
    for (int i=0; i*cs<sz; i++) {
      for (int j=0; j*cs<sz; j++) {
        if ((i+j)%2==0) g2.fillRect(i*cs, j*cs, cs, cs);
  @Override protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (list != null) {
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setPaint(Color.BLACK);
      g2.setStroke(STROKE);
      for (Shape s : list) {
        g2.draw(s);
      }
      g2.dispose();
    }
    g2.dispose();
    return img;
  }
  private static TexturePaint makeTexturePaint() {
    BufferedImage img = makeBGImage();
    int w = img.getWidth(), h = img.getHeight();
    Rectangle2D r2d = new Rectangle2D.Float(0,0,w,h);
    return new TexturePaint(img, r2d);
  }
  @Override
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    if (backImage!=null) {
      ((Graphics2D)g).drawImage(backImage, 0, 0, this);
    }
    if (offImage!=null) {
      MemoryImageSource producer = new MemoryImageSource(320, 240, pix, 0, 320);
      g.drawImage(createImage(producer), 0, 0, null);
      g.dispose();
    }
  }
  int penc = 0x0;
  public void mouseDragged(MouseEvent e) {
    Point pt = e.getPoint();
    double xDelta = e.getX() - startPoint.getX();
    double yDelta = e.getY() - startPoint.getY();
    double delta = Math.max(Math.abs(xDelta), Math.abs(yDelta));

    double xIncrement = xDelta / delta;
    double yIncrement = yDelta / delta;
    double xStart = startPoint.x;
    double yStart = startPoint.y;
    for (int i=0; i<delta; i++) {
      Point p = new Point((int)xStart, (int)yStart);
      if (p.x<0 || p.y<0 || p.x>=320 || p.y>=240) break;
      pix[p.x + p.y * 320] = penc;
      if (p.x+1<320) pix[p.x + p.y * 320 +1 ]  = penc;
      if (p.x+2<320) pix[p.x + p.y * 320 +2 ]  = penc;
      if (p.y+1<240) pix[p.x + (p.y +1) * 320] = penc;
      if (p.y+2<240) pix[p.x + (p.y +2) * 320] = penc;
      if (p.x>1)     pix[p.x + p.y * 320 -1 ]  = penc;
      if (p.x>2)     pix[p.x + p.y * 320 -2 ]  = penc;
      if (p.y>1)     pix[p.x + (p.y -1) * 320] = penc;
      if (p.y>2)     pix[p.x + (p.y -2) * 320] = penc;
      repaint(p.x-2, p.y-2, 4, 4);
      xStart += xIncrement;
      yStart += yIncrement;
    }
    startPoint = pt;
  }
  private Rectangle getRepaintRectangle(Point srcPoint, Point destPoint) {
    polygon.reset();
    polygon.addPoint(srcPoint.x,  srcPoint.y);
    polygon.addPoint(destPoint.x, srcPoint.y);
    polygon.addPoint(destPoint.x, destPoint.y);
    polygon.addPoint(srcPoint.x,  destPoint.y);
    return polygon.getBounds();
  }
  public void mousePressed(MouseEvent e) {
    startPoint = e.getPoint();
    penc = (e.getButton()==MouseEvent.BUTTON1)?0xff000000:0x0;
  }
  public void mouseMoved(MouseEvent e) {}
  public void mouseExited(MouseEvent e) {}
  public void mouseEntered(MouseEvent e) {}
  public void mouseReleased(MouseEvent e) {}
  public void mouseClicked(MouseEvent e) {}
}
}}

* 解説 [#explanation]
上記のサンプルでは、パネル上でマウスがドラッグされた場合その軌跡を短い直線でつなぎ合わせることで曲線を描画しています。

- 新規`Path2D`を生成してマウスがクリックされた場所を`Path2D.moveTo(...)`で始点に設定
- ドラッグされた時の位置を`Path2D.lineTo(...)`で終点にしてパネルを`repaint()`
- `paintComponent(...)`をオーバーライドして、上記の直線のリスト(`Path2D`のリスト)を描画
- 次の直線のための始点を現在の終点に変更

* 参考リンク [#reference]
- [[MemoryImageSourceで配列から画像を生成>Swing/MemoryImageSource]]

* コメント [#comment]
#comment
- マウス右ボタンをドラッグで消しゴム…のテスト -- &user(aterai); &new{2010-01-12 (火) 16:16:59};
-- 追記:不要なコードを削除。 -- &user(aterai); &new{2010-04-30 (金) 19:26:37};
-- [[MemoryImageSourceで配列から画像を生成>Swing/MemoryImageSource]]に移動。 -- &user(aterai); &new{2010-06-07 (月) 15:21:37};
- わからん!! --  &new{2010-04-30 (金) 18:11:55};
- 直線を`Path2D`で保存する方法に変更。 -- &user(aterai); &new{2015-12-08 (火) 16:16:59};

#comment