| The AffineTransforms 
              map 2D structures in one space to another space while maintaining 
              straight lines and the parallelism in the original image. The operations 
              include translation, scaling, flipping, rotation, 
              and shearing.  In Chapter 6: Supplements: Java 
              2D review we discussed the application of affine 
              transforms to shapes drawn with the Graphics2D 
              tools. Here we see that such transforms can be applied to images. 
             An AffineTransformOp 
              object uses an instance of AffineTransform 
              to apply the transform to a source image to create a new destination 
              image (they must be different BufferedImage 
              objects.) For example, the following code shows how to apply a shearing 
              operation to an image:      AffineTransform shearer = AffineTransform.getShearInstance 
              (shx, shy); AffineTransFormOp shear_op = new AffineTransformOp (shearer, 
              interpolation );
 BufferedImage dest_image = shearOp.filter (source_image, 
              null);
 Each point in the source image at (x,y) 
              will move to (x 
              + shx*y, y + shy * x) in the destination image. After an 
              transform, a pixel in a destination image will usually not correspond 
              directly to a particular pixel in the source. So the transforms 
              require an interpolation algorithm to determine the colors of the 
              destination pixels.  The interpolation setting in the second argument of the constructor 
              allow you to choose among a nearest neighbor algorithm, a 
              bilinear interpolation algorithm, and a bicubic interpolation 
              algorithm became effective with Java 5.0. The AffineTransformOp 
              class offers three static constants to choose from for the interpolation 
              setting:  
              AffineTransformOp.TYPE_NEAREST_NEIGHBOR 
                - The nearest neighbor technique applies the color of the 
                nearest transformed source pixel to the destination pixel. 
 
AffineTransformOp.TYPE_BILINEAR 
                - The bilinear interpolation instead uses a combination 
                of colors from a set of transformed source pixels around the position 
                of the destination pixel. See the description of a bilinear algorithm 
                in the specification for  
                 javax.media.jai.InterpolationBilenear. 
                
 
AffineTransformOp.TYPE_BICUBIC 
                -this is a new algorithm implemented in J2SE5.0 that uses a "performs 
                interpolation using the piecewise cubic polynomial". See 
                the description of a bicubic algorithm in the specification for 
                 
                 javax.media.jai.InterpolationBicubic. 
                If the value of the interpolation argument in the AffineTransformOp 
              constructor is set to null, 
              then the nearest neightbor algorithm is used.  Note also that these transforms can result in cutting off some 
              parts of the source image that extend past the borders of the destination 
              image. Also, some operations, such as a rotation, can leave some 
              areas black where no image data remains.  The TransformsApplet 
              shown below demonstrates the shear transform operation on an image. 
              The user enters the x and y shear factors via two sliders.  
              
                 
                  | TransformAppletResources: saturnVoyager.jpg
 |   
                  |  import 
                      javax.swing.*;import java.awt.*;
 import java.awt.image.*;
 import javax.swing.event.*;
 import java.awt.geom.*;
 
 /** Demonstrate an affine transform of an image. **/
 public class TransformApplet extends JApplet
 implements 
                      ChangeListener {
 
 BufferedImage fSrcImage, fDstImage;
 ImageIcon fDstIcon;
 JLabel fShxLabel, fShyLabel;
 JSlider fShxSlider,fShySlider;
 double fShxFactor = 0.0;
 double fShyFactor = 0.0;
 
 /**
 * Build the interface with the source 
                      and filter output image
 * displayed side by side.
 **/
 public void init () {
 
 setLayout (new BorderLayout ());
 
 fSrcImage = getBufImage ("saturnVoyager.jpg");
 if (fSrcImage == null) {
 System.out.println 
                      ("Error in reading image file!");
 return;
 }
 
 JPanel control_panel = new JPanel(new 
                      GridLayout (2,1));
 control_panel.add (fShxLabel =
 new JLabel ("shx 0 ",SwingConstants.RIGHT) );
 control_panel.add (fShxSlider =
 new JSlider (Adjustable.HORIZONTAL, 0, 10, 0));
 fShxSlider.addChangeListener (this);
 
 control_panel.add (fShyLabel =
 new JLabel ("shy 0 ",SwingConstants.RIGHT) );
 control_panel.add (fShySlider =
 new JSlider (Adjustable.HORIZONTAL, 0, 10, 0));
 fShySlider.addChangeListener (this);
 
 // Transform image with default 
                      settings.
 transformFilter();
 
 // Use ImageIcon objects to hold 
                      the two images
 ImageIcon src_icon = new ImageIcon 
                      (fSrcImage);
 fDstIcon = new ImageIcon (fDstImage);
 
 // Put the icons on labels
 JLabel src_display = new JLabel 
                      (src_icon);
 JLabel dst_display = new JLabel 
                      (fDstIcon);
 
 // And then display the labels on 
                      the scroll panes
 JScrollPane src_pane = new JScrollPane 
                      (src_display);
 JScrollPane dst_pane = new JScrollPane 
                      (dst_display);
 
 // Use a JSplitPane to show the 
                      source and destination
 // images side by side.
 JSplitPane split_pane =
 new JSplitPane (JSplitPane.HORIZONTAL_SPLIT,
 true, 
                      src_pane, dst_pane);
 
 split_pane.setResizeWeight (0.5);
 split_pane.setContinuousLayout (true);
 
 // Add the DrawingPanel to the contentPane.
 add (split_pane, BorderLayout.CENTER);
 add (control_panel, BorderLayout.SOUTH);
 
 } // init
 
 /**
 * This class implements ChangeListener 
                      for the
 * slider. So the ChangeEvents come 
                      here when the
 * slider is moved.
 **/
 public void stateChanged (ChangeEvent evt) {
 
 // Use the label to show the numerical 
                      value of the slider.
 fShxLabel.setText ("shx " + fShxSlider.getValue 
                      ());
 
 // Get the values from the slider 
                      and set a new scale factor.
 fShxFactor = fShxSlider.getValue 
                      ();
 
 // Use the label to show the numerical 
                      value of the slider.
 fShyLabel.setText ("shy " + fShySlider.getValue 
                      ());
 
 // Get the values from the slider 
                      and set a new scale factor.
 fShyFactor = fShySlider.getValue 
                      ();
 
 // Transform the image.
 transformFilter();
 
 // Reset the destination images 
                      with the modified version.
 fDstIcon.setImage(fDstImage);
 
 // Repaint to show the modified 
                      image.
 repaint ();
 
 } // stateChanged
 
 /** Create the filter to execute an affine transform 
                      amd
 * apply it to the source image.
 **/
 void transformFilter () {
 
 // Some extreme combinations of 
                      filters can result in
 // invalid operations so catch the 
                      resulting exceptions
 // and reset images to the default.
 try {
 AffineTransform shearer 
                      =
 AffineTransform.getShearInstance 
                      (fShxFactor, fShyFactor);
 AffineTransformOp shear_op 
                      =
 new AffineTransformOp 
                      (shearer, null);
 fDstImage = shear_op.filter 
                      (fSrcImage, null);
 }
 catch (Exception e) {
 System.out.println("Shearing 
                      exception = " + e);
 // Reset back to default
 AffineTransform shearer 
                      =
 AffineTransform.getShearInstance 
                      (0.0, 0.0);
 AffineTransformOp shear_op 
                      =
 new AffineTransformOp 
                      (shearer, null);
 fDstImage = shear_op.filter 
                      (fSrcImage, null);
 }
 } // transformFilter
 
 /**
 *  Download the image 
                      file and convert to a
 *  BufferedImage object.
 **/
 BufferedImage getBufImage (String image_name){
 
 // Get the image
 Image img = getImage (getCodeBase(), 
                      image_name);
 
 // and use a MediaTracker to load 
                      it before converting it to
 // a BufferedImage.
 try {
 MediaTracker tracker 
                      = new MediaTracker (this);
 tracker.addImage (img,0);
 tracker.waitForID (0);
 } catch (InterruptedException e) 
                      { return null; }
 
 int width = img.getWidth (this);
 int height= img.getHeight (this);
 
 BufferedImage buffered_image =
 new BufferedImage (width, 
                      height, BufferedImage.TYPE_INT_RGB);
 Graphics2D g2 = buffered_image.createGraphics 
                      ();
 g2.drawImage (img,0 ,0, null);
 
 return buffered_image;
 
 } // getBufImage
 
 } // class TransformApplet
 |  
 It will be left as an exercise to demonstrate the other types of 
              affine transforms.  References & Web Resources  
             Latest update: March 8, 2006 |