Can't get zooming into a selected area to work correctly.



Hi all again,

I am having problems getting my zoom into area working properly(at all)?

The idea being that the mouse is dragged over an area and the area within the selection rectangle is scaled and then centred to fit in the scrollpane.

I am using getCenterX & Y of the selection rectangle to centre in scrollpane.

The image is scaled, but does not centre within the scroll pane.

I also want to be able to do the following:
Once The selection has been zoomed and centred, I want to be able to make another selection within that selection and zoom in again.

I did manage to make the image selection work earlier, but once it centred the zoomed selection, doing another selection gave odd results sometimes moving to top left of image or some other random place within the image.

I am posting the code, which has been derived from Some code Peter posted earlier.

I would appreciate any help here as I am stuck.

Below is the code, all I have changed from original code is to call zoomArea() from mouse released, and added a resize component event and a method from the frame to pass the scrollpane reference to the image select componenet.

ImageSelectComponenent:

package peted;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.event.*;

public class ImageSelectComponent extends JComponent implements MouseInputListener
{
private BufferedImage _image;
private float _scale = 1.0f;

private boolean _fDragging;
private Point2D _ptDragStart;
private Point2D _ptDragCur;
private Rectangle2D _rectDragBounds;

private AffineTransform _transformToClient = new AffineTransform();
private AffineTransform _transformFromClient = new AffineTransform();
private JScrollPane _scrollPane;

private boolean isCentred = true;
public ImageSelectComponent()
{
this.addMouseListener(this);
this.addMouseMotionListener(this);
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {

EventQueue.invokeLater(new Runnable() {


public void run() {
if(_scrollPane != null) {
JViewport vp = _scrollPane.getViewport();
Point p = null;
if (!isCentred) {
p = new Point((int)(_RectFromDrag().getCenterX() * _scale),
(int)(_RectFromDrag().getCenterY() * _scale));

} else {
p = new Point(
(int)((getWidth() - vp.getWidth()) / 2),
(int)((getHeight() - vp.getHeight()) / 2));
}
assert(p != null);
vp.setViewPosition(p);
revalidate();
repaint();

}
}
});
}
});
}

public void setImage(BufferedImage image)
{
// I'm only copying the provided image because later on, I found that
// the entire image got cleared to black when I tried to write to it.
// Seems to be related to the fact that the image is from a file, and
// this might be a Mac-only issue (bug?). In any case, copying the
// passed-in image to a new one allows me to write to it later without
// any trouble.
_image = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);

Graphics2D gfx = _image.createGraphics();

gfx.drawImage(image, 0, 0, null);
gfx.dispose();

_UpdateMetrics();
}

public BufferedImage getImage()
{
return _image;
}

public void setScale(float scale)
{
isCentred = true;
_scale = scale;
_transformToClient = AffineTransform.getScaleInstance(_scale, _scale);
_transformFromClient = AffineTransform.getScaleInstance(1 / _scale, 1 / _scale);
_UpdateMetrics();
}

public float getScale()
{
return _scale;
}

protected void paintComponent(Graphics gfxArg)
{
Graphics2D gfx = (Graphics2D)gfxArg;

// Draw the image with the current transformation
if (_image != null)
{
AffineTransform transformSav = gfx.getTransform();

gfx.transform(_transformToClient);
// gfx.drawImage(_image, 0, 0, Math.round(_image.getWidth() * _scale), Math.round(_image.getHeight() * _scale), null);
gfx.drawImage(_image, 0, 0, null);
gfx.setTransform(transformSav);
}

// Drag data is already in client coordinates
if (_fDragging)
{
gfx.draw(_RectFromDrag());
}
}

private void _UpdateMetrics()
{
if (_image != null)
{
Rectangle2D rectBounds =
Util.RectTransform(_transformToClient, new Rectangle(0, 0, _image.getWidth(), _image.getHeight()));
// Dimension sizeNew = new Dimension(Math.round(_image.getWidth() * _scale), Math.round(_image.getHeight() * _scale));
Dimension sizeNew = new Dimension((int)Math.round(rectBounds.getWidth()), (int)Math.round(rectBounds.getHeight()));

setMinimumSize(sizeNew);
setMaximumSize(sizeNew);
setPreferredSize(sizeNew);
setSize(sizeNew);
}
}

public void mouseClicked(MouseEvent arg0)
{
}

public void mouseEntered(MouseEvent arg0)
{
}

public void mouseExited(MouseEvent arg0)
{
}

public void mousePressed(MouseEvent arg0)
{
_fDragging = true;
_ptDragStart = arg0.getPoint();

// To determine limits of mouse input, start with a rectangle in
// the image coordinates describing the whole image, and transform
// that rectangle into client coordinates
// _rectDragBounds = new Rectangle(0, 0,
// Math.round(_image.getWidth() * _scale), Math.round(_image.getHeight() * _scale));
_rectDragBounds = Util.RectTransform(_transformToClient,
new Rectangle(0, 0, _image.getWidth(), _image.getHeight()));
}

public void mouseReleased(MouseEvent arg0)
{
_fDragging = false;

if (_image != null)
{
// Rectangle2D rectComponent = _RectFromDrag(),
// rectImage = new Rectangle((int)Math.round(rectComponent.getX() / _scale),
// (int)Math.round(rectComponent.getY() / _scale),
// (int)Math.round(rectComponent.getWidth() / _scale),
// (int)Math.round(rectComponent.getHeight() / _scale));
Graphics2D gfx = _image.createGraphics();

//gfx.transform(_transformFromClient);
gfx.setColor(Color.WHITE);
//gfx.fill(rectImage);
//gfx.fill(_RectFromDrag());
zoomArea();
//gfx.transform(_transformToClient);
gfx.dispose();
}

this.repaint();
}

private void zoomArea() {
isCentred = false;
Rectangle2D zoomRect = _RectFromDrag();
double scale = calculateSelectionToFit(new Dimension((int)(zoomRect.getWidth()), (int)(zoomRect.getHeight())));
_scale = (float)scale;
_transformToClient.scale(_scale, _scale);

Point rectSelectPoint = new Point((int)(zoomRect.getCenterX() * _scale),
(int)(zoomRect.getCenterY() * _scale));

JViewport vp = _scrollPane.getViewport();
vp.setViewPosition(rectSelectPoint);
this.setPreferredSize(new Dimension(_image.getWidth(), _image.getHeight()));

}


public void setScrollPane(JScrollPane scrollPane) {
_scrollPane = scrollPane;
}



public void mouseDragged(MouseEvent arg0)
{
_ptDragCur = Util.PointConstrained(_rectDragBounds, arg0.getPoint());

this.repaint();
}

// used by zoom selection to fit zoom selection within scroll pane.
private double calculateSelectionToFit(Dimension dimension) {
double xFactor = (_image.getWidth()) / dimension.getWidth();
double yFactor = (_image.getHeight()) / dimension.getHeight();

// use smaller of the factors to maintain aspect ratio
double keepAspectRatio = xFactor > yFactor ? yFactor : xFactor;
return keepAspectRatio;

}

public void mouseMoved(MouseEvent arg0)
{
}

private Rectangle2D _RectFromDrag()
{
int x = (int)Math.min(_ptDragStart.getX(), _ptDragCur.getX()),
y = (int)Math.min(_ptDragStart.getY(), _ptDragCur.getY()),
dx = (int)Math.abs(_ptDragStart.getX() - _ptDragCur.getX()),
dy = (int)Math.abs(_ptDragStart.getY() - _ptDragCur.getY());

return new Rectangle(x, y, dx, dy);
}
}


ImageSelectFrame:

package peted;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.IOException;

import javax.imageio.*;
import javax.swing.*;
import java.awt.Dimension;

public class ImageSelectFrame extends JFrame
{

/**
* This method initializes this
*
*/
private void initialize() {
this.setPreferredSize(new Dimension(800, 600));
this.setSize(new Dimension(424, 229));

}

/**
* @param args
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new ImageSelectFrame();

frame.pack();
frame.setVisible(true);
}
});
}

private ImageSelectFrame()
{
super("TestImageSelect");
initialize();

this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);

final ImageSelectComponent isc = new ImageSelectComponent();
JScrollPane pane = new JScrollPane(isc);

this.add(pane);
isc.setScrollPane(pane);
JMenuBar mbar = new JMenuBar();

JMenu menu = new JMenu("File");

JMenuItem mitem = new JMenuItem("Open...");
mitem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
_PromptImage(isc);
}
});
menu.add(mitem);
menu.addSeparator();

mitem = new JMenuItem("Exit");
mitem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
ImageSelectFrame.this.dispose();
}
});

menu.add(mitem);
mbar.add(menu);

menu = new JMenu("Image");
mitem = new JMenuItem("Set Scale...");
mitem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
_PromptScale(isc);
}
});
menu.add(mitem);
mbar.add(menu);

this.setJMenuBar(mbar);
}

private void _PromptImage(ImageSelectComponent isc)
{
JFileChooser chooser = new JFileChooser();

if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION)
{
try
{
BufferedImage image = ImageIO.read(chooser.getSelectedFile());

if (image == null)
{
throw new Exception("Unrecognized image file contents");
}
isc.setImage(image);
}
catch (Exception e)
{
JOptionPane.showMessageDialog(this,
"Unable to open image file. Error: \"" + e.getMessage() + "\"",
"Invalid image",
JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
}
}
}

private void _PromptScale(ImageSelectComponent isc)
{
try
{
float scale = Float.parseFloat(
JOptionPane.showInputDialog(
this,
"Enter the new scale as a decimal:",
Float.toString(isc.getScale())));

if (scale <= 0)
{
throw new Exception("Scale must be a decimal value greater than 0");
}

isc.setScale(scale);
}
catch (Exception e)
{
JOptionPane.showMessageDialog(this,
e.getMessage(),
"Invalid scale",
JOptionPane.ERROR_MESSAGE);

e.printStackTrace();
}
}
} // @jve:decl-index=0:visual-constraint="10,10"

Util:

package peted;

import java.awt.*;
import java.awt.geom.*;

public class Util
{
public static Rectangle2D RectTransform(AffineTransform transform, Rectangle2D rect)
{
// A non-quadrant rotation would create a non-rectangular result
if ((transform.getType() & AffineTransform.TYPE_GENERAL_ROTATION) != 0)
{
throw new IllegalArgumentException("the only valid rotation type for transform is TYPE_QUADRANT_ROTATION");
}

Shape shapeNew = transform.createTransformedShape(rect);
Rectangle2D rectBounds = shapeNew.getBounds2D();

return new Rectangle((int)Math.round(rectBounds.getMinX()),
(int)Math.round(rectBounds.getMinY()),
(int)Math.round(rectBounds.getWidth()),
(int)Math.round(rectBounds.getHeight()));
}

public static Point2D PointConstrained(Rectangle2D rectConstraint, Point2D ptSource)
{
return new Point.Double(Math.min(Math.max(ptSource.getX(), rectConstraint.getMinX()), rectConstraint.getMaxX()),
Math.min(Math.max(ptSource.getY(), rectConstraint.getMinY()), rectConstraint.getMaxY()));
}
}

Thanks in advance Rich
.



Relevant Pages