Java6/7を使用して再利用可能なJPanel画像ビューアを作成しようとしています。サイズ変更中にJPanel(ビューポートの原点)の左上隅が移動しない限り、mouseDraggedを使用して画像ドラッグを実装したり、JPanelのサイズを変更して画像をトリミングまたは表示したりするのに問題はありません。パネルに合わせて画像のサイズを変更したくないことに注意してください。パネルのサイズを変更してトリミングしている間、画像を画面に固定したままにしておきたい。
フレームの左端をドラッグしてビューポートの原点を移動すると、サイズ変更中に画像の原点を動的に再計算しても、サイズ変更操作中にかなりの画像ジッターが発生します。これは、paintComponent()が呼び出されてから、drawImage()の呼び出しによって画像が実際にレンダリングされるまでの間に、ビューポートの原点が変更されたことが原因のようです。上記のジッターを排除する方法について何か考えはありますか?前もって感謝します。
以下の2つのクラスは、動作を示す必要があります。
/***** ImageViewer.java *****/
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;
import javax.imageio.ImageIO;
public class ImageViewer extends JFrame
{
private ImagePanel canvas;
private BufferedImage theImage;
private JFileChooser theChooser;
public ImageViewer( BufferedImage bi )
{
canvas= new ImagePanel( );
add( canvas, BorderLayout.CENTER );
JPanel control= new JPanel();
JButton loadBtn= new JButton( "Load Image" );
control.add( loadBtn );
add( control, BorderLayout.NORTH );
loadBtn.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent e )
{ doLoad(); }
});
theChooser= new JFileChooser();
}
public void doLoad()
{
File iFile = null;
int retVal = theChooser.showOpenDialog(this);
if (retVal == JFileChooser.APPROVE_OPTION) {
iFile = theChooser.getSelectedFile();
try {
theImage = ImageIO.read(iFile);
} catch (FileNotFoundException ie) {
System.err.println("File not found");
System.exit(1);
} catch (IOException ie) {
System.err.println("IOException");
System.exit(1);
}
canvas.setImage( theImage );
repaint();
}
}
/**
* @param args
*/
public static void main(String[] args)
{
BufferedImage bImage= null;
ImageViewer theApp= new ImageViewer( bImage );
theApp.setDefaultCloseOperation(EXIT_ON_CLOSE);
theApp.pack();
theApp.setVisible( true );
}
}
/***** ImagePanel.java *****/
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.JPanel;
public class ImagePanel extends JPanel
{
private BufferedImage theImage;
private Dimension preferredSize;
private double scaleF = 1.0; // image scaling factor
private Point2D.Double deltaLoc; // dx, dy in scaled units for image upper
// left corner from origin to current loc
// inside or outside viewport
private Point panelLastLoc; // last location of upper left corner of the
// viewport in screen coordinates
private int lastX;
private int lastY;
private PanelState state = PanelState.IDLE;
public enum PanelState
{
IDLE, RESIZING, PMOVING, IMOVING
};
public ImagePanel()
{
setBackground(Color.GRAY);
preferredSize = new Dimension(400, 400);
addHierarchyListener(new HierarchyListener() {
public void hierarchyChanged(HierarchyEvent he)
{
doComponentChanged(he);
}
});
addHierarchyBoundsListener(new HierarchyBoundsAdapter() {
public void ancestorMoved(HierarchyEvent he)
{
doComponentMoved();
}
public void ancestorResized(HierarchyEvent he)
{
doAncestorResizing();
}
});
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent ce)
{
doComponentResized();
}
});
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me)
{
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
state = PanelState.IMOVING;
lastX = me.getX();
lastY = me.getY();
}
public void mouseReleased(MouseEvent me)
{
setCursor(Cursor.getDefaultCursor());
state = PanelState.IDLE;
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent me)
{
doImageMoved(me);
}
});
}
public ImagePanel(BufferedImage bi)
{
theImage = bi;
preferredSize = new Dimension(theImage.getWidth(), theImage.getHeight());
}
public Dimension getPreferredSize()
{
return preferredSize;
}
private void doComponentChanged(HierarchyEvent he)
{
if (isShowing()) {
panelLastLoc = getLocationOnScreen();
}
state = PanelState.IDLE;
}
private void doImageMoved(MouseEvent me)
{
if (!isShowing() || theImage == null)
return;
switch (state)
{
case RESIZING:
error("IMOVING->RESIZING");
break;
case PMOVING:
error("IMOVING->PMOVING");
break;
case IDLE:
error("IMOVING->IDLE");
break;
case IMOVING:
if (contains(me.getX(), me.getY())) {
int dx = me.getX() - lastX;
int dy = me.getY() - lastY;
lastX = me.getX();
lastY = me.getY();
deltaLoc.x += dx;
deltaLoc.y += dy;
repaint();
}
return;
}
}
private void doComponentMoved()
{
if (!isShowing() || theImage == null)
return;
switch (state)
{
case RESIZING:
return;
case IMOVING:
error("IMOVING->PMOVING");
break;
case PMOVING:
case IDLE:
panelLastLoc = getLocationOnScreen();
state = PanelState.PMOVING;
}
return;
}
private void doAncestorResizing()
{
if (!isShowing() || theImage == null)
return;
switch (state)
{
case IMOVING:
error("IMOVING->RESIZING");
return;
case RESIZING:
case PMOVING:
case IDLE:
Point cLoc = getLocationOnScreen();
state = PanelState.RESIZING;
if (theImage != null) {
deltaLoc.x -= cLoc.x - panelLastLoc.x;
deltaLoc.y -= cLoc.y - panelLastLoc.y;
}
panelLastLoc = cLoc;
}
}
private void doComponentResized()
{
Point cLoc = getLocationOnScreen();
switch (state)
{
case RESIZING:
state = PanelState.IDLE;
break;
case IMOVING:
error("IMOVING->RESIZED");
break;
case PMOVING:
state = PanelState.IDLE;
break;
case IDLE:
break;
}
if (theImage != null) {
deltaLoc.x -= cLoc.x - panelLastLoc.x;
deltaLoc.y -= cLoc.y - panelLastLoc.y;
}
panelLastLoc = cLoc;
}
public void setImage(BufferedImage nI)
{
theImage = nI;
scaleF = 1.0;
state = PanelState.IDLE;
deltaLoc = new Point2D.Double(Math.round((getWidth() - theImage.getWidth()) / 2.0),
Math.round((getHeight() - theImage.getHeight()) / 2.0));
panelLastLoc = getLocationOnScreen();
}
private void error(String em)
{
System.err.println(em);
System.exit(1);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
if (theImage == null)
return;
/*
* Check if the viewport has resized or moved. The check below is
* performed on every repaint.
*/
Point cLoc = getLocationOnScreen();
Dimension cDim = getSize();
if (state == PanelState.RESIZING || state == PanelState.IDLE) {
deltaLoc.x -= cLoc.x - panelLastLoc.x;
deltaLoc.y -= cLoc.y - panelLastLoc.y;
}
/*
System.err.println("image left edge= "
+ Math.round(cLoc.x + deltaLoc.x) + "; state= " + state
+ "; cDim= " + cDim + "; cLoc= " + cLoc + "; pLLoc= "
+ panelLastLoc + "; dLoc= " + deltaLoc);
*/
// In all cases update panelLastLoc
panelLastLoc = cLoc;
g2.drawImage(theImage, (int) Math.round(deltaLoc.x),
(int) Math.round(deltaLoc.y), null);
}
}