views:

495

answers:

2

The code that I have here is using a MouseAdapter to listen for the user to "draw" a box around the area of an image that they would like to zoom in on and calculate the ratio of the box to the image. It then resizes the image to the calculated ratio. This part works.

The issue that I am having is making the JScrollPane view appear as if it is still at the same top left position after the image has been resized. I have tried several methods that seem to have gotten close to the result I want but not exactly.

This is the Listener that finds the scale ratio and sets the position:

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.Graphics;
import java.awt.Point;

import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.JComponent;

public class DynamicZoom extends MouseAdapter {

private Point start;
private Point end;
private double zoom = 1.0;
private JScrollPane pane;
private JViewport port;


public void mousePressed(MouseEvent e) {
    if(e.getButton() == MouseEvent.BUTTON1) {
        this.pane = (JScrollPane)e.getSource();
        this.port = pane.getViewport();
        start = e.getPoint();
    }
}

public void mouseReleased(MouseEvent e) {
    if(this.pane != null) {
        Point curr = this.port.getViewPosition();
        end = e.getPoint();

        ImageComponent canvas = (ImageComponent)this.port.getView();
        zoom = canvas.getScale();
        double factor = 0.0;
        double selectedWidth = Math.abs(end.getX() - start.getX());
        double selectedHeight = Math.abs(end.getY() - start.getY());
        if(selectedWidth > selectedHeight)
            factor = this.port.getWidth() / selectedWidth;
        else
            factor = this.port.getHeight() / selectedHeight;

        zoom *= factor;
        int x = (int)((start.x+curr.x)*zoom);
        int y = (int)((start.y+curr.y)*zoom);

        Point point = new Point(x, y);

        ((ImageComponent)(this.port.getView())).setScale(zoom);

        ResizeViewport.resize(pane);
        this.port.setViewPosition(point);
    }
}

public void mouseDragged(MouseEvent e) {
    if(this.pane != null) {
        Graphics g = this.port.getGraphics();
        int width = this.start.x - e.getX();
        int height = this.start.y - e.getY();

        int w = Math.abs( width );
        int h = Math.abs( height );
        int x = width < 0 ? this.start.x : e.getX();
        int y = height < 0 ? this.start.y : e.getY();
        g.drawRect(x, y, w, h);
        this.port.repaint();
    }
}

}

This is the ImageComponent it resizes and displays the image:

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;

import javax.swing.JComponent;

public class ImageComponent extends JComponent {
private static final long serialVersionUID = 1975488835382044371L;
private BufferedImage img = null;

private double scale = 0.0;

public ImageComponent() {}

public ImageComponent(BufferedImage img) {
    this.displayPage(img);
}

@Override
public void paint(Graphics g) {
    Graphics2D g2 = ((Graphics2D)g);
    if(this.img != null) {
        int width = (int)(this.img.getWidth() * this.scale);
        int height = (int)(this.img.getHeight() * this.scale);
        this.setPreferredSize(new Dimension(width, height));
        g2.drawImage(this.img, 0, 0, width, height, null, null);
        g2.dispose();
    }
}

public void displayPage(BufferedImage pic) {
    if(img != null) {
        this.img = pic;
    }
}

public BufferedImage getPage() {
    return this.img;
}

public void setScale(double ratio) {
    if(ratio > .04) {
        this.scale = ratio;
        this.repaint();
    }
}

public double getScale() {
    return this.scale;
}
}

The ResizeViewport forces the viewport to show the entire ImageComponent when it is scaled up because otherwise it will clip the image at the size that it previously was:

import java.awt.Dimension;

import javax.swing.JScrollPane;
public class ResizeViewport {
public static void resize(JScrollPane scroll) {
    int vw = scroll.getWidth()-scroll.getVerticalScrollBar().getWidth();
    int vh = scroll.getHeight()-scroll.getHorizontalScrollBar().getHeight();
    scroll.getViewport().setViewSize(new Dimension(vw, vh));
}
}
A: 

The issue that I am having is making the JScrollPane view appear as if it is still at the same top left position after the image has been resized.

Sorry, I'm not sure what you're asking. I might be able to give an answer if you clarify a little.

Stuart
Wait, should this be a comment instead of an answer?
Stuart
Sorry to be vague or confusing but I had an image in a jscrollpane that was being enlarged or shrunk and I needed to set the position of the jscrollpane to a new location based of of the transformation's ratio... if that makes any better sense.
Daniel
A: 

It turns out there is nothing wrong with the math used to calculate the position or the way I designed the code. The problem was that the ImageComponent was still painting in another Thread when the position was being calculated; therefore returning "false" values for the getWidth() and getHeight() methods. I solved the issue using the following code:

EventQueue.invokeLater(new Runnable() {
   public void run() {
      port.setViewPosition(new Point(x,y));
   }
});

This allows the painting to finish before trying to calculate the size of the image and setting the position in the JScrollPane. Problem solved. I would still like to throw a thanks out to the people that took the time to at least review this issue.

Daniel