TITLE:JPanelに表示した画像のズームとスクロール
#navi(../)
#tags(AffineTransform, JPanel, MouseListener, MouseMotionListener, MouseWheelListener)
RIGHT:Posted by &author(aterai); at 2014-04-21
* JPanelに表示した画像のズームとスクロール [#n09fa895]
`JPanel`に表示した画像に`AffineTransform`による変換を適用して、マウスを使った拡大・縮小・移動を実行します。[https://forums.oracle.com/thread/1263955 How to implement Zoom & Pan in Java using Graphics2D]のコードを参考・引用しています。

#download
#ref(https://lh3.googleusercontent.com/-c5Y9hCoRQbU/U1PhhnitgFI/AAAAAAAACD0/ZXIcyPywcr0/s800/ZoomingAndPanning.png)

** サンプルコード [#t414a62d]
#code(link){{
class ZoomAndPanHandler extends MouseAdapter {
  private static final double ZOOM_MULTIPLICATION_FACTOR = 1.2;
  private static final int MIN_ZOOM = -10;
  private static final int MAX_ZOOM = 10;
  private static final int EXTENT = 1;
  private final BoundedRangeModel zoomRange = new DefaultBoundedRangeModel(
    0, EXTENT, MIN_ZOOM, MAX_ZOOM + EXTENT);
  private final AffineTransform coordTransform = new AffineTransform();
  private final Point dragStartPoint = new Point();
  @Override public void mousePressed(MouseEvent e) {
    dragStartPoint.setLocation(e.getPoint());
  }
  @Override public void mouseDragged(MouseEvent e) {
    Point dragEndPoint = e.getPoint();
    Point dragStart = transformPoint(dragStartPoint);
    Point dragEnd   = transformPoint(dragEndPoint);
    coordTransform.translate(dragEnd.x - dragStart.x, dragEnd.y - dragStart.y);
    dragStartPoint.setLocation(dragEndPoint);
    ((Component) e.getSource()).repaint();
  }
  @Override public void mouseWheelMoved(MouseWheelEvent e) {
    int dir = e.getWheelRotation();
    int z = zoomRange.getValue();
    zoomRange.setValue(z + (dir > 0 ? -1 : 1));
    if (z == zoomRange.getValue()) {
      return;
    }
    Point p = e.getPoint();
    Point p1 = transformPoint(p);
    double scale = dir > 0 ? 1 / ZOOM_MULTIPLICATION_FACTOR : ZOOM_MULTIPLICATION_FACTOR;
    coordTransform.scale(scale, scale);
    Point p2 = transformPoint(p);
    coordTransform.translate(p2.getX() - p1.getX(), p2.getY() - p1.getY());
    ((Component) e.getSource()).repaint();
  }
  //https://forums.oracle.com/thread/1263955
  //How to implement Zoom & Pan in Java using Graphics2D
  private Point transformPoint(Point p1) {
    Point p2 = new Point();
    try {
      AffineTransform inverse = coordTransform.createInverse();
      inverse.transform(p1, p2);
    } catch (NoninvertibleTransformException ex) {
      ex.printStackTrace();
    }
    return p2;
  }
  public AffineTransform getCoordTransform() {
    return coordTransform;
  }
}
}}

** 解説 [#v5dc7b5e]
`AffineTransform#createInverse()`で取得した`AffineTransform`オブジェクトでマウス位置の逆変換を行い、現在表示されている倍率でのズームの中心や移動距離などを計算しています。

- ズーム
-- ホイール上回転でマウスの位置を中心にして拡大
-- ホイール下回転でマウスの位置を中心にして縮小
- スクロール
-- マウスドラッグで移動

** 参考リンク [#x2408272]
- [https://forums.oracle.com/thread/1263955 How to implement Zoom & Pan in Java using Graphics2D]
- [http://sozai-free.com/ 2000ピクセル以上のフリー写真素材集]

** コメント [#y2de5eb2]
#comment