HOME  |    TRAINING  |   FREE TUTORIALS   |   JOBS
Find out more about our new RSS feed.
FREE Tutorial
BEGINNING JAVA 2 - JDK 1.3 VERSION PART 5 - TRANSFORMING IMAGES

CATEGORY
SEARCH OUR OTHER TUTORIALS

DESCRIPTION

We can apply a transformation to a graphics context to modify the user coordinate system relative to the device coordinate system. The transformation can be a translation, a rotation, a scaling operation, a shearing operation or a combination of all four. Of course, such a transformation applies to images that you draw as well as anything else.
Click here to be kept informed of our new Tutorials.


This free tutorial is a sample from the book Beginning Java 2: 1.3 Version.


In our previous example we adjusted the size of the applet to accommodate the image. In many situations you would not want to do this. Typically there are likely to be all kinds of things on the web page so you would want your applet to keep within the space allotted to it. We could have done this by scaling the image to fit the space available. Rather than go over the old ground let's create a new applet to try this out, and to add a bit of spice this time we will spin the image about its center, rather than dropping it. That way we will get to use a more complicated transform.

Try It Out - Spinning an Image

This applet will create an animation that spins the image about its center point. We will therefore need to make the diagonal of the image fit within both the height and width of the applet if we want to see all of it as it rotates. We also want the scaled image to fit in the center of the applet, so we will translate the user space after we have applied the scale transform.

Once the image is loaded, we can calculate the length of the diagonal of the image as the square root of the sum of the squares of the width and height. We can then calculate the scale factor that we require by dividing the width and height of the applet by the diagonal of the image, and taking the minimum of these two values. We can create an AffineTransform object in the init() method that combines both the scaling and the translation, and then just apply it in the paint() method for the ImagePanel class. This applet class will contain the same methods as the previous example but with different implementations. The rotation will be accomplished by an additional transformation. We will also have an inner ImagePanel class to define the panel that will display the image. This will only differ in the implementation of the paint() method.

Let's start with the init() method and the data members in the Applet class. The loading of the image will be identical to the previous example, so we need the image and tracker members too. After the image has been loaded, we will calculate the scaling and translation that is necessary.

Here's the applet class with its data members and the init() method:

import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.net.*;
import java.awt.geom.*;    // For AffineTransform

public class WhirlingLogo extends JApplet
            implements Runnable
{
// This method is called when the applet is loaded
public void init()
{
 tracker = new MediaTracker(this);
 Image image = null;
 try
 {
  // Image from a file specified by a URL
  image = getImage(new URL(getCodeBase(),"Images/wrox_logo.gif")); 
 }
 catch(MalformedURLException e)
 {
  System.out.println("Failed to create URL:\n" + e);
 }
 tracker.addImage(image,0);        // Load image
 try
 {
  tracker.waitForAll();         // Wait for image to load
  if(tracker.isErrorAny())        // If there is an error
   return;               // give up

  Dimension size = getSize();      // Get applet size
  imageWidth = image.getWidth(this);   // Get image width
  imageHeight = image.getHeight(this);  // and its height

  // Calculate scale factor so diagonal of image fits width and height
  double diagonal = Math.sqrt(imageWidth*imageWidth +
  imageHeight*imageHeight);
  double scaleFactor = Math.min(size.width/diagonal, 
  size.height/diagonal);

  // Create a transform to translate and scale the image
  at.setToTranslation((size.width-imageWidth*scaleFactor)/2,
            (size.height-imageHeight*scaleFactor)/2);
  at.scale(scaleFactor,scaleFactor);

  imagePanel = new ImagePanel(image); 
  // Create panel showing the image
  getContentPane().add(imagePanel);  
  // Add the panel to the content pane
 }
 catch(InterruptedException e)
 {
  System.out.println(e);
 }
}

// Plus the rest of the applet

Thread whirler;             // Animation thread
boolean whirling = false;        // Animation control
MediaTracker tracker;          // Tracks image loading
ImagePanel imagePanel;
AffineTransform at = new AffineTransform();  
int imageWidth, imageHeight;       // Image dimensions
double angle;              // Rotation angle 
final int INTERVAL = 50;         // Time interval msec
final int ROTATION_TIME = 2000;     
// Complete rotation time msec
final int STEPS_PER_ROTATION = ROTATION_TIME/INTERVAL;
int stepCount;              // Total number of steps
}

The unshaded code is exactly the same as in the previous method. The AffineTransform member, at, stores a transform that scales and translates the image. The angle member will store the rotation angle in radians that will be calculated in the run() method for the whirler thread, and applied in the paint() method for the imagePanel object. We have made this is a member of the class rather than declare it as a local variable in the run() method so we can pick up the value when the applet is stopped and restarted. The other fields that follow angle are all concerned with orienting and drawing the image.

The constant, INTERVAL, stores the time interval between one instance of drawing the image and the next. We store the time for a complete rotation of the image through 360 degrees, which is 2p radians, in ROTATIONTIME. The variable STEPS_PER_ROTATION holds the number of steps for a complete rotation, so with the values we have set for the previous two variables, this will be 40. Finally, the variable, stepCount, will accumulate the total number of steps modulo STEPS_PER_ROTATION. The methods to start and stop the animation thread are the same as in the previous example, apart from the new names for the thread and the control variable:

// This method is called when the browser starts the applet
public void start()
{
 if(tracker.isErrorAny())      // If any image errors
  return;              // don't create the thread
 whirler = new Thread(this);     // Create the animation thread
 whirling = true;
 whirler.start();          // and start it
}

// This method is called when the browser want to stop the applet
// when is it not visible for example
public void stop()
{
 whirling = false;          // Stop the animation loop
 whirler = null;           // Discard the thread
}

The thread code itself will be very similar to the previous example - the timing mechanism is exactly the same. We now need to increment the rotation angle in each time interval so it will be much simpler:

// This method is called when the animation thread is started
public void run()
{
 long time = System.currentTimeMillis();  // Starting time

 // Move image while whirling is true
 while(whirling)
 {
  imagePanel.repaint();        // Repaint the image

  // Wait until the end of the interval
  try
  {
   time += INTERVAL;         // Increment the time
   angle = 2.0*Math.PI*stepCount++/ STEPS_PER_ROTATION;
   stepCount %= STEPS_PER_ROTATION;
   Thread.sleep(Math.max(0, time - System.currentTimeMillis()));
  }
  catch (InterruptedException e)
  {
  break;
  }
 }
}

The stepCount variable starts at 0 and is incremented by 1 on each iteration of the loop. Since a complete rotation through 2 radians should occur after STEPS_PER_ROTATION steps, after stepCount steps the rotation angle is the result of the expression 2.0*Math.PI*stepCount/STEPS_PER_ROTATION. In the statement calculating the rotation angle we also increment stepCount using the postfix increment operator. The image returns to its original position after STEPS_PER_ROTATION steps, so we maintain the value of stepCount modulo STEPS_PER_ROTATION.

The last bit we need to complete the applet is the ImagePanel class, and this only differs from the previous example in the implementation of the paint() method:

class ImagePanel extends JPanel
{
 public ImagePanel(Image image)
 {
  this.image = image;
 }

 public void paint(Graphics g)
 {
  Graphics2D g2D = (Graphics2D)g;
  g2D.transform(at);               
  // Apply scale & translate

  g2D.setPaint(Color.lightGray);
  g2D.fillRect(0, 0, imageWidth, imageHeight);

  g2D.rotate(angle, imageWidth/2.0, imageHeight/2.0); 
  // Rotate about center

  // draw scaled imaged with background
  g2D.drawImage(image, 0, 0, this);
 }

 Image image;                   // The image
}

That's the complete applet so give it a whirl. It would be a good idea to make the applet dimension larger in the html file - 200x200 say - then you can see the image more clearly. The downside to a larger applet is that it will take more processor time since there are more pixels to process.

Continued...


NEXT PAGE



7 RELATED COURSES AVAILABLE
INTRODUCTION TO JAVA PROGRAMMING
The aims of this Java training courses is to understand the role that Java plays on the Internet; describe the be....
JAVA (V1.2): ADVANCED PROGRAMMING
This course teaches the reader to learn, understand and become familiar with the advanced features of Java progra....
INTRODUCTION TO JAVABEANS
To go from the fundamentals of JavaBeans programming to the threshold of Advanced level. Gaining in depth progr....
C++ PROGRAMMING
Object oriented programming is fast becoming the leading software design methodology, with C++ becoming ever more....
C PROGRAMMING
This course is design to provide non-C programmers with the essential skills and knowledge necessary to allow the....
 
1 RELATED JOBS AVAILABLE
JAVA DEVELOPER MANCHESTER
Computer Futures Solutions are seeking a Senior Java Developer for their Manchester based client. You will be joi....
CONTACT US
Wednesday 7th January 2009  © COPYRIGHT 2009 - VISUALSOFT