/* @(#) ZoomPanel.java 1.1 2001/21/11
 * Copyright (c) 2001 Lawrence Rodrigues
 */

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;

import java.net.*;
import java.util.*;
import java.beans.*;
import javax.swing.*;
//import java.awt.print.*;
import java.awt.geom.*;

/** Displays and magnifies an image. 
  * This class is a shorter version of the ImageCanvas2D class described in the book
  * Building Imaging Applications with Java technology. See chapters 6 and 7.
  * If you don't own this book, download chapter 7 form the Addison-Wesley site:
  * http://cseng.aw.com/book/0,3828,0201700743,00.html 
  **/
public class ZoomPanel extends JComponent {

   protected AffineTransform atx = new AffineTransform();
   protected int imageWidth, imageHeight;
   protected double magFactorX = 1.0;
   protected double magFactorY = 1.0;
   protected int magCenterX = 0;
   protected int magCenterY =0;
   protected Point zoomOffset = new Point(0,0);
   protected int width, height;

   transient public BufferedImage bufferedImage, displayImage;
   transient protected Graphics2D dispGc;

   public void setBufferedImage(BufferedImage bimage){
       reset();
       this.bufferedImage = bimage;
       imageWidth = bufferedImage.getWidth();
       imageHeight = bufferedImage.getHeight();
       applyTransform(atx);
       repaint();
    }

   public BufferedImage getBufferedImage(){ return bufferedImage; }

   public void setMagFactorX(double magFactor){
      this.magFactorX = magFactor;
   }
   public void setMagFactorY(double magFactor){
      this.magFactorY = magFactor;
   }

   public double getMagFactorX(){ return magFactorX;}
   public double getMagFactorY(){ return magFactorY;}


   public AffineTransform getAffineTransform(){
      return atx;
   }

   public Dimension getImageDimension() {
      return new Dimension(imageWidth, imageHeight);
   }

   public Graphics2D getDisplayedImageGC() {
      return dispGc;
   }

   /** Applies a specified transformation to an input image, and then displays it on the canvas
     * @param bi the buffered image to which the transformation is applied.
     * @param atx the transformation to be applied.
     */
   protected void applyTransform(BufferedImage bi, AffineTransform atx){
      if(bufferedImage == null) return;
      createDisplayImage();
      dispGc.setColor(Color.black);
      dispGc.fillRect(0,0,displayImage.getWidth(), displayImage.getHeight());
      dispGc.drawImage(bi,atx,this);
      repaint();
   }

   /** Applies a specified transformation to the current off screen image,
     * and then displays it on the canvas.
     * @param atx the transformation to be applied.
     */
    public void applyTransform(AffineTransform atx){
      applyTransform(bufferedImage, atx);
      this.atx = atx;
      repaint();
   }

    protected void createDisplayImage(){
       displayImage = new BufferedImage(imageWidth,imageHeight,BufferedImage.TYPE_INT_RGB);
       setOffScrGc();
    }

    /** Sets off screen Graphics. The ImageCanvas has to be visible
      * for the off screen screen to be available
      **/
    protected void setOffScrGc(){
       if(displayImage!= null){
          dispGc = displayImage.createGraphics();
          dispGc.setColor(getBackground());
          dispGc.fillRect(0,0, width, height);
          dispGc.setColor(getForeground());
          dispGc.setRenderingHint(RenderingHints.KEY_RENDERING,
                                  RenderingHints.VALUE_RENDER_QUALITY);
       }
    }

   public void paintComponent(Graphics gc){
       super.paintComponent(gc);
       Graphics2D g = (Graphics2D)gc;
       Rectangle d = getBounds();
       if((d.width != width)|| (d.height != height)){
           width = d.width;
           height = d.height;
       }
       if(displayImage!= null){
          g.fillRect(0,0,displayImage.getWidth(), displayImage.getHeight());
          g.drawImage(displayImage,0,0,this);
       }
    }

    public void reset(){
       magFactorX = 1.0;
       magFactorY = 1.0;
       atx = new AffineTransform();
       applyTransform(bufferedImage,atx); 
   }

   public void magnify(Rectangle currentShape) {
      int shapeWidth = currentShape.width;
      int shapeHeight = currentShape.height;
      double magX = imageWidth/(double)shapeWidth;
      double magY = imageHeight/(double)shapeHeight;
      int x = currentShape.x + shapeWidth/2;
      int y = currentShape.y + shapeHeight/2;
      paintImage(x,y,magX, magY);
      repaint();
   }
 
   public void paintImage(int magCenterX, int magCenterY, double magX, double magY){
      setMagFactorX(this.magFactorX *magX);
      setMagFactorY(this.magFactorY *magY);
      this.magCenterX = magCenterX;
      this.magCenterY = magCenterY;
      try {
        Point2D mgp = null;
        mgp = atx.inverseTransform((Point2D)(new Point(magCenterX, magCenterY)),(Point2D)mgp);
        double x = (mgp.getX()*magX)-mgp.getX();
        double y = (mgp.getY()*magY)-mgp.getY();
        scale(-x,-y, magX, magY);
      }catch (Exception e) {System.out.println(e); }
   }

   public void scale(double magOffsetX, double magOffsetY, double magX, double magY){
      atx.translate(magOffsetX,magOffsetY);
      atx.scale(magX,magY);
      applyTransform(atx);
   }

   public void resetAndScale(double magOffsetX, double magOffsetY, double magX, double magY){
      atx.setToTranslation(magOffsetX,magOffsetY);
      atx.scale(magX,magY);
      applyTransform(atx);
  }

}