Java/Swing Components/Animation

Материал из Java эксперт
Перейти к: навигация, поиск

Animation By jdesktop animation

   <source lang="java">

import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.TimingTarget; import org.jdesktop.animation.timing.TimingTargetAdapter; /*

* MyIntAnim.java
*
* Created on May 3, 2007, 2:24 PM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*
* @author Chet
*/

public class MyIntAnim {

   private int myInt;
   public void setMyInt(int newValue) {
       myInt = newValue;
       System.out.println("newValue = " + newValue);
   }
   public MyIntAnim() {
       // Set up the animation
       TimingTarget myTarget = new TimingTargetAdapter() {
           public void timingEvent(float fraction) {
               setMyInt((int)(fraction * 10));
           }
       };
       Animator anim = new Animator(1000, myTarget);
       anim.start();
   }
   
   public static void main(String[] args) {
       new MyIntAnim();
       // Sleep so that animation timer starts before main thread dies
       // and process quits
       try {
           Thread.sleep(2000);
       } catch (Exception e) {}
   }
   

}


      </source>
   
  
 
  



Animation Trigger

   <source lang="java">

import java.awt.BorderLayout; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import org.jdesktop.animation.timing.triggers.ActionTrigger; import org.jdesktop.animation.timing.triggers.FocusTrigger; import org.jdesktop.animation.timing.triggers.FocusTriggerEvent; import org.jdesktop.animation.timing.triggers.MouseTrigger; import org.jdesktop.animation.timing.triggers.MouseTriggerEvent; import org.jdesktop.animation.timing.triggers.TimingTrigger; import org.jdesktop.animation.timing.triggers.TimingTriggerEvent; /*

* Triggers.java
*
* Created on May 3, 2007, 1:24 PM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.net.URL; import javax.imageio.ImageIO; import javax.swing.JPanel; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.interpolation.PropertySetter; /**

*
* @author Chet
*/

public class Triggers extends JComponent {

   SpherePanel armed, over, action, focus, timing;
   static JButton triggerButton;
   
   /** Creates a new instance of Triggers */
   public Triggers() {
       setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
       action = new SpherePanel("yellow-sphere.png");
       focus = new SpherePanel("blue-sphere.png");
       armed = new SpherePanel("red-sphere.png");
       over = new SpherePanel("green-sphere.png");
       timing = new SpherePanel("gray-sphere.png");
       
       add(action);
       add(focus);
       add(armed);
       add(over);
       add(timing);
       
       // Add triggers for each sphere, depending on what we want to 
       // trigger them
       ActionTrigger.addTrigger(triggerButton, action.getAnimator());
       FocusTrigger.addTrigger(triggerButton,
               focus.getAnimator(), FocusTriggerEvent.IN);
       MouseTrigger.addTrigger(triggerButton, 
               armed.getAnimator(), MouseTriggerEvent.PRESS);
       MouseTrigger.addTrigger(triggerButton, 
               over.getAnimator(), MouseTriggerEvent.ENTER);
       TimingTrigger.addTrigger(action.getAnimator(),
               timing.getAnimator(), TimingTriggerEvent.STOP);
   }
   
   private static void createAndShowGUI() {
       JFrame f = new JFrame("Triggers");
       f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       f.setLayout(new BorderLayout());
       JPanel buttonPanel = new JPanel();
       buttonPanel.setLayout(new BorderLayout());
       // Note: "Other Button" exists only to provide another component to
       // move focus from/to, in order to show how FocusTrigger works
       buttonPanel.add(new JButton("Other Button"), BorderLayout.NORTH);
       triggerButton = new JButton("Trigger");
       buttonPanel.add(triggerButton, BorderLayout.SOUTH);
       f.add(buttonPanel, BorderLayout.NORTH);
       f.add(new Triggers(), BorderLayout.CENTER);
       f.pack();
       f.setVisible(true);
   }
   
   public static void main(String args[]) {
       Runnable doCreateAndShowGUI = new Runnable() {
           public void run() {
               createAndShowGUI();
           }
       };
       SwingUtilities.invokeLater(doCreateAndShowGUI);
   }
   

} /*

* SpherePanel.java
*
* Created on February 19, 2007, 9:09 AM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/

/**

* This class encapsulates both the rendering of a sphere, at a location
* that may be animating, and the animation that drives the sphere
* movement.
*
* @author Chet
*/

class SpherePanel extends JPanel {

   BufferedImage sphereImage = null;
   private static final int PADDING = 5;
   private static final int PANEL_HEIGHT = 300;
   private int sphereX = PADDING, sphereY = 0;
   Animator bouncer;
   
   /**
    * The animation changes the location of the sphere over time through
    * this property setter. We force a repaint to display the sphere in
    * its new location.
    */
   public void setSphereY(int sphereY) {
       this.sphereY = sphereY;
       repaint();
   }
   
   /**
    * Load the named image and create the animator that will bounce the 
    * image down and back up in this panel.
    */
   SpherePanel(String filename) {
       try {
           URL url = getClass().getResource(filename);
           sphereImage = ImageIO.read(url);
       } catch (Exception e) {
           System.out.println("Problem loading image " + filename + ": " + e);
           return;
       }
       setPreferredSize(new Dimension(sphereImage.getWidth() + 2 * PADDING, 
               PANEL_HEIGHT));
       bouncer = PropertySetter.createAnimator(2000, this, "sphereY",
               0, (PANEL_HEIGHT - sphereImage.getHeight()), 0);
       bouncer.setAcceleration(.5f);
       bouncer.setDeceleration(.5f);
   }
   
   Animator getAnimator() {
       return bouncer;
   }
   
   @Override
   protected void paintComponent(Graphics g) {
       g.setColor(Color.white);
       g.fillRect(0, 0, getWidth(), getHeight());
       g.drawImage(sphereImage, sphereX, sphereY, null);
   }
   

}


      </source>
   
  
 
  



Animator Setup

   <source lang="java">

import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.interpolation.PropertySetter; /*

* MyIntAnimPS.java
*
* Created on May 3, 2007, 2:32 PM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* Compare this version to MyIntAnim; PropertySetter simplifies setting up
* and running animations.
*
* @author Chet
*/

public class MyIntAnimPS {

   private int myInt;
   
   public void setMyInt(int newValue) {
       myInt = newValue;
       System.out.println("newValue = " + newValue);
   }
   public MyIntAnimPS() {
       // Set up the animation
       Animator anim = PropertySetter.createAnimator(1000, 
               this, "myInt", 0, 10);
       anim.start();
   }
   
   public static void main(String[] args) {
       new MyIntAnimPS();
       // Sleep so that animation timer starts before main thread dies
       // and process quits
       try {
           Thread.sleep(2000);
       } catch (Exception e) {}
   }

}


      </source>
   
  
 
  



Basic Race with Animation

   <source lang="java">

import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.Point; import javax.swing.JComponent; import javax.imageio.ImageIO; import javax.swing.JFrame; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.UIManager; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.SwingUtilities; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.TimingTargetAdapter; /*

* BasicRace.java
*
* Created on May 3, 2007, 7:37 AM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* The simplest version of the animation; set up a Animator to
* move the car from one position to another over a given time period.
* 
* 
* @author Chet
*/

public class BasicRace extends TimingTargetAdapter implements ActionListener {

   public static final int RACE_TIME = 2000;    
   Point start = TrackView.START_POS;
   Point end = TrackView.FIRST_TURN_START;
   Point current = new Point();
   protected Animator animator;
   TrackView track;
   RaceControlPanel controlPanel;
   
   /** Creates a new instance of BasicRace */
   public BasicRace(String appName) {
       RaceGUI basicGUI = new RaceGUI(appName);
       controlPanel = basicGUI.getControlPanel();
       controlPanel.addListener(this);
       track = basicGUI.getTrack();
       animator = new Animator(RACE_TIME, this);
   }
   
   //
   // Events
   //
   
   /**
    * This receives the Go/Stop events that start/stop the animation
    */
   public void actionPerformed(ActionEvent ae) {
       if (ae.getActionCommand().equals("Go")) {
           animator.stop();
           animator.start();
       } else if (ae.getActionCommand().equals("Stop")) {
           animator.stop();
       }
   }
   
   /**
    * TimingTarget implementation: calculate and set the current
    * car position based on the animation fraction
    */
   public void timingEvent(float fraction) {
       // Simple linear interpolation to find current position
       current.x = (int)(start.x + (end.x - start.x) * fraction);
       current.y = (int)(start.y + (end.y - start.y) * fraction);
       
       // set the new position; this will force a repaint in TrackView
       // and will display the car in the new position
       track.setCarPosition(current);
   }
   public static void main(String args[]) {
       Runnable doCreateAndShowGUI = new Runnable() {
           public void run() {
               BasicRace race = new BasicRace("BasicRace");
           }
       };
       SwingUtilities.invokeLater(doCreateAndShowGUI);
   }
   

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* Go/Stop buttons to control the animation
*
* @author Chet
*/

class RaceControlPanel extends JPanel {

   /** Make these static so that outside classes can easily
    *  add themselves as listeners */
   JButton goButton = new JButton("Go");
   JButton stopButton = new JButton("Stop");
   /**
    * Creates a new instance of RaceControlPanel
    */
   public RaceControlPanel() {
       add(goButton);
       add(stopButton);
   }
   
   public JButton getGoButton() {
       return goButton;
   }
   
   public JButton getStopButton() {
       return stopButton;
   }
   
   public void addListener(ActionListener listener) {
       goButton.addActionListener(listener);
       stopButton.addActionListener(listener);
   }
   

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* The GUI used by all of the different race demos.
* It contains a control panel (for the Go/Stop buttons) and a
* TrackView (where the race is rendered)
*
* @author Chet
*/

class RaceGUI {

   private TrackView track;
   private RaceControlPanel controlPanel;
   /**
    * Creates a new instance of RaceGUI
    */
   public RaceGUI(String appName) {
       UIManager.put("swing.boldMetal", Boolean.FALSE);
       JFrame f = new JFrame(appName);
       f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       f.setLayout(new BorderLayout());
       
       // Add Track view
       track = new TrackView();
       f.add(track, BorderLayout.CENTER);
       
       // Add control panel
       controlPanel = new RaceControlPanel();
       f.add(controlPanel, BorderLayout.SOUTH);
       
       f.pack();
       f.setVisible(true);
   }
   
   public TrackView getTrack() {
       return track;
   }
   
   public RaceControlPanel getControlPanel() {
       return controlPanel;
   }

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* This class does the work of rendering the current view of the
* racetrack.  It holds the car position and rotation and displays
* the car accordingly.  The track itself is merely a background image
* that is copied the same on every repaint.
* Note that carPosition and carRotation are both JavaBean properties, which
* is exploited in the SetterRace and MultiStepRace variations.
*
* @author Chet
*/

class TrackView extends JComponent {

   BufferedImage car;
   BufferedImage track;
   Point carPosition;
   double carRotation = 0;
   int trackW, trackH;
   int carW, carH, carWHalf, carHHalf;
   /** Hard-coded positions of interest on the track */
   static final Point START_POS = new Point(450, 70);
   static final Point FIRST_TURN_START = new Point(130, 70);
   static final Point FIRST_TURN_END = new Point(76, 127);
   static final Point SECOND_TURN_START = new Point(76, 404);
   static final Point SECOND_TURN_END = new Point(130, 461);
   static final Point THIRD_TURN_START = new Point(450, 461);
   static final Point THIRD_TURN_END = new Point(504, 404);
   static final Point FOURTH_TURN_START = new Point(504, 127);
   
   /** Creates a new instance of TrackView */
   public TrackView() {
       try {
           car = ImageIO.read(TrackView.class.getResource("beetle_red.gif"));
           track = ImageIO.read(TrackView.class.getResource("track.jpg"));
       } catch (Exception e) {
           System.out.println("Problem loading track/car images: " + e);
       }
       carPosition = new Point(START_POS.x, START_POS.y);
       carW = car.getWidth();
       carH = car.getHeight();
       carWHalf = carW / 2;
       carHHalf = carH / 2;
       trackW = track.getWidth();
       trackH = track.getHeight();
   }
   
   public Dimension getPreferredSize() {
       return new Dimension(trackW, trackH);
   }
   
   /**
    * Render the track and car
    */
   public void paintComponent(Graphics g) {
       // First draw the race track
       g.drawImage(track, 0, 0, null);
       
       // Now draw the car.  The translate/rotate/translate settings account
       // for any nonzero carRotation values
       Graphics2D g2d = (Graphics2D)g.create();
       g2d.translate(carPosition.x, carPosition.y);
       g2d.rotate(Math.toRadians(carRotation));
       g2d.translate(-(carPosition.x), -(carPosition.y));
       
       // Now the graphics has been set up appropriately; draw the
       // car in position
       g2d.drawImage(car, carPosition.x - carWHalf, carPosition.y - carHHalf, null);
   }
   
   /**
    * Set the new position and schedule a repaint
    */
   public void setCarPosition(Point newPosition) {
       repaint(0, carPosition.x - carWHalf, carPosition.y - carHHalf,
               carW, carH);
       carPosition.x = newPosition.x;
       carPosition.y = newPosition.y;
       repaint(0, carPosition.x - carWHalf, carPosition.y - carHHalf,
               carW, carH);
   }
   
   /**
    * Set the new rotation and schedule a repaint
    */
   public void setCarRotation(double newDegrees) {
       carRotation = newDegrees;
       // repaint area accounts for larger rectangular are because rotate
       // car will exceed normal rectangular bounds
       repaint(0, carPosition.x - carW, carPosition.y - carH,
               2 * carW, 2 * carH);
   }
       

}


      </source>
   
  
 
  



Component Transition Animation

   <source lang="java">

import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.interpolation.PropertySetter; import org.jdesktop.animation.transitions.Effect; import org.jdesktop.animation.transitions.EffectsManager; import org.jdesktop.animation.transitions.ScreenTransition; import org.jdesktop.animation.transitions.TransitionTarget; import org.jdesktop.animation.transitions.effects.rupositeEffect; import org.jdesktop.animation.transitions.effects.FadeIn; /*

* SearchTransition.java
*
* Created on May 3, 2007, 3:05 PM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*
* @author Chet
*/

public class SearchTransition extends JComponent implements TransitionTarget,

       ActionListener {
   //
   // GUI components used in the application screens
   //
   JLabel instructions = new JLabel("Search and ye shall find...");
   JLabel searchLabel = new JLabel("Search:");
   JTextField searchField = new JTextField("");
   JEditorPane results = new JEditorPane("text/html",
           "<html><body>Dung Beetles: An Ode
" + "My Life with Dung Beetles
" + "Beetle Bailey Gets Latrine Duty
" + "Evolution"s Oddities
" + "Society"s Parasites
" + "You Dung Me Wrong: A Country Music History
" + "Ding, Dung, The Witch is Dead
" + ""To be or not to beetle"
" + "Gross Insects of the World
" + "Nature"s Sanitation Engineers
" + "Why are they here?
" + "</body></html>"); JScrollPane scroller = new JScrollPane(results); private static final int LABEL_W = 50; private static final int LABEL_H = 20; private static final int FIELD_W = 100; private static final int FIELD_H = 20; private static final int INSTRUCTIONS_W = 170; private static final int INSTRUCTIONS_H = 20; private static final int RESULTS_X = 30; // // Animation variables // Animator animator = new Animator(500); // Animate for half-second // Setup transition with: // "this" as the transition container // "this" as the TransitionTarget callback object // animator as the animator that drives the transition ScreenTransition transition = new ScreenTransition(this, this, animator); private CompositeEffect moverFader = null; // // Misc other instance variables // private int currentScreen = 0; // Which screen are we on? private int prevHeight = -1; Paint bgGradient = null; int prevW, prevH; /** Creates a new instance of SearchTransition */ public SearchTransition() { results.setEditable(false); // Setup the animation parameters animator.setAcceleration(.2f); // Accelerate for first 20% animator.setDeceleration(.4f); // Decelerate for last 40% // Set this as the listener for entries in the search field searchField.addActionListener(this); instructions.setFont(instructions.getFont().deriveFont(15f)); } @Override public void setBounds(int x, int y, int w, int h) { super.setBounds(x, y, w, h); if (w != prevW || h != prevH) { // Setup GUI for current screen given new size of our container setupNextScreen(); prevW = w; prevH = h; } } /** * Arrange the GUI for the initial search screen. */ private void setupSearchScreen() { int instructionsX = (getWidth() - INSTRUCTIONS_W) / 2; int instructionsY = getHeight() / 4; int searchX = (getWidth() - LABEL_W - FIELD_W - 10) / 2; int searchY = instructionsY + INSTRUCTIONS_H + 20; int fieldX = searchX + LABEL_W + 10; int fieldY = searchY; add(instructions); add(searchLabel); add(searchField); instructions.setBounds(instructionsX, instructionsY, INSTRUCTIONS_W, INSTRUCTIONS_H); searchLabel.setBounds(searchX, searchY, LABEL_W, LABEL_H); searchField.setBounds(fieldX, fieldY, FIELD_W, FIELD_H); } /** * Arrange the GUI for the results screen */ public void setupResultsScreen() { int searchX = getWidth() - LABEL_W - FIELD_W - RESULTS_X - 10; int searchY = 10; int fieldX = searchX + LABEL_W + 10; int fieldY = searchY; int resultsX = RESULTS_X; int resultsY = searchY + LABEL_H + 20; add(searchLabel); add(searchField); add(scroller); searchLabel.setBounds(searchX, 10, LABEL_W, LABEL_H); searchField.setBounds(fieldX, fieldY, FIELD_W, FIELD_H); scroller.setBounds(resultsX, resultsY, getWidth() - (2 * resultsX), getHeight() - resultsY - 20); } /** * Change the gradient and effect according to the new window size */ private void setupBackgroundAndEffect() { // init the background gradient according to current height bgGradient = new GradientPaint(0, 0, Color.LIGHT_GRAY.brighter(), 0, getHeight(), Color.DARK_GRAY.brighter()); // Init resultsEffect with current component size info MoveIn mover = new MoveIn(RESULTS_X, getHeight()); FadeIn fader = new FadeIn(); moverFader = new CompositeEffect(mover); moverFader.addEffect(fader); EffectsManager.setEffect(scroller, moverFader, EffectsManager.TransitionType.APPEARING); prevHeight = getHeight(); } /** * Override of paintComponent() to draw the gradient background */ @Override protected void paintComponent(Graphics g) { if (bgGradient == null || getHeight() != prevHeight) { setupBackgroundAndEffect(); } ((Graphics2D)g).setPaint(bgGradient); g.fillRect(0, 0, getWidth(), getHeight()); } /** * TransitionTarget callback; clear current state and set up * state for next screen */ public void setupNextScreen() { // Clear out current GUI state removeAll(); switch (currentScreen) { case 0: setupSearchScreen(); break; case 1: setupResultsScreen(); break; default: break; } } // Handle user hitting Enter in the search field public void actionPerformed(ActionEvent ae) { if (moverFader == null || prevHeight != getHeight()) { setupBackgroundAndEffect(); } // Change currentScreen, used later in setupNextScreen() callback currentScreen = (currentScreen == 0) ? 1 : 0; transition.start(); } private static void createAndShowGUI() { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(400, 300); SearchTransition component = new SearchTransition(); f.add(component); f.setVisible(true); } /** * @param args the command line arguments */ public static void main(String[] args) { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } catch (InstantiationException ex) { ex.printStackTrace(); } catch (IllegalAccessException ex) { ex.printStackTrace(); } catch (UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } Runnable doCreateAndShowGUI = new Runnable() { public void run() { createAndShowGUI(); } }; SwingUtilities.invokeLater(doCreateAndShowGUI); }

} /**

* Custom effect: moves a component in to its end location
* from a specified starting point
*/

class MoveIn extends Effect {

   private Point startLocation = new Point();
   
   public MoveIn(int x, int y) {
       startLocation.x = x;
       startLocation.y = y;
   }
   /**
    * Handles setup of animation that will vary the location during the
    * transition
    */
   @Override
   public void init(Animator animator, Effect parentEffect) {
       Effect targetEffect = (parentEffect == null) ? this : parentEffect;
       PropertySetter ps;
       ps = new PropertySetter(targetEffect, "location", 
               startLocation, new Point(getEnd().getX(), getEnd().getY()));
       animator.addTarget(ps);
       super.init(animator, parentEffect);
   }

}


      </source>
   
  
 
  



Discrete Interpolation

   <source lang="java">

import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.interpolation.DiscreteInterpolator; import org.jdesktop.animation.timing.interpolation.KeyFrames; import org.jdesktop.animation.timing.interpolation.KeyValues; import org.jdesktop.animation.timing.interpolation.PropertySetter; /*

* DiscreteInterpolation.java
*
* Created on May 3, 2007, 2:42 PM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*
* @author Chet
*/

public class DiscreteInterpolation {

   private int intValue;
   
   /** Creates a new instance of DiscreteInterpolation */
   public DiscreteInterpolation() {
   }
   
   public void setIntValue(int intValue) {
       this.intValue = intValue;
       System.out.println("intValue = " + intValue);
   }
   
   public static void main(String[] args) {
       KeyValues keyValues = KeyValues.create(2, 6, 3, 5, 4);
       KeyFrames keyFrames = new KeyFrames(keyValues,
               DiscreteInterpolator.getInstance());
       Animator anim = PropertySetter.createAnimator(1000, 
               new DiscreteInterpolation(), "intValue", keyFrames);
       anim.start();
       
   }

}


      </source>
   
  
 
  



Fade In Button

   <source lang="java">

import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.Animator.Direction; import org.jdesktop.animation.timing.Animator.RepeatBehavior; import org.jdesktop.animation.timing.TimingTarget; /*

* FadingButtonTF.java
*
* Created on May 3, 2007, 7:20 AM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*
* @author Chet
*/

public class FadingButtonTF extends JButton

       implements ActionListener, TimingTarget {
   float alpha = 1.0f;                 // current opacity of button
   Animator animator;                  // for later start/stop actions
   int animationDuration = 2000;   // each cycle will take 2 seconds
   BufferedImage buttonImage = null;
   
   /** Creates a new instance of FadingButtonTF */
   public FadingButtonTF(String label) {
       super(label);
       setOpaque(false);
       animator = new Animator(animationDuration/2, Animator.INFINITE, 
               RepeatBehavior.REVERSE, this);
       animator.setStartFraction(1.0f);
       animator.setStartDirection(Direction.BACKWARD);
       addActionListener(this);
   }
   
   public void paint(Graphics g) {
       // Create an image for the button graphics if necessary
       if (buttonImage == null || buttonImage.getWidth() != getWidth() ||
               buttonImage.getHeight() != getHeight()) {
           buttonImage = getGraphicsConfiguration().
                   createCompatibleImage(getWidth(), getHeight());
       }
       Graphics gButton = buttonImage.getGraphics();
       gButton.setClip(g.getClip());
       
       //  Have the superclass render the button for us
       super.paint(gButton);
       
       // Make the graphics object sent to this paint() method translucent
 Graphics2D g2d  = (Graphics2D)g;
 AlphaComposite newComposite = 
     AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);
 g2d.setComposite(newComposite);
       
       // Copy the button"s image to the destination graphics, translucently
       g2d.drawImage(buttonImage, 0, 0, null);
   }
   
   /**
    * This method receives click events, which start and stop the animation
    */
   public void actionPerformed(ActionEvent ae) {
       if (!animator.isRunning()) {
           this.setText("Stop Animation");
           animator.start();
       } else {
           animator.stop();
           this.setText("Start Animation");
           // reset alpha to opaque
           alpha = 1.0f;
       }
   }
   // Ununsed MouseListener implementations
   public void begin() {}
   public void end() {}
   public void repeat() {}
   
   /**
    * TimingTarget implementation: this method sets the alpha of our button
    * to be equal to the current elapsed fraction of the animation
    */
   public void timingEvent(float fraction) {
       alpha = fraction;
       // redisplay our cbutton
       repaint();
   }
   private static void createAndShowGUI() {
       JFrame f = new JFrame("Fading Button TF");
       f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       f.setSize(300, 300);
       JPanel checkerboard = new Checkerboard();
       checkerboard.add(new FadingButtonTF("Start Animation"));
       f.add(checkerboard);
       f.setVisible(true);
   }
   
   public static void main(String args[]) {
       Runnable doCreateAndShowGUI = new Runnable() {
           public void run() {
               createAndShowGUI();
           }
       };
       SwingUtilities.invokeLater(doCreateAndShowGUI);
   }
   private static class Checkerboard extends JPanel {
       private static final int DIVISIONS = 10;
       static final int CHECKER_SIZE = 60;
       public void paintComponent(Graphics g) {
           g.setColor(Color.white);
           g.fillRect(0, 0, getWidth(), getHeight());
           g.setColor(Color.BLACK);
           for (int stripeX = 0; stripeX < getWidth(); stripeX += CHECKER_SIZE) {
               for (int y = 0, row = 0; y < getHeight(); y += CHECKER_SIZE/2, ++row) {
                   int x = (row % 2 == 0) ? stripeX : (stripeX + CHECKER_SIZE/2);
                   g.fillRect(x, y, CHECKER_SIZE/2, CHECKER_SIZE/2);
               }
           }
       }
   }

}


      </source>
   
  
 
  



Fade In Demo

   <source lang="java">

/*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.GraphicsConfiguration; import java.awt.Transparency; import java.awt.Graphics; import java.awt.GraphicsEnvironment; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; import java.awt.AlphaComposite; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.io.IOException; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.text.JTextComponent; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.TimingTargetAdapter; import org.jdesktop.animation.timing.interpolation.KeyFrames; import org.jdesktop.animation.timing.interpolation.KeyValues; import org.jdesktop.animation.timing.interpolation.PropertySetter; /**

*
* @author Romain Guy <romain.guy@mac.ru>
*/

public class FadingDemo extends JFrame {

   private ImageViewer imageViewer;
   private JButton nextButton;
   private JButton previousButton;
   private HelpGlassPane glass;
   private JTextField titleField;
   public FadingDemo() {
       super("Fading Demo");
       
       add(buildTitle(), BorderLayout.NORTH);
       add(buildImageViewer(), BorderLayout.CENTER);
       add(buildControls(), BorderLayout.SOUTH);
       
       pack();
       
       setupGlassPane();
       
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       setLocationRelativeTo(null);
       setResizable(false);
   }
   
   private JComponent buildTitle() {
       titleField = new JTextField("Suzhou");
       return titleField;
   }
   
   private void setupGlassPane() {
       glass = new HelpGlassPane();
       setGlassPane(glass);
       glass.setVisible(true);
   }
   
   public static void setTextAndAnimate(final JTextComponent textComponent,
           final String text) {
      Color c = textComponent.getForeground();
      KeyFrames keyFrames = new KeyFrames(KeyValues.create(
                  new Color(c.getRed(), c.getGreen(), c.getBlue(), 255),
                  new Color(c.getRed(), c.getGreen(), c.getBlue(), 0),
                  new Color(c.getRed(), c.getGreen(), c.getBlue(), 255)
              ));
      PropertySetter setter = new PropertySetter(textComponent, "foreground",
              keyFrames);
      Animator animator = new Animator(200, setter);
      animator.addTarget(new TimingTargetAdapter() {
          private boolean textSet = false;
          public void timingEvent(float fraction) {
              if (fraction >= 0.5f && !textSet) {
                  textComponent.setText(text);
                  textSet = true;
              }
          } 
      });
      animator.start();
   }
   
   private JComponent buildControls() {
       JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING));
       
       panel.add(previousButton = new JButton("Previous"));
       previousButton.addActionListener(new ActionListener() {
           public void actionPerformed(ActionEvent e) {
               imageViewer.previous();
               setTextAndAnimate(titleField, "Suzhou");
           }
       });
       panel.add(nextButton = new JButton("Next"));
       nextButton.addActionListener(new ActionListener() {
           public void actionPerformed(ActionEvent e) {
               imageViewer.next();
               setTextAndAnimate(titleField, "Shanghai");
               
               if (glass.isVisible()) {
                   Animator animator = new Animator(200);
                   animator.addTarget(new PropertySetter(glass, "alpha", 0.0f));
                   animator.setAcceleration(0.2f);
                   animator.setDeceleration(0.4f);
                   animator.start();
               }
           }
       });
       
       return panel;
   }
   
   private JComponent buildImageViewer() {
       return imageViewer = new ImageViewer();
   }
   
   public class HelpGlassPane extends JComponent {
       private BufferedImage helpImage;
       private float alpha = 1.0f;
       
       private HelpGlassPane() {
           try {
               helpImage = GraphicsUtilities.loadCompatibleImage(
                       getClass().getResource("help.png"));
           } catch (IOException ex) {
               ex.printStackTrace();
           }
           addMouseListener(new MouseAdapter() {
               @Override
               public void mouseClicked(MouseEvent e) {
                   Animator animator = new Animator(200);
                   animator.addTarget(new PropertySetter(
                           HelpGlassPane.this, "alpha", 0.0f));
                   animator.setAcceleration(0.2f);
                   animator.setDeceleration(0.4f);
                   animator.start();
               }
           });
       }
       
       public void setAlpha(float alpha) {
           this.alpha = alpha;
           if (alpha <= 0.01f) {
               setVisible(false);
           }
           repaint();
       }
       
       public float getAlpha() {
           return this.alpha;
       }
       
       @Override
       protected void paintComponent(Graphics g) {
           Graphics2D g2 = (Graphics2D) g.create();
           
           Point p = nextButton.getLocationOnScreen();
           
           p.x += nextButton.getWidth() / 2 - 16;
           p.y += nextButton.getHeight() / 2 - helpImage.getHeight() + 10;
           
           SwingUtilities.convertPointFromScreen(p, this);
           
           g2.setComposite(AlphaComposite.SrcOver.derive(alpha));
           g2.drawImage(helpImage, p.x, p.y, null);
       }
   }
   
   public static class ImageViewer extends JComponent {
       private BufferedImage firstImage;
       private BufferedImage secondImage;
       
       private float alpha = 0.0f;
       
       private ImageViewer() {
           try {
               firstImage = GraphicsUtilities.loadCompatibleImage(
                   getClass().getResource("suzhou.jpg"));
               secondImage = GraphicsUtilities.loadCompatibleImage(
                   getClass().getResource("shanghai.jpg"));
           } catch (IOException ex) {
               ex.printStackTrace();
           }
       }
       
       @Override
       public Dimension getPreferredSize() {
           return new Dimension(firstImage.getWidth(), firstImage.getHeight());
       }
       
       public void next() {
           Animator animator = new Animator(1000);
           animator.addTarget(new PropertySetter(this, "alpha", 1.0f));
           animator.setAcceleration(0.2f);
           animator.setDeceleration(0.4f);
           animator.start();
       }
       
       public void previous() {
           Animator animator = new Animator(1000);
           animator.addTarget(new PropertySetter(this, "alpha", 0.0f));
           animator.setAcceleration(0.2f);
           animator.setDeceleration(0.4f);
           animator.start();
       }
       
       public void setAlpha(float alpha) {
           this.alpha = alpha;
           repaint();
       }
       
       public float getAlpha() {
           return this.alpha;
       }
       
       @Override
       protected void paintComponent(Graphics g) {
           Graphics2D g2 = (Graphics2D) g.create();
           
           g2.setComposite(AlphaComposite.SrcOver.derive(1.0f - alpha));
           g2.drawImage(firstImage, 0, 0, null);
           g2.setComposite(AlphaComposite.SrcOver.derive(alpha));
           g2.drawImage(secondImage, 0, 0, null);
       }
   }
   public static void main(String[] args) {
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               new FadingDemo().setVisible(true);
           }
       });
   }

}

/**

*

GraphicsUtilities contains a set of tools to perform * common graphics operations easily. These operations are divided into * several themes, listed below.

*

Compatible Images

*

Compatible images can, and should, be used to increase drawing * performance. This class provides a number of methods to load compatible * images directly from files or to convert existing images to compatibles * images.

*

Creating Thumbnails

*

This class provides a number of methods to easily scale down images. * Some of these methods offer a trade-off between speed and result quality and * shouuld be used all the time. They also offer the advantage of producing * compatible images, thus automatically resulting into better runtime * performance.

*

All these methodes are both faster than * {@link java.awt.Image#getScaledInstance(int, int, int)} and produce * better-looking results than the various drawImage() methods * in {@link java.awt.Graphics}, which can be used for image scaling.

*

Image Manipulation

*

This class provides two methods to get and set pixels in a buffered image. * These methods try to avoid unmanaging the image in order to keep good * performance.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class GraphicsUtilities {

   private GraphicsUtilities() {
   }
   // Returns the graphics configuration for the primary screen
   private static GraphicsConfiguration getGraphicsConfiguration() {
       return GraphicsEnvironment.getLocalGraphicsEnvironment().
                   getDefaultScreenDevice().getDefaultConfiguration();
   }
   /**
*

Returns a new BufferedImage using the same color model * as the image passed as a parameter. The returned image is only compatible * with the image passed as a parameter. This does not mean the returned * image is compatible with the hardware.

    *
    * @param image the reference image from which the color model of the new
    *   image is obtained
    * @return a new BufferedImage, compatible with the color model
    *   of image
    */
   public static BufferedImage createColorModelCompatibleImage(BufferedImage image) {
       ColorModel cm = image.getColorModel();
       return new BufferedImage(cm,
           cm.createCompatibleWritableRaster(image.getWidth(),
                                             image.getHeight()),
           cm.isAlphaPremultiplied(), null);
   }
   /**
*

Returns a new compatible image with the same width, height and * transparency as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param image the reference image from which the dimension and the
    *   transparency of the new image are obtained
    * @return a new compatible BufferedImage with the same
    *   dimension and transparency as image
    */
   public static BufferedImage createCompatibleImage(BufferedImage image) {
       return createCompatibleImage(image, image.getWidth(), image.getHeight());
   }
   /**
*

Returns a new compatible image of the specified width and height, and * the same transparency setting as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @param image the reference image from which the transparency of the new
    *   image is obtained
    * @return a new compatible BufferedImage with the same
    *   transparency as image and the specified dimension
    */
   public static BufferedImage createCompatibleImage(BufferedImage image,
                                                     int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  image.getTransparency());
   }
   /**
*

Returns a new opaque compatible image of the specified width and * height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new opaque compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleImage(int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height);
   }
   /**
*

Returns a new translucent compatible image of the specified width * and height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleTranslucentImage(int width,
                                                                int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  Transparency.TRANSLUCENT);
   }
   /**
*

Returns a new compatible image from a URL. The image is loaded from the * specified location and then turned, if necessary into a compatible * image.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param resource the URL of the picture to load as a compatible image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    * @throws java.io.IOException if the image cannot be read or loaded
    */
   public static BufferedImage loadCompatibleImage(URL resource)
           throws IOException {
       BufferedImage image = ImageIO.read(resource);
       return toCompatibleImage(image);
   }
   /**
*

Return a new compatible image that contains a copy of the specified * image. This method ensures an image is compatible with the hardware, * and therefore optimized for fast blitting operations.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @param image the image to copy into a new compatible image
    * @return a new compatible copy, with the
    *   same width and height and transparency and content, of image
    */
   public static BufferedImage toCompatibleImage(BufferedImage image) {
       if (image.getColorModel().equals(
               getGraphicsConfiguration().getColorModel())) {
           return image;
       }
       BufferedImage compatibleImage =
               getGraphicsConfiguration().createCompatibleImage(
                   image.getWidth(), image.getHeight(),
                   image.getTransparency());
       Graphics g = compatibleImage.getGraphics();
       g.drawImage(image, 0, 0, null);
       g.dispose();
       return compatibleImage;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newSize) {
       float ratio;
       int width = image.getWidth();
       int height = image.getHeight();
       if (width > height) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) width / (float) height;
           width = newSize;
           height = (int) (newSize / ratio);
       } else {
           if (newSize >= height) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image height");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) height / (float) width;
           height = newSize;
           width = (int) (newSize / ratio);
       }
       BufferedImage temp = createCompatibleImage(image, width, height);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one of the dimensions
    *   is <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newWidth, int newHeight) {
       if (newWidth >= image.getWidth() ||
           newHeight >= image.getHeight()) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage temp = createCompatibleImage(image, newWidth, newHeight);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newSize) {
       int width = image.getWidth();
       int height = image.getHeight();
       boolean isWidthGreater = width > height;
       if (isWidthGreater) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           }
       } else if (newSize >= height) {
           throw new IllegalArgumentException("newSize must be lower than" +
                                              " the image height");
       }
       if (newSize <= 0) {
           throw new IllegalArgumentException("newSize must" +
                                              " be greater than 0");
       }
       float ratioWH = (float) width / (float) height;
       float ratioHW = (float) height / (float) width;
       BufferedImage thumb = image;
       do {
           if (isWidthGreater) {
               width /= 2;
               if (width < newSize) {
                   width = newSize;
               }
               height = (int) (width / ratioWH);
           } else {
               height /= 2;
               if (height < newSize) {
                   height = newSize;
               }
               width = (int) (height / ratioHW);
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (newSize != (isWidthGreater ? width : height));
       return thumb;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one the dimensions is not > 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newWidth, int newHeight) {
       int width = image.getWidth();
       int height = image.getHeight();
       if (newWidth >= width || newHeight >= height) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage thumb = image;
       do {
           if (width > newWidth) {
               width /= 2;
               if (width < newWidth) {
                   width = newWidth;
               }
           }
           if (height > newHeight) {
               height /= 2;
               if (height < newHeight) {
                   height = newHeight;
               }
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (width != newWidth || height != newHeight);
       return thumb;
   }
   /**
*

Returns an array of pixels, stored as integers, from a * BufferedImage. The pixels are grabbed from a rectangular * area defined by a location and two dimensions. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the source image
    * @param x the x location at which to start grabbing pixels
    * @param y the y location at which to start grabbing pixels
    * @param w the width of the rectangle of pixels to grab
    * @param h the height of the rectangle of pixels to grab
    * @param pixels a pre-allocated array of pixels of size w*h; can be null
    * @return pixels if non-null, a new array of integers
    *   otherwise
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static int[] getPixels(BufferedImage img,
                                 int x, int y, int w, int h, int[] pixels) {
       if (w == 0 || h == 0) {
           return new int[0];
       }
       if (pixels == null) {
           pixels = new int[w * h];
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           Raster raster = img.getRaster();
           return (int[]) raster.getDataElements(x, y, w, h, pixels);
       }
       // Unmanages the image
       return img.getRGB(x, y, w, h, pixels, 0, w);
   }
   /**
*

Writes a rectangular area of pixels in the destination * BufferedImage. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the destination image
    * @param x the x location at which to start storing pixels
    * @param y the y location at which to start storing pixels
    * @param w the width of the rectangle of pixels to store
    * @param h the height of the rectangle of pixels to store
    * @param pixels an array of pixels, stored as integers
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static void setPixels(BufferedImage img,
                                int x, int y, int w, int h, int[] pixels) {
       if (pixels == null || w == 0 || h == 0) {
           return;
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           WritableRaster raster = img.getRaster();
           raster.setDataElements(x, y, w, h, pixels);
       } else {
           // Unmanages the image
           img.setRGB(x, y, w, h, pixels, 0, w);
       }
   }

}


      </source>
   
  
 
  



Morphing Demo

   <source lang="java">

/*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.GraphicsConfiguration; import java.awt.Transparency; import java.awt.Graphics; import java.awt.GraphicsEnvironment; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.FlatteningPathIterator; import java.awt.geom.IllegalPathStateException; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.GraphicsConfiguration; import java.awt.Transparency; import java.awt.Graphics; import java.awt.GraphicsEnvironment; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; import java.awt.AlphaComposite; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.LinearGradientPaint; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.GeneralPath; import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Map; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.interpolation.PropertySetter; import org.jdesktop.animation.timing.triggers.MouseTrigger; import org.jdesktop.animation.timing.triggers.MouseTriggerEvent; /**

*
* @author Romain Guy <romain.guy@mac.ru>
*/

public class MorphingDemo extends JFrame {

   private ImageViewer imageViewer;
   public MorphingDemo() {
       super("Morphing Demo");
       
       add(buildImageViewer());
       add(buildControls(), BorderLayout.SOUTH);
       
       pack();
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       setLocationRelativeTo(null);
   }
       
   private JComponent buildImageViewer() {
       return imageViewer = new ImageViewer();
   }
   
   private JComponent buildControls() {
       JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEADING));
       
       JButton button;
       panel.add(button = new DirectionButton("Backward",
               DirectionButton.Direction.LEFT));
       button.addActionListener(new ActionListener() {
           public void actionPerformed(ActionEvent e) {
               imageViewer.previous();
           }
       });
       
       panel.add(button = new DirectionButton("Forward",
               DirectionButton.Direction.RIGHT));
       button.addActionListener(new ActionListener() {
           public void actionPerformed(ActionEvent e) {
               imageViewer.next();
           }
       });
       
       return panel;
   }
   
   public static class DirectionButton extends JButton {
       public enum Direction {
           LEFT,
           RIGHT
       };
       private DirectionButton.Direction direction;
       private Map desktopHints;
       private float morphing = 0.0f;
       
       private DirectionButton(String text, Direction direction) {
           super(text);
           this.direction = direction;
           
           setupTriggers();
           setFont(getFont().deriveFont(Font.BOLD));
           setOpaque(false);
           setBorderPainted(false);
           setContentAreaFilled(false);
           setFocusPainted(false);
       }
       
       private void setupTriggers() {
           Animator animator = PropertySetter.createAnimator(
                   150, this, "morphing", 0.0f, 1.0f);
           animator.setAcceleration(0.2f);
           animator.setDeceleration(0.3f);
           MouseTrigger.addTrigger(this, animator, MouseTriggerEvent.ENTER, true);
       }
       
       private Morphing2D createMorph() {
           Shape sourceShape = new RoundRectangle2D.Double(2.0, 2.0,
                   getWidth() - 4.0, getHeight() - 4.0, 12.0, 12.0);
           
           GeneralPath.Double destinationShape = new GeneralPath.Double();
           destinationShape.moveTo(2.0, getHeight() / 2.0);
           destinationShape.lineTo(22.0, 0.0);
           destinationShape.lineTo(22.0, 5.0);
           destinationShape.lineTo(getWidth() - 2.0, 5.0);
           destinationShape.lineTo(getWidth() - 2.0, getHeight() - 5.0);
           destinationShape.lineTo(22.0, getHeight() - 5.0);
           destinationShape.lineTo(22.0, getHeight());
           destinationShape.closePath();
           
           return new Morphing2D(sourceShape, destinationShape);
       }
       
       public float getMorphing() {
           return morphing;
       }
       public void setMorphing(float morphing) {
           this.morphing = morphing;
           repaint();
       }
       
       @Override
       protected void paintComponent(Graphics g) {
           Graphics2D g2 = (Graphics2D) g.create();
           g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                   RenderingHints.VALUE_ANTIALIAS_ON);
           
           if (desktopHints == null) {
               Toolkit tk = Toolkit.getDefaultToolkit();
               desktopHints = (Map) (tk.getDesktopProperty("awt.font.desktophints"));
           }
           if (desktopHints != null) {
               g2.addRenderingHints(desktopHints);
           }
           
           LinearGradientPaint p;
           Color[] colors;
           if (!getModel().isArmed()) {
               colors = new Color[] {
                   new Color(0x63a5f7),
                   new Color(0x3799f4),
                   new Color(0x2d7eeb),
                   new Color(0x30a5f9) };
           } else {
               colors = new Color[] {
                   new Color(0x63a5f7).darker(),
                   new Color(0x3799f4).darker(),
                   new Color(0x2d7eeb).darker(),
                   new Color(0x30a5f9).darker() };
           }
           
           p = new LinearGradientPaint(0.0f, 0.0f, 0.0f, getHeight(),
               new float[] { 0.0f, 0.5f, 0.501f, 1.0f },
               colors);
           
           g2.setPaint(p);
           
           Morphing2D morph = createMorph();
           morph.setMorphing(getMorphing());
           if (direction == Direction.RIGHT) {
               g2.translate(getWidth(), 0.0);
               g2.scale(-1.0, 1.0);
           }
           g2.fill(morph);
           if (direction == Direction.RIGHT) {
               g2.scale(-1.0, 1.0);
               g2.translate(-getWidth(), 0.0);
           }
           
           int width = g2.getFontMetrics().stringWidth(getText());
           
           int x = (getWidth() - width) / 2;
           int y = getHeight() / 2 + g2.getFontMetrics().getAscent() / 2 - 1;
           g2.setColor(Color.BLACK);
           g2.drawString(getText(), x, y + 1);
           g2.setColor(Color.WHITE);
           g2.drawString(getText(), x, y);
       }
   }
   public static class ImageViewer extends JComponent {
       private BufferedImage firstImage;
       private BufferedImage secondImage;
       
       private float alpha = 0.0f;
       
       private ImageViewer() {
           try {
               firstImage = GraphicsUtilities.loadCompatibleImage(
                   getClass().getResource("suzhou.jpg"));
               secondImage = GraphicsUtilities.loadCompatibleImage(
                   getClass().getResource("shanghai.jpg"));
           } catch (IOException ex) {
               ex.printStackTrace();
           }
       }
       
       @Override
       public Dimension getPreferredSize() {
           return new Dimension(firstImage.getWidth(), firstImage.getHeight());
       }
       
       public void next() {
           Animator animator = new Animator(500);
           animator.addTarget(new PropertySetter(this, "alpha", 1.0f));
           animator.setAcceleration(0.2f);
           animator.setDeceleration(0.4f);
           animator.start();
       }
       
       public void previous() {
           Animator animator = new Animator(500);
           animator.addTarget(new PropertySetter(this, "alpha", 0.0f));
           animator.setAcceleration(0.2f);
           animator.setDeceleration(0.4f);
           animator.start();
       }
       
       public void setAlpha(float alpha) {
           this.alpha = alpha;
           repaint();
       }
       
       public float getAlpha() {
           return this.alpha;
       }
       
       @Override
       protected void paintComponent(Graphics g) {
           Graphics2D g2 = (Graphics2D) g.create();
           
           g2.setComposite(AlphaComposite.SrcOver.derive(1.0f - alpha));
           g2.drawImage(firstImage, 0, 0, null);
           g2.setComposite(AlphaComposite.SrcOver.derive(alpha));
           g2.drawImage(secondImage, 0, 0, null);
       }
   }
   
   public static void main(String[] args) {
       SwingUtilities.invokeLater(new Runnable() {
           public void run() { 
               new MorphingDemo().setVisible(true);
           }
       });
   }

} /*

* $Id: Morphing2D.java,v 1.1 2007/01/26 17:35:35 gfx Exp $
*
* Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* Licensed under LGPL.
*/

/**

*

A morphing shape is a shape which geometry is constructed from two * other shapes: a start shape and an end shape.

*

The morphing property of a morphing shape defines the amount of * transformation applied to the start shape to turn it into the end shape.

*

Both shapes must have the same winding rule.

*
* @author Jim Graham
* @author Romain Guy <romain.guy@mac.ru> (Maintainer)
*/

class Morphing2D implements Shape {

   private double morph;
   private Geometry startGeometry;
   private Geometry endGeometry;
   /**
*

Creates a new morphing shape. A morphing shape can be used to turn * one shape into another one. The transformation can be controlled by the * morph property.

    *
    * @param startShape the shape to morph from
    * @param endShape   the shape to morph to
    *
    * @throws IllegalPathStateException if the shapes do not have the same
    *                                   winding rule
    * @see #getMorphing()
    * @see #setMorphing(double)
    */
   public Morphing2D(Shape startShape, Shape endShape) {
       startGeometry = new Geometry(startShape);
       endGeometry = new Geometry(endShape);
       if (startGeometry.getWindingRule() != endGeometry.getWindingRule()) {
           throw new IllegalPathStateException("shapes must use same " +
                                               "winding rule");
       }
       double tvals0[] = startGeometry.getTvals();
       double tvals1[] = endGeometry.getTvals();
       double masterTvals[] = mergeTvals(tvals0, tvals1);
       startGeometry.setTvals(masterTvals);
       endGeometry.setTvals(masterTvals);
   }
   /**
*

Returns the morphing value between the two shapes.

    *
    * @return the morphing value between the two shapes
    *
    * @see #setMorphing(double)
    */
   public double getMorphing() {
       return morph;
   }
   /**
*

Sets the morphing value between the two shapes. This value controls * the transformation from the start shape to the end shape. A value of 0.0 * is the start shap. A value of 1.0 is the end shape. A value of 0.5 is a * new shape, morphed half way from the start shape to the end shape.

*

The specified value should be between 0.0 and 1.0. If not, the value * is clamped in the appropriate range.

    *
    * @param morph the morphing value between the two shapes
    *
    * @see #getMorphing()
    */
   public void setMorphing(double morph) {
       if (morph > 1) {
           morph = 1;
       } else if (morph >= 0) {
           // morphing is finite, not NaN, and in range
       } else {
           // morph is < 0 or NaN
           morph = 0;
       }
       this.morph = morph;
   }
   private static double interp(double v0, double v1, double t) {
       return (v0 + ((v1 - v0) * t));
   }
   private static double[] mergeTvals(double tvals0[], double tvals1[]) {
       int i0 = 0;
       int i1 = 0;
       int numtvals = 0;
       while (i0 < tvals0.length && i1 < tvals1.length) {
           double t0 = tvals0[i0];
           double t1 = tvals1[i1];
           if (t0 <= t1) {
               i0++;
           }
           if (t1 <= t0) {
               i1++;
           }
           numtvals++;
       }
       double newtvals[] = new double[numtvals];
       i0 = 0;
       i1 = 0;
       numtvals = 0;
       while (i0 < tvals0.length && i1 < tvals1.length) {
           double t0 = tvals0[i0];
           double t1 = tvals1[i1];
           if (t0 <= t1) {
               newtvals[numtvals] = t0;
               i0++;
           }
           if (t1 <= t0) {
               newtvals[numtvals] = t1;
               i1++;
           }
           numtvals++;
       }
       return newtvals;
   }
   /**
    * @{inheritDoc}
    */
   public Rectangle getBounds() {
       return getBounds2D().getBounds();
   }
   /**
    * @{inheritDoc}
    */
   public Rectangle2D getBounds2D() {
       int n = startGeometry.getNumCoords();
       double xmin, ymin, xmax, ymax;
       xmin = xmax = interp(startGeometry.getCoord(0), endGeometry.getCoord(0),
                            morph);
       ymin = ymax = interp(startGeometry.getCoord(1), endGeometry.getCoord(1),
                            morph);
       for (int i = 2; i < n; i += 2) {
           double x = interp(startGeometry.getCoord(i),
                             endGeometry.getCoord(i), morph);
           double y = interp(startGeometry.getCoord(i + 1),
                             endGeometry.getCoord(i + 1), morph);
           if (xmin > x) {
               xmin = x;
           }
           if (ymin > y) {
               ymin = y;
           }
           if (xmax < x) {
               xmax = x;
           }
           if (ymax < y) {
               ymax = y;
           }
       }
       return new Rectangle2D.Double(xmin, ymin, xmax - xmin, ymax - ymin);
   }
   /**
    * @{inheritDoc}
    */
   public boolean contains(double x, double y) {
       throw new InternalError("unimplemented");
   }
   /**
    * @{inheritDoc}
    */
   public boolean contains(Point2D p) {
       return contains(p.getX(), p.getY());
   }
   /**
    * @{inheritDoc}
    */
   public boolean intersects(double x, double y, double w, double h) {
       throw new InternalError("unimplemented");
   }
   /**
    * @{inheritDoc}
    */
   public boolean intersects(Rectangle2D r) {
       return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
   }
   /**
    * @{inheritDoc}
    */
   public boolean contains(double x, double y, double w, double h) {
       throw new InternalError("unimplemented");
   }
   /**
    * @{inheritDoc}
    */
   public boolean contains(Rectangle2D r) {
       return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
   }
   /**
    * @{inheritDoc}
    */
   public PathIterator getPathIterator(AffineTransform at) {
       return new Iterator(at, startGeometry, endGeometry, morph);
   }
   /**
    * @{inheritDoc}
    */
   public PathIterator getPathIterator(AffineTransform at, double flatness) {
       return new FlatteningPathIterator(getPathIterator(at), flatness);
   }
   private static class Geometry {
       static final double THIRD = (1.0 / 3.0);
       static final double MIN_LEN = 0.001;
       double bezierCoords[];
       int numCoords;
       int windingrule;
       double myTvals[];
       public Geometry(Shape s) {
           // Multiple of 6 plus 2 more for initial moveto
           bezierCoords = new double[20];
           PathIterator pi = s.getPathIterator(null);
           windingrule = pi.getWindingRule();
           if (pi.isDone()) {
               // We will have 1 segment and it will be all zeros
               // It will have 8 coordinates (2 for moveto, 6 for cubic)
               numCoords = 8;
           }
           double coords[] = new double[6];
           int type = pi.currentSegment(coords);
           pi.next();
           if (type != PathIterator.SEG_MOVETO) {
               throw new IllegalPathStateException("missing initial moveto");
           }
           double curx = bezierCoords[0] = coords[0];
           double cury = bezierCoords[1] = coords[1];
           double newx, newy;
           numCoords = 2;
           while (!pi.isDone()) {
               if (numCoords + 6 > bezierCoords.length) {
                   // Keep array size to a multiple of 6 plus 2
                   int newsize = (numCoords - 2) * 2 + 2;
                   double newCoords[] = new double[newsize];
                   System.arraycopy(bezierCoords, 0, newCoords, 0, numCoords);
                   bezierCoords = newCoords;
               }
               switch (pi.currentSegment(coords)) {
                   case PathIterator.SEG_MOVETO:
                       throw new InternalError(
                               "Cannot handle multiple subpaths");
                   case PathIterator.SEG_CLOSE:
                       if (curx == bezierCoords[0] && cury == bezierCoords[1])
                       {
                           break;
                       }
                       coords[0] = bezierCoords[0];
                       coords[1] = bezierCoords[1];
                       /* NO BREAK */
                   case PathIterator.SEG_LINETO:
                       newx = coords[0];
                       newy = coords[1];
                       // A third of the way from curxy to newxy:
                       bezierCoords[numCoords++] = interp(curx, newx, THIRD);
                       bezierCoords[numCoords++] = interp(cury, newy, THIRD);
                       // A third of the way from newxy back to curxy:
                       bezierCoords[numCoords++] = interp(newx, curx, THIRD);
                       bezierCoords[numCoords++] = interp(newy, cury, THIRD);
                       bezierCoords[numCoords++] = curx = newx;
                       bezierCoords[numCoords++] = cury = newy;
                       break;
                   case PathIterator.SEG_QUADTO:
                       double ctrlx = coords[0];
                       double ctrly = coords[1];
                       newx = coords[2];
                       newy = coords[3];
                       // A third of the way from ctrlxy back to curxy:
                       bezierCoords[numCoords++] = interp(ctrlx, curx, THIRD);
                       bezierCoords[numCoords++] = interp(ctrly, cury, THIRD);
                       // A third of the way from ctrlxy to newxy:
                       bezierCoords[numCoords++] = interp(ctrlx, newx, THIRD);
                       bezierCoords[numCoords++] = interp(ctrly, newy, THIRD);
                       bezierCoords[numCoords++] = curx = newx;
                       bezierCoords[numCoords++] = cury = newy;
                       break;
                   case PathIterator.SEG_CUBICTO:
                       bezierCoords[numCoords++] = coords[0];
                       bezierCoords[numCoords++] = coords[1];
                       bezierCoords[numCoords++] = coords[2];
                       bezierCoords[numCoords++] = coords[3];
                       bezierCoords[numCoords++] = curx = coords[4];
                       bezierCoords[numCoords++] = cury = coords[5];
                       break;
               }
               pi.next();
           }
           // Add closing segment if either:
           // - we only have initial moveto - expand it to an empty cubic
           // - or we are not back to the starting point
           if ((numCoords < 8) ||
               curx != bezierCoords[0] ||
               cury != bezierCoords[1]) {
               newx = bezierCoords[0];
               newy = bezierCoords[1];
               // A third of the way from curxy to newxy:
               bezierCoords[numCoords++] = interp(curx, newx, THIRD);
               bezierCoords[numCoords++] = interp(cury, newy, THIRD);
               // A third of the way from newxy back to curxy:
               bezierCoords[numCoords++] = interp(newx, curx, THIRD);
               bezierCoords[numCoords++] = interp(newy, cury, THIRD);
               bezierCoords[numCoords++] = newx;
               bezierCoords[numCoords++] = newy;
           }
           // Now find the segment endpoint with the smallest Y coordinate
           int minPt = 0;
           double minX = bezierCoords[0];
           double minY = bezierCoords[1];
           for (int ci = 6; ci < numCoords; ci += 6) {
               double x = bezierCoords[ci];
               double y = bezierCoords[ci + 1];
               if (y < minY || (y == minY && x < minX)) {
                   minPt = ci;
                   minX = x;
                   minY = y;
               }
           }
           // If the smallest Y coordinate is not the first coordinate,
           // rotate the points so that it is...
           if (minPt > 0) {
               // Keep in mind that first 2 coords == last 2 coords
               double newCoords[] = new double[numCoords];
               // Copy all coordinates from minPt to the end of the
               // array to the beginning of the new array
               System.arraycopy(bezierCoords, minPt,
                                newCoords, 0,
                                numCoords - minPt);
               // Now we do not want to copy 0,1 as they are duplicates
               // of the last 2 coordinates which we just copied.  So
               // we start the source copy at index 2, but we still
               // copy a full minPt coordinates which copies the two
               // coordinates that were at minPt to the last two elements
               // of the array, thus ensuring that thew new array starts
               // and ends with the same pair of coordinates...
               System.arraycopy(bezierCoords, 2,
                                newCoords, numCoords - minPt,
                                minPt);
               bezierCoords = newCoords;
           }
           /* Clockwise enforcement:
            * - This technique is based on the formula for calculating
            *   the area of a Polygon.  The standard formula is:
            *   Area(Poly) = 1/2 * sum(x[i]*y[i+1] - x[i+1]y[i])
            * - The returned area is negative if the polygon is
            *   "mostly clockwise" and positive if the polygon is
            *   "mostly counter-clockwise".
            * - One failure mode of the Area calculation is if the
            *   Polygon is self-intersecting.  This is due to the
            *   fact that the areas on each side of the self-intersection
            *   are bounded by segments which have opposite winding
            *   direction.  Thus, those areas will have opposite signs
            *   on the acccumulation of their area summations and end
            *   up canceling each other out partially.
            * - This failure mode of the algorithm in determining the
            *   exact magnitude of the area is not actually a big problem
            *   for our needs here since we are only using the sign of
            *   the resulting area to figure out the overall winding
            *   direction of the path.  If self-intersections cause
            *   different parts of the path to disagree as to the
            *   local winding direction, that is no matter as we just
            *   wait for the final answer to tell us which winding
            *   direction had greater representation.  If the final
            *   result is zero then the path was equal parts clockwise
            *   and counter-clockwise and we do not care about which
            *   way we order it as either way will require half of the
            *   path to unwind and re-wind itself.
            */
           double area = 0;
           // Note that first and last points are the same so we
           // do not need to process coords[0,1] against coords[n-2,n-1]
           curx = bezierCoords[0];
           cury = bezierCoords[1];
           for (int i = 2; i < numCoords; i += 2) {
               newx = bezierCoords[i];
               newy = bezierCoords[i + 1];
               area += curx * newy - newx * cury;
               curx = newx;
               cury = newy;
           }
           if (area < 0) {
               /* The area is negative so the shape was clockwise
                * in a Euclidean sense.  But, our screen coordinate
                * systems have the origin in the upper left so they
                * are flipped.  Thus, this path "looks" ccw on the
                * screen so we are flipping it to "look" clockwise.
                * Note that the first and last points are the same
                * so we do not need to swap them.
                * (Not that it matters whether the paths end up cw
                *  or ccw in the end as long as all of them are the
                *  same, but above we called this section "Clockwise
                *  Enforcement", so we do not want to be liars. ;-)
                */
               // Note that [0,1] do not need to be swapped with [n-2,n-1]
               // So first pair to swap is [2,3] and [n-4,n-3]
               int i = 2;
               int j = numCoords - 4;
               while (i < j) {
                   curx = bezierCoords[i];
                   cury = bezierCoords[i + 1];
                   bezierCoords[i] = bezierCoords[j];
                   bezierCoords[i + 1] = bezierCoords[j + 1];
                   bezierCoords[j] = curx;
                   bezierCoords[j + 1] = cury;
                   i += 2;
                   j -= 2;
               }
           }
       }
       public int getWindingRule() {
           return windingrule;
       }
       public int getNumCoords() {
           return numCoords;
       }
       public double getCoord(int i) {
           return bezierCoords[i];
       }
       public double[] getTvals() {
           if (myTvals != null) {
               return myTvals;
           }
           // assert(numCoords >= 8);
           // assert(((numCoords - 2) % 6) == 0);
           double tvals[] = new double[(numCoords - 2) / 6 + 1];
           // First calculate total "length" of path
           // Length of each segment is averaged between
           // the length between the endpoints (a lower bound for a cubic)
           // and the length of the control polygon (an upper bound)
           double segx = bezierCoords[0];
           double segy = bezierCoords[1];
           double tlen = 0;
           int ci = 2;
           int ti = 0;
           while (ci < numCoords) {
               double prevx, prevy, newx, newy;
               prevx = segx;
               prevy = segy;
               newx = bezierCoords[ci++];
               newy = bezierCoords[ci++];
               prevx -= newx;
               prevy -= newy;
               double len = Math.sqrt(prevx * prevx + prevy * prevy);
               prevx = newx;
               prevy = newy;
               newx = bezierCoords[ci++];
               newy = bezierCoords[ci++];
               prevx -= newx;
               prevy -= newy;
               len += Math.sqrt(prevx * prevx + prevy * prevy);
               prevx = newx;
               prevy = newy;
               newx = bezierCoords[ci++];
               newy = bezierCoords[ci++];
               prevx -= newx;
               prevy -= newy;
               len += Math.sqrt(prevx * prevx + prevy * prevy);
               // len is now the total length of the control polygon
               segx -= newx;
               segy -= newy;
               len += Math.sqrt(segx * segx + segy * segy);
               // len is now sum of linear length and control polygon length
               len /= 2;
               // len is now average of the two lengths
               /* If the result is zero length then we will have problems
                * below trying to do the math and bookkeeping to split
                * the segment or pair it against the segments in the
                * other shape.  Since these lengths are just estimates
                * to map the segments of the two shapes onto corresponding
                * segments of "approximately the same length", we will
                * simply fudge the length of this segment to be at least
                * a minimum value and it will simply grow from zero or
                * near zero length to a non-trivial size as it morphs.
                */
               if (len < MIN_LEN) {
                   len = MIN_LEN;
               }
               tlen += len;
               tvals[ti++] = tlen;
               segx = newx;
               segy = newy;
           }
           // Now set tvals for each segment to its proportional
           // part of the length
           double prevt = tvals[0];
           tvals[0] = 0;
           for (ti = 1; ti < tvals.length - 1; ti++) {
               double nextt = tvals[ti];
               tvals[ti] = prevt / tlen;
               prevt = nextt;
           }
           tvals[ti] = 1;
           return (myTvals = tvals);
       }
       public void setTvals(double newTvals[]) {
           double oldCoords[] = bezierCoords;
           double newCoords[] = new double[2 + (newTvals.length - 1) * 6];
           double oldTvals[] = getTvals();
           int oldci = 0;
           double x0, xc0, xc1, x1;
           double y0, yc0, yc1, y1;
           x0 = xc0 = xc1 = x1 = oldCoords[oldci++];
           y0 = yc0 = yc1 = y1 = oldCoords[oldci++];
           int newci = 0;
           newCoords[newci++] = x0;
           newCoords[newci++] = y0;
           double t0 = 0;
           double t1 = 0;
           int oldti = 1;
           int newti = 1;
           while (newti < newTvals.length) {
               if (t0 >= t1) {
                   x0 = x1;
                   y0 = y1;
                   xc0 = oldCoords[oldci++];
                   yc0 = oldCoords[oldci++];
                   xc1 = oldCoords[oldci++];
                   yc1 = oldCoords[oldci++];
                   x1 = oldCoords[oldci++];
                   y1 = oldCoords[oldci++];
                   t1 = oldTvals[oldti++];
               }
               double nt = newTvals[newti++];
               // assert(nt > t0);
               if (nt < t1) {
                   // Make nt proportional to [t0 => t1] range
                   double relt = (nt - t0) / (t1 - t0);
                   newCoords[newci++] = x0 = interp(x0, xc0, relt);
                   newCoords[newci++] = y0 = interp(y0, yc0, relt);
                   xc0 = interp(xc0, xc1, relt);
                   yc0 = interp(yc0, yc1, relt);
                   xc1 = interp(xc1, x1, relt);
                   yc1 = interp(yc1, y1, relt);
                   newCoords[newci++] = x0 = interp(x0, xc0, relt);
                   newCoords[newci++] = y0 = interp(y0, yc0, relt);
                   xc0 = interp(xc0, xc1, relt);
                   yc0 = interp(yc0, yc1, relt);
                   newCoords[newci++] = x0 = interp(x0, xc0, relt);
                   newCoords[newci++] = y0 = interp(y0, yc0, relt);
               } else {
                   newCoords[newci++] = xc0;
                   newCoords[newci++] = yc0;
                   newCoords[newci++] = xc1;
                   newCoords[newci++] = yc1;
                   newCoords[newci++] = x1;
                   newCoords[newci++] = y1;
               }
               t0 = nt;
           }
           bezierCoords = newCoords;
           numCoords = newCoords.length;
           myTvals = newTvals;
       }
   }
   private static class Iterator implements PathIterator {
       AffineTransform at;
       Geometry g0;
       Geometry g1;
       double t;
       int cindex;
       public Iterator(AffineTransform at,
                       Geometry g0, Geometry g1,
                       double t) {
           this.at = at;
           this.g0 = g0;
           this.g1 = g1;
           this.t = t;
       }
       /**
        * @{inheritDoc}
        */
       public int getWindingRule() {
           return g0.getWindingRule();
       }
       /**
        * @{inheritDoc}
        */
       public boolean isDone() {
           return (cindex > g0.getNumCoords());
       }
       /**
        * @{inheritDoc}
        */
       public void next() {
           if (cindex == 0) {
               cindex = 2;
           } else {
               cindex += 6;
           }
       }
       double dcoords[];
       /**
        * @{inheritDoc}
        */
       public int currentSegment(float[] coords) {
           if (dcoords == null) {
               dcoords = new double[6];
           }
           int type = currentSegment(dcoords);
           if (type != SEG_CLOSE) {
               coords[0] = (float) dcoords[0];
               coords[1] = (float) dcoords[1];
               if (type != SEG_MOVETO) {
                   coords[2] = (float) dcoords[2];
                   coords[3] = (float) dcoords[3];
                   coords[4] = (float) dcoords[4];
                   coords[5] = (float) dcoords[5];
               }
           }
           return type;
       }
       /**
        * @{inheritDoc}
        */
       public int currentSegment(double[] coords) {
           int type;
           int n;
           if (cindex == 0) {
               type = SEG_MOVETO;
               n = 2;
           } else if (cindex >= g0.getNumCoords()) {
               type = SEG_CLOSE;
               n = 0;
           } else {
               type = SEG_CUBICTO;
               n = 6;
           }
           if (n > 0) {
               for (int i = 0; i < n; i++) {
                   coords[i] = interp(g0.getCoord(cindex + i),
                                      g1.getCoord(cindex + i),
                                      t);
               }
               if (at != null) {
                   at.transform(coords, 0, coords, 0, n / 2);
               }
           }
           return type;
       }
   }

} /**

*

GraphicsUtilities contains a set of tools to perform * common graphics operations easily. These operations are divided into * several themes, listed below.

*

Compatible Images

*

Compatible images can, and should, be used to increase drawing * performance. This class provides a number of methods to load compatible * images directly from files or to convert existing images to compatibles * images.

*

Creating Thumbnails

*

This class provides a number of methods to easily scale down images. * Some of these methods offer a trade-off between speed and result quality and * shouuld be used all the time. They also offer the advantage of producing * compatible images, thus automatically resulting into better runtime * performance.

*

All these methodes are both faster than * {@link java.awt.Image#getScaledInstance(int, int, int)} and produce * better-looking results than the various drawImage() methods * in {@link java.awt.Graphics}, which can be used for image scaling.

*

Image Manipulation

*

This class provides two methods to get and set pixels in a buffered image. * These methods try to avoid unmanaging the image in order to keep good * performance.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class GraphicsUtilities {

   private GraphicsUtilities() {
   }
   // Returns the graphics configuration for the primary screen
   private static GraphicsConfiguration getGraphicsConfiguration() {
       return GraphicsEnvironment.getLocalGraphicsEnvironment().
                   getDefaultScreenDevice().getDefaultConfiguration();
   }
   /**
*

Returns a new BufferedImage using the same color model * as the image passed as a parameter. The returned image is only compatible * with the image passed as a parameter. This does not mean the returned * image is compatible with the hardware.

    *
    * @param image the reference image from which the color model of the new
    *   image is obtained
    * @return a new BufferedImage, compatible with the color model
    *   of image
    */
   public static BufferedImage createColorModelCompatibleImage(BufferedImage image) {
       ColorModel cm = image.getColorModel();
       return new BufferedImage(cm,
           cm.createCompatibleWritableRaster(image.getWidth(),
                                             image.getHeight()),
           cm.isAlphaPremultiplied(), null);
   }
   /**
*

Returns a new compatible image with the same width, height and * transparency as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param image the reference image from which the dimension and the
    *   transparency of the new image are obtained
    * @return a new compatible BufferedImage with the same
    *   dimension and transparency as image
    */
   public static BufferedImage createCompatibleImage(BufferedImage image) {
       return createCompatibleImage(image, image.getWidth(), image.getHeight());
   }
   /**
*

Returns a new compatible image of the specified width and height, and * the same transparency setting as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @param image the reference image from which the transparency of the new
    *   image is obtained
    * @return a new compatible BufferedImage with the same
    *   transparency as image and the specified dimension
    */
   public static BufferedImage createCompatibleImage(BufferedImage image,
                                                     int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  image.getTransparency());
   }
   /**
*

Returns a new opaque compatible image of the specified width and * height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new opaque compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleImage(int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height);
   }
   /**
*

Returns a new translucent compatible image of the specified width * and height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleTranslucentImage(int width,
                                                                int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  Transparency.TRANSLUCENT);
   }
   /**
*

Returns a new compatible image from a URL. The image is loaded from the * specified location and then turned, if necessary into a compatible * image.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param resource the URL of the picture to load as a compatible image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    * @throws java.io.IOException if the image cannot be read or loaded
    */
   public static BufferedImage loadCompatibleImage(URL resource)
           throws IOException {
       BufferedImage image = ImageIO.read(resource);
       return toCompatibleImage(image);
   }
   /**
*

Return a new compatible image that contains a copy of the specified * image. This method ensures an image is compatible with the hardware, * and therefore optimized for fast blitting operations.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @param image the image to copy into a new compatible image
    * @return a new compatible copy, with the
    *   same width and height and transparency and content, of image
    */
   public static BufferedImage toCompatibleImage(BufferedImage image) {
       if (image.getColorModel().equals(
               getGraphicsConfiguration().getColorModel())) {
           return image;
       }
       BufferedImage compatibleImage =
               getGraphicsConfiguration().createCompatibleImage(
                   image.getWidth(), image.getHeight(),
                   image.getTransparency());
       Graphics g = compatibleImage.getGraphics();
       g.drawImage(image, 0, 0, null);
       g.dispose();
       return compatibleImage;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newSize) {
       float ratio;
       int width = image.getWidth();
       int height = image.getHeight();
       if (width > height) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) width / (float) height;
           width = newSize;
           height = (int) (newSize / ratio);
       } else {
           if (newSize >= height) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image height");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) height / (float) width;
           height = newSize;
           width = (int) (newSize / ratio);
       }
       BufferedImage temp = createCompatibleImage(image, width, height);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one of the dimensions
    *   is <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newWidth, int newHeight) {
       if (newWidth >= image.getWidth() ||
           newHeight >= image.getHeight()) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage temp = createCompatibleImage(image, newWidth, newHeight);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newSize) {
       int width = image.getWidth();
       int height = image.getHeight();
       boolean isWidthGreater = width > height;
       if (isWidthGreater) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           }
       } else if (newSize >= height) {
           throw new IllegalArgumentException("newSize must be lower than" +
                                              " the image height");
       }
       if (newSize <= 0) {
           throw new IllegalArgumentException("newSize must" +
                                              " be greater than 0");
       }
       float ratioWH = (float) width / (float) height;
       float ratioHW = (float) height / (float) width;
       BufferedImage thumb = image;
       do {
           if (isWidthGreater) {
               width /= 2;
               if (width < newSize) {
                   width = newSize;
               }
               height = (int) (width / ratioWH);
           } else {
               height /= 2;
               if (height < newSize) {
                   height = newSize;
               }
               width = (int) (height / ratioHW);
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (newSize != (isWidthGreater ? width : height));
       return thumb;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one the dimensions is not > 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newWidth, int newHeight) {
       int width = image.getWidth();
       int height = image.getHeight();
       if (newWidth >= width || newHeight >= height) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage thumb = image;
       do {
           if (width > newWidth) {
               width /= 2;
               if (width < newWidth) {
                   width = newWidth;
               }
           }
           if (height > newHeight) {
               height /= 2;
               if (height < newHeight) {
                   height = newHeight;
               }
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (width != newWidth || height != newHeight);
       return thumb;
   }
   /**
*

Returns an array of pixels, stored as integers, from a * BufferedImage. The pixels are grabbed from a rectangular * area defined by a location and two dimensions. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the source image
    * @param x the x location at which to start grabbing pixels
    * @param y the y location at which to start grabbing pixels
    * @param w the width of the rectangle of pixels to grab
    * @param h the height of the rectangle of pixels to grab
    * @param pixels a pre-allocated array of pixels of size w*h; can be null
    * @return pixels if non-null, a new array of integers
    *   otherwise
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static int[] getPixels(BufferedImage img,
                                 int x, int y, int w, int h, int[] pixels) {
       if (w == 0 || h == 0) {
           return new int[0];
       }
       if (pixels == null) {
           pixels = new int[w * h];
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           Raster raster = img.getRaster();
           return (int[]) raster.getDataElements(x, y, w, h, pixels);
       }
       // Unmanages the image
       return img.getRGB(x, y, w, h, pixels, 0, w);
   }
   /**
*

Writes a rectangular area of pixels in the destination * BufferedImage. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the destination image
    * @param x the x location at which to start storing pixels
    * @param y the y location at which to start storing pixels
    * @param w the width of the rectangle of pixels to store
    * @param h the height of the rectangle of pixels to store
    * @param pixels an array of pixels, stored as integers
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static void setPixels(BufferedImage img,
                                int x, int y, int w, int h, int[] pixels) {
       if (pixels == null || w == 0 || h == 0) {
           return;
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           WritableRaster raster = img.getRaster();
           raster.setDataElements(x, y, w, h, pixels);
       } else {
           // Unmanages the image
           img.setRGB(x, y, w, h, pixels, 0, w);
       }
   }

}


      </source>
   
  
 
  



Multi Step Race

   <source lang="java">

import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.SwingUtilities; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.Animator.RepeatBehavior; import org.jdesktop.animation.timing.interpolation.Interpolator; import org.jdesktop.animation.timing.interpolation.KeyFrames; import org.jdesktop.animation.timing.interpolation.KeyTimes; import org.jdesktop.animation.timing.interpolation.KeyValues; import org.jdesktop.animation.timing.interpolation.PropertySetter; import org.jdesktop.animation.timing.interpolation.SplineInterpolator; import org.jdesktop.animation.timing.triggers.ActionTrigger; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.Point; import javax.swing.JComponent; import javax.imageio.ImageIO; import javax.swing.JFrame; import java.applet.AudioClip; import java.net.URL; import org.jdesktop.animation.timing.TimingTarget; import org.jdesktop.animation.timing.interpolation.KeyFrames; /*

* MultiStepRace.java
*
* Created on May 3, 2007, 2:45 PM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.UIManager;

import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; /**

* The full-blown demo with all of the bells and whistles.  This one uses
* the facilities shown in all of the other variations, but adds 
* both multi-step and non-linear interpolation.  It does this by
* creating a KeyFrames object to hold the times/values/splines
* used for each segment of the race.  It also adds an animation for
* the rotation of the car (since the car should turn as it goes around the
* curves) and sound effects (just to go completely overboard).
*
* @author Chet
*/

public class MultiStepRace {

   protected Animator animator;
   private SoundEffects soundEffects;
   public static final int RACE_TIME = 10000;
   
   
   /** Creates a new instance of BasicRace */
   public MultiStepRace(String appName) {
       RaceGUI basicGUI = new RaceGUI(appName);
       
       // We"re going to need a more involved PropertyRange object
       // that has all curves of the track in it, as well as 
       // non-linear movement around the curves
       Point values[] = {
           TrackView.START_POS,
           TrackView.FIRST_TURN_START, TrackView.FIRST_TURN_END,
           TrackView.SECOND_TURN_START, TrackView.SECOND_TURN_END,
           TrackView.THIRD_TURN_START, TrackView.THIRD_TURN_END,
           TrackView.FOURTH_TURN_START, 
           TrackView.START_POS};
       KeyValues keyValues = KeyValues.create(values);
       // Calculate the keyTimes based on the distances that must be
       // traveled on each leg of the journey
       double totalDistance = 0;
       double segmentDistance[] = new double[values.length];
       for (int i = 0; i < (values.length - 1); ++i) {
           segmentDistance[i] = values[i].distance(values[i + 1]);
           totalDistance += segmentDistance[i];
       }
       segmentDistance[(values.length-1)] = 
               values[(values.length - 1)].distance(values[0]);
       totalDistance += segmentDistance[(values.length-1)];
       float times[] = new float[values.length];
       float elapsedTime = 0.0f;
       times[0] = 0.0f;
       times[values.length - 1] = 1.0f;
       for (int i = 0; i < (values.length - 2); ++i) {
           times[i + 1] = elapsedTime + (float)(segmentDistance[i] / totalDistance);
           elapsedTime = times[i + 1];
       }
       KeyTimes keyTimes = new KeyTimes(times);
       // For realistic movement, we want a big acceleration
       // on the straightaways
       Interpolator initialSpline = new SplineInterpolator(1.00f, 0.00f, 0.2f, .2f);
       Interpolator straightawaySpline = new SplineInterpolator(0.50f, 0.20f, .50f, .80f);
       Interpolator curveSpline = new SplineInterpolator(0.50f, 0.20f, .50f, .80f);
       Interpolator finalSpline = new SplineInterpolator(0.50f, 0.00f, .50f, 1.00f);
       KeyFrames keyFrames = new KeyFrames(keyValues, keyTimes,
               initialSpline, curveSpline, straightawaySpline, curveSpline,
               straightawaySpline, curveSpline,
               straightawaySpline, finalSpline);
       // This PropertySetter enables the animation for the car movement all 
       // the way around the track
       PropertySetter modifier = new PropertySetter(basicGUI.getTrack(), 
               "carPosition", keyFrames);
       animator = new Animator(RACE_TIME, Animator.INFINITE,
               RepeatBehavior.LOOP, modifier);
       
       // Now create similar keyframes for rotation of car
       keyValues = KeyValues.create(360, 315, 270, 225, 180, 135, 90, 45, 0);
       Interpolator straightawayTurnSpline = new SplineInterpolator(1.0f, 0.0f, 1.0f, 0.0f);
       Interpolator curveTurnSpline = new SplineInterpolator(0.0f, 0.5f, 0.5f, 1.0f);
       keyFrames = new KeyFrames(keyValues, keyTimes, 
               straightawayTurnSpline, curveTurnSpline, 
               straightawayTurnSpline, curveTurnSpline, 
               straightawayTurnSpline, curveTurnSpline, 
               straightawayTurnSpline, curveTurnSpline);
       modifier = new PropertySetter(basicGUI.getTrack(), "carRotation", 
               keyFrames);
       animator.addTarget(modifier);
       
       // Finally, add sound effects, triggered by the same animator
       soundEffects = new SoundEffects(keyFrames);
       animator.addTarget(soundEffects);
       
       // Instead of manually tracking the events, have the framework do
       // the work by setting up a trigger
       JButton goButton = basicGUI.getControlPanel().getGoButton();
       JButton stopButton = basicGUI.getControlPanel().getStopButton();
       ActionTrigger trigger = ActionTrigger.addTrigger(goButton, animator);
       stopButton.addActionListener(new Stopper(animator));
   }
   /**
    * Handle clicks on the Stop button to stop the race
    */
   private class Stopper implements ActionListener {
       Animator timer;
       Stopper(Animator timer) {
           this.timer = timer;
       }
       public void actionPerformed(ActionEvent ae) {
           timer.stop();
       }
   }
   public static void main(String args[]) {
       Runnable doCreateAndShowGUI = new Runnable() {
           public void run() {
               MultiStepRace race = new MultiStepRace("Multi Step Race");
           }
       };
       SwingUtilities.invokeLater(doCreateAndShowGUI);
   }
   

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* Go/Stop buttons to control the animation
*
* @author Chet
*/

class RaceControlPanel extends JPanel {

   /** Make these static so that outside classes can easily
    *  add themselves as listeners */
   JButton goButton = new JButton("Go");
   JButton stopButton = new JButton("Stop");
   /**
    * Creates a new instance of RaceControlPanel
    */
   public RaceControlPanel() {
       add(goButton);
       add(stopButton);
   }
   
   public JButton getGoButton() {
       return goButton;
   }
   
   public JButton getStopButton() {
       return stopButton;
   }
   
   public void addListener(ActionListener listener) {
       goButton.addActionListener(listener);
       stopButton.addActionListener(listener);
   }
   

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* The GUI used by all of the different race demos.
* It contains a control panel (for the Go/Stop buttons) and a
* TrackView (where the race is rendered)
*
* @author Chet
*/

class RaceGUI {

   private TrackView track;
   private RaceControlPanel controlPanel;
   /**
    * Creates a new instance of RaceGUI
    */
   public RaceGUI(String appName) {
       UIManager.put("swing.boldMetal", Boolean.FALSE);
       JFrame f = new JFrame(appName);
       f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       f.setLayout(new BorderLayout());
       
       // Add Track view
       track = new TrackView();
       f.add(track, BorderLayout.CENTER);
       
       // Add control panel
       controlPanel = new RaceControlPanel();
       f.add(controlPanel, BorderLayout.SOUTH);
       
       f.pack();
       f.setVisible(true);
   }
   
   public TrackView getTrack() {
       return track;
   }
   
   public RaceControlPanel getControlPanel() {
       return controlPanel;
   }

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* Simple utility class used to load and play sound effects for
* MultiStepRace.
*
* @author Chet
*/

class SoundEffects implements TimingTarget {

   AudioClip drivingClip;
   AudioClip turningClip;
   KeyFrames keyFrames;
   
   /** Creates a new instance of SoundEffects */
   public SoundEffects(KeyFrames keyFrames) {
       this.keyFrames = keyFrames;
       try {
           URL url  = SoundEffects.class.getResource("vroom.wav");
           drivingClip = java.applet.Applet.newAudioClip(url);
           url  = SoundEffects.class.getResource("drift.wav");
           turningClip = java.applet.Applet.newAudioClip(url);
       } catch (Exception e) {
           System.out.println("Problem loading track/car images: " + e);
       }
   }
   
   /**
    * Plays the driving clip
    */
   public void drive() {
       if (drivingClip != null) {
           drivingClip.loop();
       }
   }
   
   /**
    * Stops current clips
    */
   public void stop() {
       if (drivingClip != null) {
           drivingClip.stop();
       }
       if (turningClip != null) {
           turningClip.stop();
       }
   }
   
   /**
    * Plays the turning clip
    */
   public void turn() {
       if (turningClip != null) {
           turningClip.play();
       }
   }
   
   // TimingTarget implementation
   
   boolean pastFirstTurn = false;
   boolean pastSecondTurn = false;
   boolean pastThirdTurn = false;
   boolean pastFourthTurn = false;
   
   public void begin() {
       drive();
       pastFirstTurn = false;
       pastSecondTurn = false;
       pastThirdTurn = false;
       pastFourthTurn = false;
   }
   
   public void end() {
       stop();
   }
   
   /**
    * This method figures out when the car hits one of the turns
    * and plays the turn clip appropriately
    */
   public void timingEvent(float fraction) {
      if (!pastFirstTurn) {
          if (keyFrames.getInterval(fraction) == 1) {
              turn();
              pastFirstTurn = true;
          }
      } else if (!pastSecondTurn) {
          if (keyFrames.getInterval(fraction) == 3) {
              turn();
              pastSecondTurn = true;
          }
      } else if (!pastThirdTurn) {
          if (keyFrames.getInterval(fraction) == 5) {
              turn();
              pastThirdTurn = true;
          }
      } else if (!pastFourthTurn) {
          if (keyFrames.getInterval(fraction) == 7) {
              turn();
              pastFourthTurn = true;
          }
      }
   }
   public void repeat() {
       pastFirstTurn = false;
       pastSecondTurn = false;
       pastThirdTurn = false;
       pastFourthTurn = false;
   }    

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* This class does the work of rendering the current view of the
* racetrack.  It holds the car position and rotation and displays
* the car accordingly.  The track itself is merely a background image
* that is copied the same on every repaint.
* Note that carPosition and carRotation are both JavaBean properties, which
* is exploited in the SetterRace and MultiStepRace variations.
*
* @author Chet
*/

class TrackView extends JComponent {

   BufferedImage car;
   BufferedImage track;
   Point carPosition;
   double carRotation = 0;
   int trackW, trackH;
   int carW, carH, carWHalf, carHHalf;
   /** Hard-coded positions of interest on the track */
   static final Point START_POS = new Point(450, 70);
   static final Point FIRST_TURN_START = new Point(130, 70);
   static final Point FIRST_TURN_END = new Point(76, 127);
   static final Point SECOND_TURN_START = new Point(76, 404);
   static final Point SECOND_TURN_END = new Point(130, 461);
   static final Point THIRD_TURN_START = new Point(450, 461);
   static final Point THIRD_TURN_END = new Point(504, 404);
   static final Point FOURTH_TURN_START = new Point(504, 127);
   
   /** Creates a new instance of TrackView */
   public TrackView() {
       try {
           car = ImageIO.read(TrackView.class.getResource("beetle_red.gif"));
           track = ImageIO.read(TrackView.class.getResource("track.jpg"));
       } catch (Exception e) {
           System.out.println("Problem loading track/car images: " + e);
       }
       carPosition = new Point(START_POS.x, START_POS.y);
       carW = car.getWidth();
       carH = car.getHeight();
       carWHalf = carW / 2;
       carHHalf = carH / 2;
       trackW = track.getWidth();
       trackH = track.getHeight();
   }
   
   public Dimension getPreferredSize() {
       return new Dimension(trackW, trackH);
   }
   
   /**
    * Render the track and car
    */
   public void paintComponent(Graphics g) {
       // First draw the race track
       g.drawImage(track, 0, 0, null);
       
       // Now draw the car.  The translate/rotate/translate settings account
       // for any nonzero carRotation values
       Graphics2D g2d = (Graphics2D)g.create();
       g2d.translate(carPosition.x, carPosition.y);
       g2d.rotate(Math.toRadians(carRotation));
       g2d.translate(-(carPosition.x), -(carPosition.y));
       
       // Now the graphics has been set up appropriately; draw the
       // car in position
       g2d.drawImage(car, carPosition.x - carWHalf, carPosition.y - carHHalf, null);
   }
   
   /**
    * Set the new position and schedule a repaint
    */
   public void setCarPosition(Point newPosition) {
       repaint(0, carPosition.x - carWHalf, carPosition.y - carHHalf,
               carW, carH);
       carPosition.x = newPosition.x;
       carPosition.y = newPosition.y;
       repaint(0, carPosition.x - carWHalf, carPosition.y - carHHalf,
               carW, carH);
   }
   
   /**
    * Set the new rotation and schedule a repaint
    */
   public void setCarRotation(double newDegrees) {
       carRotation = newDegrees;
       // repaint area accounts for larger rectangular are because rotate
       // car will exceed normal rectangular bounds
       repaint(0, carPosition.x - carW, carPosition.y - carH,
               2 * carW, 2 * carH);
   }
       

}


      </source>
   
  
 
  



NonLinear Race Demo

   <source lang="java">

import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.UIManager; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.Point; import javax.swing.JComponent; import javax.imageio.ImageIO; import javax.swing.JFrame; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.SwingUtilities; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.TimingTargetAdapter; /*

* NonLinearRace.java
*
* Created on May 3, 2007, 7:37 AM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* The simplest version of the animation; set up a Animator to
* move the car from one position to another over a given time period.
* 
* 
* @author Chet
*/

public class NonLinearRaceDemo extends TimingTargetAdapter implements ActionListener {

   public static final int RACE_TIME = 2000;    
   Point start = TrackView.START_POS;
   Point end = TrackView.FIRST_TURN_START;
   Point current = new Point();
   protected Animator animator;
   TrackView track;
   RaceControlPanel controlPanel;
   
   /** Creates a new instance of NonLinearRaceDemo */
   public NonLinearRaceDemo(String appName) {
       RaceGUI basicGUI = new RaceGUI(appName);
       controlPanel = basicGUI.getControlPanel();
       controlPanel.addListener(this);
       track = basicGUI.getTrack();
       animator = new Animator(RACE_TIME, this);
   }
   
   //
   // Events
   //
   
   /**
    * This receives the Go/Stop events that start/stop the animation
    */
   public void actionPerformed(ActionEvent ae) {
       if (ae.getActionCommand().equals("Go")) {
           animator.stop();
           animator.start();
       } else if (ae.getActionCommand().equals("Stop")) {
           animator.stop();
       }
   }
   
   /**
    * TimingTarget implementation: calculate and set the current
    * car position based on the animation fraction
    */
   public void timingEvent(float fraction) {
       // Simple linear interpolation to find current position
       current.x = (int)(start.x + (end.x - start.x) * fraction);
       current.y = (int)(start.y + (end.y - start.y) * fraction);
       
       // set the new position; this will force a repaint in TrackView
       // and will display the car in the new position
       track.setCarPosition(current);
   }
   public static void main(String args[]) {
       Runnable doCreateAndShowGUI = new Runnable() {
           public void run() {
               NonLinearRaceDemo race = new NonLinearRaceDemo("NonLinearRaceDemo");
           }
       };
       SwingUtilities.invokeLater(doCreateAndShowGUI);
   }
   

} /*

* NonLinearRace.java
*
* Created on May 3, 2007, 7:49 AM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* Simple subclass of SetterRace that merely sets acceleration/deceleration 
* factors to give a non-linear motion effect to the car"s movement.
*
* @author Chet
*/

class NonLinearRace extends BasicRace {

   /** Creates a new instance of NonLinearRace */
   public NonLinearRace(String appName) {
       super(appName);
       // Accelerate for first half of duration, decelerate for final 10%
       animator.setAcceleration(.5f);
       animator.setDeceleration(.1f);
   }
   
   public static void main(String args[]) {
       Runnable doCreateAndShowGUI = new Runnable() {
           public void run() {
               NonLinearRace race = new NonLinearRace("Non-Linear Race");
           }
       };
       SwingUtilities.invokeLater(doCreateAndShowGUI);
   }

} /*

* BasicRace.java
*
* Created on May 3, 2007, 7:37 AM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* The simplest version of the animation; set up a Animator to
* move the car from one position to another over a given time period.
* 
* 
* @author Chet
*/

class BasicRace extends TimingTargetAdapter implements ActionListener {

   public static final int RACE_TIME = 2000;    
   Point start = TrackView.START_POS;
   Point end = TrackView.FIRST_TURN_START;
   Point current = new Point();
   protected Animator animator;
   TrackView track;
   RaceControlPanel controlPanel;
   
   /** Creates a new instance of BasicRace */
   public BasicRace(String appName) {
       RaceGUI basicGUI = new RaceGUI(appName);
       controlPanel = basicGUI.getControlPanel();
       controlPanel.addListener(this);
       track = basicGUI.getTrack();
       animator = new Animator(RACE_TIME, this);
   }
   
   //
   // Events
   //
   
   /**
    * This receives the Go/Stop events that start/stop the animation
    */
   public void actionPerformed(ActionEvent ae) {
       if (ae.getActionCommand().equals("Go")) {
           animator.stop();
           animator.start();
       } else if (ae.getActionCommand().equals("Stop")) {
           animator.stop();
       }
   }
   
   /**
    * TimingTarget implementation: calculate and set the current
    * car position based on the animation fraction
    */
   public void timingEvent(float fraction) {
       // Simple linear interpolation to find current position
       current.x = (int)(start.x + (end.x - start.x) * fraction);
       current.y = (int)(start.y + (end.y - start.y) * fraction);
       
       // set the new position; this will force a repaint in TrackView
       // and will display the car in the new position
       track.setCarPosition(current);
   }
   public static void main(String args[]) {
       Runnable doCreateAndShowGUI = new Runnable() {
           public void run() {
               BasicRace race = new BasicRace("BasicRace");
           }
       };
       SwingUtilities.invokeLater(doCreateAndShowGUI);
   }
   

}

/**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* This class does the work of rendering the current view of the
* racetrack.  It holds the car position and rotation and displays
* the car accordingly.  The track itself is merely a background image
* that is copied the same on every repaint.
* Note that carPosition and carRotation are both JavaBean properties, which
* is exploited in the SetterRace and MultiStepRace variations.
*
* @author Chet
*/

class TrackView extends JComponent {

   BufferedImage car;
   BufferedImage track;
   Point carPosition;
   double carRotation = 0;
   int trackW, trackH;
   int carW, carH, carWHalf, carHHalf;
   /** Hard-coded positions of interest on the track */
   static final Point START_POS = new Point(450, 70);
   static final Point FIRST_TURN_START = new Point(130, 70);
   static final Point FIRST_TURN_END = new Point(76, 127);
   static final Point SECOND_TURN_START = new Point(76, 404);
   static final Point SECOND_TURN_END = new Point(130, 461);
   static final Point THIRD_TURN_START = new Point(450, 461);
   static final Point THIRD_TURN_END = new Point(504, 404);
   static final Point FOURTH_TURN_START = new Point(504, 127);
   
   /** Creates a new instance of TrackView */
   public TrackView() {
       try {
           car = ImageIO.read(TrackView.class.getResource("beetle_red.gif"));
           track = ImageIO.read(TrackView.class.getResource("track.jpg"));
       } catch (Exception e) {
           System.out.println("Problem loading track/car images: " + e);
       }
       carPosition = new Point(START_POS.x, START_POS.y);
       carW = car.getWidth();
       carH = car.getHeight();
       carWHalf = carW / 2;
       carHHalf = carH / 2;
       trackW = track.getWidth();
       trackH = track.getHeight();
   }
   
   public Dimension getPreferredSize() {
       return new Dimension(trackW, trackH);
   }
   
   /**
    * Render the track and car
    */
   public void paintComponent(Graphics g) {
       // First draw the race track
       g.drawImage(track, 0, 0, null);
       
       // Now draw the car.  The translate/rotate/translate settings account
       // for any nonzero carRotation values
       Graphics2D g2d = (Graphics2D)g.create();
       g2d.translate(carPosition.x, carPosition.y);
       g2d.rotate(Math.toRadians(carRotation));
       g2d.translate(-(carPosition.x), -(carPosition.y));
       
       // Now the graphics has been set up appropriately; draw the
       // car in position
       g2d.drawImage(car, carPosition.x - carWHalf, carPosition.y - carHHalf, null);
   }
   
   /**
    * Set the new position and schedule a repaint
    */
   public void setCarPosition(Point newPosition) {
       repaint(0, carPosition.x - carWHalf, carPosition.y - carHHalf,
               carW, carH);
       carPosition.x = newPosition.x;
       carPosition.y = newPosition.y;
       repaint(0, carPosition.x - carWHalf, carPosition.y - carHHalf,
               carW, carH);
   }
   
   /**
    * Set the new rotation and schedule a repaint
    */
   public void setCarRotation(double newDegrees) {
       carRotation = newDegrees;
       // repaint area accounts for larger rectangular are because rotate
       // car will exceed normal rectangular bounds
       repaint(0, carPosition.x - carW, carPosition.y - carH,
               2 * carW, 2 * carH);
   }
       

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* The GUI used by all of the different race demos.
* It contains a control panel (for the Go/Stop buttons) and a
* TrackView (where the race is rendered)
*
* @author Chet
*/

class RaceGUI {

   private TrackView track;
   private RaceControlPanel controlPanel;
   /**
    * Creates a new instance of RaceGUI
    */
   public RaceGUI(String appName) {
       UIManager.put("swing.boldMetal", Boolean.FALSE);
       JFrame f = new JFrame(appName);
       f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       f.setLayout(new BorderLayout());
       
       // Add Track view
       track = new TrackView();
       f.add(track, BorderLayout.CENTER);
       
       // Add control panel
       controlPanel = new RaceControlPanel();
       f.add(controlPanel, BorderLayout.SOUTH);
       
       f.pack();
       f.setVisible(true);
   }
   
   public TrackView getTrack() {
       return track;
   }
   
   public RaceControlPanel getControlPanel() {
       return controlPanel;
   }

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* Go/Stop buttons to control the animation
*
* @author Chet
*/

class RaceControlPanel extends JPanel {

   /** Make these static so that outside classes can easily
    *  add themselves as listeners */
   JButton goButton = new JButton("Go");
   JButton stopButton = new JButton("Stop");
   /**
    * Creates a new instance of RaceControlPanel
    */
   public RaceControlPanel() {
       add(goButton);
       add(stopButton);
   }
   
   public JButton getGoButton() {
       return goButton;
   }
   
   public JButton getStopButton() {
       return stopButton;
   }
   
   public void addListener(ActionListener listener) {
       goButton.addActionListener(listener);
       stopButton.addActionListener(listener);
   }
   

}


      </source>
   
  
 
  



Pulse Animation

   <source lang="java">

/*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ColorModel; import java.awt.image.BufferedImage; import java.awt.Color; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.GraphicsConfiguration; import java.awt.Transparency; import java.awt.Graphics; import java.awt.GraphicsEnvironment; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; import java.awt.AlphaComposite; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; import java.io.IOException; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.interpolation.PropertySetter; /**

*
* @author Romain Guy <romain.guy@mac.ru>
*/

public class PulseDemo extends JFrame {

   public PulseDemo() {
       super("Pulse Demo");
       
       setContentPane(buildBlackPanel());
       add(buildPulsatingText());
       
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       
       setSize(320, 280);
       setLocationRelativeTo(null);
   }
   
   private JComponent buildBlackPanel() {
       return new JPanel(new BorderLayout()) {
           @Override
           protected void paintComponent(Graphics g) {
               Graphics2D g2 = (Graphics2D) g.create();
               
               Rectangle clip = g2.getClipBounds();
               g2.setPaint(new GradientPaint(0.0f, 0.0f, new Color(0x666f7f).darker(),
                       0.0f, getHeight(), new Color(0x262d3d).darker()));
               
               g2.fillRect(clip.x, clip.y, clip.width, clip.height);
           }
       };
   }
   
   private JComponent buildPulsatingText() {
       return new PulsatingLogo("network-wireless.png");
   }
   
   public static class PulsatingLogo extends JComponent {
       private BufferedImage image;
       private BufferedImage glow;
       private float alpha = 0.0f;
       
       public PulsatingLogo(String imageName) {
           try {
               image = GraphicsUtilities.loadCompatibleImage(
                       getClass().getResource(imageName));
           } catch (IOException ex) {
               ex.printStackTrace();
           }
       }
       
       @Override
       public Dimension getPreferredSize() {
           return new Dimension(image.getWidth(), image.getHeight());
       }
       
       @Override
       protected void paintComponent(Graphics g) {
           Graphics2D g2;
           
           if (glow == null) {
               glow = GraphicsUtilities.createCompatibleImage(image);
               g2 = glow.createGraphics();
               g2.drawImage(image, 0, 0, null);
               g2.dispose();
               
               BufferedImageOp filter = getGaussianBlurFilter(24, true);
               glow = filter.filter(glow, null);
               filter = getGaussianBlurFilter(24, false);
               glow = filter.filter(glow, null);
               filter = new ColorTintFilter(Color.WHITE, 1.0f);
               glow = filter.filter(glow, null);
               
               startAnimator();
           }
           
           int x = (getWidth() - image.getWidth()) / 2;
           int y = (getHeight() - image.getHeight()) / 2;
           
           g2 = (Graphics2D) g.create();
           g2.setComposite(AlphaComposite.SrcOver.derive(getAlpha()));
           g2.drawImage(glow, x, y, null);
           g2.setComposite(AlphaComposite.SrcOver);
           g2.drawImage(image, x, y, null);
       }
       
       private void startAnimator() {
           PropertySetter setter = new PropertySetter(this, "alpha", 0.0f, 1.0f);
           Animator animator = new Animator(600, Animator.INFINITE,
                   Animator.RepeatBehavior.REVERSE, setter);
           animator.start();
       }
       
       public float getAlpha() {
           return alpha;
       }
       public void setAlpha(float alpha) {
           this.alpha = alpha;
           repaint();
       }
       
       public static ConvolveOp getGaussianBlurFilter(int radius,
               boolean horizontal) {
           if (radius < 1) {
               throw new IllegalArgumentException("Radius must be >= 1");
           }
           int size = radius * 2 + 1;
           float[] data = new float[size];
           float sigma = radius / 3.0f;
           float twoSigmaSquare = 2.0f * sigma * sigma;
           float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI);
           float total = 0.0f;
           for (int i = -radius; i <= radius; i++) {
               float distance = i * i;
               int index = i + radius;
               data[index] = (float) Math.exp(-distance / twoSigmaSquare) / sigmaRoot;
               total += data[index];
           }
           for (int i = 0; i < data.length; i++) {
               data[i] /= total;
           }        
           Kernel kernel = null;
           if (horizontal) {
               kernel = new Kernel(size, 1, data);
           } else {
               kernel = new Kernel(1, size, data);
           }
           return new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
       }
   }
   
   public static void main(String[] args) {
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               new PulseDemo().setVisible(true);
           }
       });
   }

}

/**

*

GraphicsUtilities contains a set of tools to perform * common graphics operations easily. These operations are divided into * several themes, listed below.

*

Compatible Images

*

Compatible images can, and should, be used to increase drawing * performance. This class provides a number of methods to load compatible * images directly from files or to convert existing images to compatibles * images.

*

Creating Thumbnails

*

This class provides a number of methods to easily scale down images. * Some of these methods offer a trade-off between speed and result quality and * shouuld be used all the time. They also offer the advantage of producing * compatible images, thus automatically resulting into better runtime * performance.

*

All these methodes are both faster than * {@link java.awt.Image#getScaledInstance(int, int, int)} and produce * better-looking results than the various drawImage() methods * in {@link java.awt.Graphics}, which can be used for image scaling.

*

Image Manipulation

*

This class provides two methods to get and set pixels in a buffered image. * These methods try to avoid unmanaging the image in order to keep good * performance.

*
* @author Romain Guy <romain.guy@mac.ru>
*/

class GraphicsUtilities {

   private GraphicsUtilities() {
   }
   // Returns the graphics configuration for the primary screen
   private static GraphicsConfiguration getGraphicsConfiguration() {
       return GraphicsEnvironment.getLocalGraphicsEnvironment().
                   getDefaultScreenDevice().getDefaultConfiguration();
   }
   /**
*

Returns a new BufferedImage using the same color model * as the image passed as a parameter. The returned image is only compatible * with the image passed as a parameter. This does not mean the returned * image is compatible with the hardware.

    *
    * @param image the reference image from which the color model of the new
    *   image is obtained
    * @return a new BufferedImage, compatible with the color model
    *   of image
    */
   public static BufferedImage createColorModelCompatibleImage(BufferedImage image) {
       ColorModel cm = image.getColorModel();
       return new BufferedImage(cm,
           cm.createCompatibleWritableRaster(image.getWidth(),
                                             image.getHeight()),
           cm.isAlphaPremultiplied(), null);
   }
   /**
*

Returns a new compatible image with the same width, height and * transparency as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param image the reference image from which the dimension and the
    *   transparency of the new image are obtained
    * @return a new compatible BufferedImage with the same
    *   dimension and transparency as image
    */
   public static BufferedImage createCompatibleImage(BufferedImage image) {
       return createCompatibleImage(image, image.getWidth(), image.getHeight());
   }
   /**
*

Returns a new compatible image of the specified width and height, and * the same transparency setting as the image specified as a parameter.

    *
    * @see java.awt.Transparency
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @param image the reference image from which the transparency of the new
    *   image is obtained
    * @return a new compatible BufferedImage with the same
    *   transparency as image and the specified dimension
    */
   public static BufferedImage createCompatibleImage(BufferedImage image,
                                                     int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  image.getTransparency());
   }
   /**
*

Returns a new opaque compatible image of the specified width and * height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new opaque compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleImage(int width, int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height);
   }
   /**
*

Returns a new translucent compatible image of the specified width * and height.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param width the width of the new image
    * @param height the height of the new image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    */
   public static BufferedImage createCompatibleTranslucentImage(int width,
                                                                int height) {
       return getGraphicsConfiguration().createCompatibleImage(width, height,
                                                  Transparency.TRANSLUCENT);
   }
   /**
*

Returns a new compatible image from a URL. The image is loaded from the * specified location and then turned, if necessary into a compatible * image.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #toCompatibleImage(java.awt.image.BufferedImage)
    * @param resource the URL of the picture to load as a compatible image
    * @return a new translucent compatible BufferedImage of the
    *   specified width and height
    * @throws java.io.IOException if the image cannot be read or loaded
    */
   public static BufferedImage loadCompatibleImage(URL resource)
           throws IOException {
       BufferedImage image = ImageIO.read(resource);
       return toCompatibleImage(image);
   }
   /**
*

Return a new compatible image that contains a copy of the specified * image. This method ensures an image is compatible with the hardware, * and therefore optimized for fast blitting operations.

    *
    * @see #createCompatibleImage(java.awt.image.BufferedImage)
    * @see #createCompatibleImage(java.awt.image.BufferedImage, int, int)
    * @see #createCompatibleImage(int, int)
    * @see #createCompatibleTranslucentImage(int, int)
    * @see #loadCompatibleImage(java.net.URL)
    * @param image the image to copy into a new compatible image
    * @return a new compatible copy, with the
    *   same width and height and transparency and content, of image
    */
   public static BufferedImage toCompatibleImage(BufferedImage image) {
       if (image.getColorModel().equals(
               getGraphicsConfiguration().getColorModel())) {
           return image;
       }
       BufferedImage compatibleImage =
               getGraphicsConfiguration().createCompatibleImage(
                   image.getWidth(), image.getHeight(),
                   image.getTransparency());
       Graphics g = compatibleImage.getGraphics();
       g.drawImage(image, 0, 0, null);
       g.dispose();
       return compatibleImage;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newSize) {
       float ratio;
       int width = image.getWidth();
       int height = image.getHeight();
       if (width > height) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) width / (float) height;
           width = newSize;
           height = (int) (newSize / ratio);
       } else {
           if (newSize >= height) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image height");
           } else if (newSize <= 0) {
                throw new IllegalArgumentException("newSize must" +
                                                   " be greater than 0");
           }
           ratio = (float) height / (float) width;
           height = newSize;
           width = (int) (newSize / ratio);
       }
       BufferedImage temp = createCompatibleImage(image, width, height);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method favors speed over quality. When the new size is less than * half the longest dimension of the source image, * {@link #createThumbnail(BufferedImage, int)} or * {@link #createThumbnail(BufferedImage, int, int)} should be used instead * to ensure the quality of the result without sacrificing too much * performance.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one of the dimensions
    *   is <= 0
    */
   public static BufferedImage createThumbnailFast(BufferedImage image,
                                                   int newWidth, int newHeight) {
       if (newWidth >= image.getWidth() ||
           newHeight >= image.getHeight()) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage temp = createCompatibleImage(image, newWidth, newHeight);
       Graphics2D g2 = temp.createGraphics();
       g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                           RenderingHints.VALUE_INTERPOLATION_BILINEAR);
       g2.drawImage(image, 0, 0, temp.getWidth(), temp.getHeight(), null);
       g2.dispose();
       return temp;
   }
   /**
*

Returns a thumbnail of a source image. newSize defines * the length of the longest dimension of the thumbnail. The other * dimension is then computed according to the dimensions ratio of the * original picture.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int, int)
    * @param image the source image
    * @param newSize the length of the largest dimension of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newSize is larger than
    *   the largest dimension of image or <= 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newSize) {
       int width = image.getWidth();
       int height = image.getHeight();
       boolean isWidthGreater = width > height;
       if (isWidthGreater) {
           if (newSize >= width) {
               throw new IllegalArgumentException("newSize must be lower than" +
                                                  " the image width");
           }
       } else if (newSize >= height) {
           throw new IllegalArgumentException("newSize must be lower than" +
                                              " the image height");
       }
       if (newSize <= 0) {
           throw new IllegalArgumentException("newSize must" +
                                              " be greater than 0");
       }
       float ratioWH = (float) width / (float) height;
       float ratioHW = (float) height / (float) width;
       BufferedImage thumb = image;
       do {
           if (isWidthGreater) {
               width /= 2;
               if (width < newSize) {
                   width = newSize;
               }
               height = (int) (width / ratioWH);
           } else {
               height /= 2;
               if (height < newSize) {
                   height = newSize;
               }
               width = (int) (height / ratioHW);
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (newSize != (isWidthGreater ? width : height));
       return thumb;
   }
   /**
*

Returns a thumbnail of a source image.

*

This method offers a good trade-off between speed and quality. * The result looks better than * {@link #createThumbnailFast(java.awt.image.BufferedImage, int)} when * the new size is less than half the longest dimension of the source * image, yet the rendering speed is almost similar.

    *
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int)
    * @see #createThumbnailFast(java.awt.image.BufferedImage, int, int)
    * @see #createThumbnail(java.awt.image.BufferedImage, int)
    * @param image the source image
    * @param newWidth the width of the thumbnail
    * @param newHeight the height of the thumbnail
    * @return a new compatible BufferedImage containing a
    *   thumbnail of image
    * @throws IllegalArgumentException if newWidth is larger than
    *   the width of image or if code>newHeight</code> is larger
    *   than the height of image or if one the dimensions is not > 0
    */
   public static BufferedImage createThumbnail(BufferedImage image,
                                               int newWidth, int newHeight) {
       int width = image.getWidth();
       int height = image.getHeight();
       if (newWidth >= width || newHeight >= height) {
           throw new IllegalArgumentException("newWidth and newHeight cannot" +
                                              " be greater than the image" +
                                              " dimensions");
       } else if (newWidth <= 0 || newHeight <= 0) {
           throw new IllegalArgumentException("newWidth and newHeight must" +
                                              " be greater than 0");
       }
       BufferedImage thumb = image;
       do {
           if (width > newWidth) {
               width /= 2;
               if (width < newWidth) {
                   width = newWidth;
               }
           }
           if (height > newHeight) {
               height /= 2;
               if (height < newHeight) {
                   height = newHeight;
               }
           }
           BufferedImage temp = createCompatibleImage(image, width, height);
           Graphics2D g2 = temp.createGraphics();
           g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                               RenderingHints.VALUE_INTERPOLATION_BILINEAR);
           g2.drawImage(thumb, 0, 0, temp.getWidth(), temp.getHeight(), null);
           g2.dispose();
           thumb = temp;
       } while (width != newWidth || height != newHeight);
       return thumb;
   }
   /**
*

Returns an array of pixels, stored as integers, from a * BufferedImage. The pixels are grabbed from a rectangular * area defined by a location and two dimensions. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the source image
    * @param x the x location at which to start grabbing pixels
    * @param y the y location at which to start grabbing pixels
    * @param w the width of the rectangle of pixels to grab
    * @param h the height of the rectangle of pixels to grab
    * @param pixels a pre-allocated array of pixels of size w*h; can be null
    * @return pixels if non-null, a new array of integers
    *   otherwise
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static int[] getPixels(BufferedImage img,
                                 int x, int y, int w, int h, int[] pixels) {
       if (w == 0 || h == 0) {
           return new int[0];
       }
       if (pixels == null) {
           pixels = new int[w * h];
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           Raster raster = img.getRaster();
           return (int[]) raster.getDataElements(x, y, w, h, pixels);
       }
       // Unmanages the image
       return img.getRGB(x, y, w, h, pixels, 0, w);
   }
   /**
*

Writes a rectangular area of pixels in the destination * BufferedImage. Calling this method on * an image of type different from BufferedImage.TYPE_INT_ARGB * and BufferedImage.TYPE_INT_RGB will unmanage the image.

    *
    * @param img the destination image
    * @param x the x location at which to start storing pixels
    * @param y the y location at which to start storing pixels
    * @param w the width of the rectangle of pixels to store
    * @param h the height of the rectangle of pixels to store
    * @param pixels an array of pixels, stored as integers
    * @throws IllegalArgumentException is pixels is non-null and
    *   of length < w*h
    */
   public static void setPixels(BufferedImage img,
                                int x, int y, int w, int h, int[] pixels) {
       if (pixels == null || w == 0 || h == 0) {
           return;
       } else if (pixels.length < w * h) {
           throw new IllegalArgumentException("pixels array must have a length" +
                                              " >= w*h");
       }
       int imageType = img.getType();
       if (imageType == BufferedImage.TYPE_INT_ARGB ||
           imageType == BufferedImage.TYPE_INT_RGB) {
           WritableRaster raster = img.getRaster();
           raster.setDataElements(x, y, w, h, pixels);
       } else {
           // Unmanages the image
           img.setRGB(x, y, w, h, pixels, 0, w);
       }
   }

} /*

* $Id: ColorTintFilter.java,v 1.1 2007/01/28 01:45:30 gfx Exp $
*
* Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
*
* Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* Copyright (c) 2006 Romain Guy <romain.guy@mac.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS"" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

A color tint filter can be used to mix a solid color to an image. The * result is an image tinted by the specified color. The force of the effect * can be controlled with the mixValue, a number between 0.0 and * 1.0 that can be seen as the percentage of the mix (0.0 does not affect the * source image and 1.0 replaces all the pixels by the solid color).

*

The color of the pixels in the resulting image is computed as follows:

*
 * cR = cS * (1 - mixValue) + cM * mixValue
 * 
*

Definition of the parameters:

*
    *
  • cR: color of the resulting pixel
  • *
  • cS: color of the source pixel
  • *
  • cM: the solid color to mix with the source image
  • *
  • mixValue: strength of the mix, a value between 0.0 and 1.0
  • *
*
* @author Romain Guy <romain.guy@mac.ru>
*/

class ColorTintFilter extends AbstractFilter {

   private final Color mixColor;
   private final float mixValue;
   private int[] preMultipliedRed;
   private int[] preMultipliedGreen;
   private int[] preMultipliedBlue;
   /**
*

Creates a new color mixer filter. The specified color will be used * to tint the source image, with a mixing strength defined by * mixValue.

    *
    * @param mixColor the solid color to mix with the source image
    * @param mixValue the strength of the mix, between 0.0 and 1.0; if the
    *   specified value lies outside this range, it is clamped
    * @throws IllegalArgumentException if mixColor is null
    */
   public ColorTintFilter(Color mixColor, float mixValue) {
       if (mixColor == null) {
           throw new IllegalArgumentException("mixColor cannot be null");
       }
       this.mixColor = mixColor;
       if (mixValue < 0.0f) {
           mixValue = 0.0f;
       } else if (mixValue > 1.0f) {
           mixValue = 1.0f;
       }
       this.mixValue = mixValue;
       
       int mix_r = (int) (mixColor.getRed()   * mixValue);
       int mix_g = (int) (mixColor.getGreen() * mixValue);
       int mix_b = (int) (mixColor.getBlue()  * mixValue);
       
       // Since we use only lookup tables to apply the filter, this filter
       // could be implemented as a LookupOp.
       float factor = 1.0f - mixValue;
       preMultipliedRed   = new int[256];
       preMultipliedGreen = new int[256];
       preMultipliedBlue  = new int[256];
       for (int i = 0; i < 256; i++) {
           int value = (int) (i * factor);
           preMultipliedRed[i]   = value + mix_r;
           preMultipliedGreen[i] = value + mix_g;
           preMultipliedBlue[i]  = value + mix_b;
       }
   }
   /**
*

Returns the mix value of this filter.

    *
    * @return the mix value, between 0.0 and 1.0
    */
   public float getMixValue() {
       return mixValue;
   }
   /**
*

Returns the solid mix color of this filter.

    *
    * @return the solid color used for mixing
    */
   public Color getMixColor() {
       return mixColor;
   }
   /**
    * {@inheritDoc}
    */
   @Override
   public BufferedImage filter(BufferedImage src, BufferedImage dst) {
       if (dst == null) {
           dst = createCompatibleDestImage(src, null);
       }
       int width = src.getWidth();
       int height = src.getHeight();
       int[] pixels = new int[width * height];
       GraphicsUtilities.getPixels(src, 0, 0, width, height, pixels);
       mixColor(pixels);
       GraphicsUtilities.setPixels(dst, 0, 0, width, height, pixels);
       return dst;
   }
   private void mixColor(int[] pixels) {
       for (int i = 0; i < pixels.length; i++) {
           int argb = pixels[i];
           pixels[i] = (argb & 0xFF000000) |
                       preMultipliedRed[(argb >> 16)   & 0xFF] << 16 |
                       preMultipliedGreen[(argb >> 8)  & 0xFF] <<  8 |
                       preMultipliedBlue[argb & 0xFF];
       }
   }

} /*

* $Id: AbstractFilter.java,v 1.1 2007/01/28 01:45:27 gfx Exp $
*
* Dual-licensed under LGPL (Sun and Romain Guy) and BSD (Romain Guy).
*
* Copyright 2005 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* Copyright (c) 2006 Romain Guy <romain.guy@mac.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS"" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*

Provides an abstract implementation of the BufferedImageOp * interface. This class can be used to created new image filters based * on BufferedImageOp.

*
* @author Romain Guy <romain.guy@mac.ru>
*/
abstract class AbstractFilter implements BufferedImageOp {
   public abstract BufferedImage filter(BufferedImage src, BufferedImage dest);
   /**
    * {@inheritDoc}
    */
   public Rectangle2D getBounds2D(BufferedImage src) {
       return new Rectangle(0, 0, src.getWidth(), src.getHeight());
   }
   /**
    * {@inheritDoc}
    */
   public BufferedImage createCompatibleDestImage(BufferedImage src,
                                                  ColorModel destCM) {
       if (destCM == null) {
           destCM = src.getColorModel();
       }
       return new BufferedImage(destCM,
                                destCM.createCompatibleWritableRaster(
                                        src.getWidth(), src.getHeight()),
                                destCM.isAlphaPremultiplied(), null);
   }
   /**
    * {@inheritDoc}
    */
   public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
       return (Point2D) srcPt.clone();
   }
   /**
    * {@inheritDoc}
    */
   public RenderingHints getRenderingHints() {
       return null;
   }

}


      </source>
   
  
 
  



Pulse Animation Field

   <source lang="java">

/*

* Copyright (c) 2007, Romain Guy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.ruponent; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.border.Border; import javax.swing.border.rupoundBorder; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.interpolation.PropertySetter; import org.jdesktop.animation.timing.triggers.FocusTrigger; import org.jdesktop.animation.timing.triggers.FocusTriggerEvent; /**

*
* @author Romain Guy <romain.guy@mac.ru>
*/

public class PulseFieldDemo extends JFrame {

   public PulseFieldDemo() {
       super("PulseField Demo");
       
       add(buildPulsatingField());
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       
       pack();
       setLocationRelativeTo(null);
   }
   
   private JComponent buildPulsatingField() {
       JTextField field = new JTextField(20);
       
       PulsatingBorder border = new PulsatingBorder(field);
       field.setBorder(new CompoundBorder(field.getBorder(), border));
       
       PropertySetter setter = new PropertySetter(
               border, "thickness", 0.0f, 1.0f);
       Animator animator = new Animator(900, Animator.INFINITE,
               Animator.RepeatBehavior.REVERSE, setter);
       animator.start();
       
       JPanel panel = new JPanel(new FlowLayout());
       panel.add(field);
       panel.add(new JButton("OK"));
       panel.add(new JButton("Cancel"));
       return panel;
   }
   
   public static class PulsatingBorder implements Border {
       private float thickness = 0.0f;
       private JComponent c;
       
       public PulsatingBorder(JComponent c) {
           this.c = c;
       }
       
       public void paintBorder(Component c, Graphics g,
               int x, int y, int width, int height) {
           Graphics2D g2 = (Graphics2D) g.create();
           g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                   RenderingHints.VALUE_ANTIALIAS_ON);
           
           Rectangle2D r = new Rectangle2D.Double(x, y, width - 1, height - 1);
           g2.setStroke(new BasicStroke(2.0f * getThickness()));
           g2.setComposite(AlphaComposite.SrcOver.derive(getThickness()));
           g2.setColor(new Color(0x54A4DE));
           g2.draw(r);
       }
       public Insets getBorderInsets(Component c) {
           return new Insets(2, 2, 2, 2);
       }
       public boolean isBorderOpaque() {
           return false;
       }
       public float getThickness() {
           return thickness;
       }
       public void setThickness(float thickness) {
           this.thickness = thickness;
           c.repaint();
       }
   }
   public static void main(String[] args) {
       try {
           UIManager.setLookAndFeel(
                   UIManager.getCrossPlatformLookAndFeelClassName());
       } catch (IllegalAccessException ex) {
           ex.printStackTrace();
       } catch (InstantiationException ex) {
           ex.printStackTrace();
       } catch (UnsupportedLookAndFeelException ex) {
           ex.printStackTrace();
       } catch (ClassNotFoundException ex) {
           ex.printStackTrace();
       }
       
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               new PulseFieldDemo().setVisible(true);
           }
       });
   }

}


      </source>
   
  
 
  



Setter Race Animation

   <source lang="java">

import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.SwingUtilities; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.interpolation.PropertySetter; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.UIManager; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.Point; import javax.swing.JComponent; import javax.imageio.ImageIO; import javax.swing.JFrame; /*

* SetterRace.java
*
* Created on May 3, 2007, 2:37 PM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* Like BasicRace, only this version uses the property setter capabilities
* of the framework.  Instead of manually calculating the position of the
* car, we have the framework do it for us, based on how we set up the
* PropertySetter object.  All of this is done at
* construction time and we merely start or stop the animation based on
* the Go/Stop buttons at runtime.
*
* @author Chet
*/

public class SetterRace implements ActionListener {

   protected Animator timer;
   public static final int RACE_TIME = 2000;
   
   
   /** Creates a new instance of BasicRace */
   public SetterRace(String appName) {
       RaceGUI basicGUI = new RaceGUI(appName);
       
       // Now set up an animation that will automatically
       // run itself with PropertySetter
       timer = PropertySetter.createAnimator(RACE_TIME, basicGUI.getTrack(), 
               "carPosition", TrackView.START_POS, TrackView.FIRST_TURN_START);
       basicGUI.getControlPanel().addListener(this);
   }
   
   public static void main(String args[]) {
       Runnable doCreateAndShowGUI = new Runnable() {
           public void run() {
               SetterRace race = new SetterRace("Property Setter Race");
           }
       };
       SwingUtilities.invokeLater(doCreateAndShowGUI);
   }
   
   /**
    * Handles clicks on Go/Stop buttons to start/stop the animation
    */
   public void actionPerformed(ActionEvent ae) {
       if (ae.getActionCommand().equals("Go")) {
           if (timer != null) {
               timer.stop();
               timer.start();
           } else {
               timer.start();
           }
       } else if (ae.getActionCommand().equals("Stop")) {
           if (timer != null) {
               timer.stop();
           }
       }
   }

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* The GUI used by all of the different race demos.
* It contains a control panel (for the Go/Stop buttons) and a
* TrackView (where the race is rendered)
*
* @author Chet
*/

class RaceGUI {

   private TrackView track;
   private RaceControlPanel controlPanel;
   /**
    * Creates a new instance of RaceGUI
    */
   public RaceGUI(String appName) {
       UIManager.put("swing.boldMetal", Boolean.FALSE);
       JFrame f = new JFrame(appName);
       f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       f.setLayout(new BorderLayout());
       
       // Add Track view
       track = new TrackView();
       f.add(track, BorderLayout.CENTER);
       
       // Add control panel
       controlPanel = new RaceControlPanel();
       f.add(controlPanel, BorderLayout.SOUTH);
       
       f.pack();
       f.setVisible(true);
   }
   
   public TrackView getTrack() {
       return track;
   }
   
   public RaceControlPanel getControlPanel() {
       return controlPanel;
   }

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* This class does the work of rendering the current view of the
* racetrack.  It holds the car position and rotation and displays
* the car accordingly.  The track itself is merely a background image
* that is copied the same on every repaint.
* Note that carPosition and carRotation are both JavaBean properties, which
* is exploited in the SetterRace and MultiStepRace variations.
*
* @author Chet
*/

class TrackView extends JComponent {

   BufferedImage car;
   BufferedImage track;
   Point carPosition;
   double carRotation = 0;
   int trackW, trackH;
   int carW, carH, carWHalf, carHHalf;
   /** Hard-coded positions of interest on the track */
   static final Point START_POS = new Point(450, 70);
   static final Point FIRST_TURN_START = new Point(130, 70);
   static final Point FIRST_TURN_END = new Point(76, 127);
   static final Point SECOND_TURN_START = new Point(76, 404);
   static final Point SECOND_TURN_END = new Point(130, 461);
   static final Point THIRD_TURN_START = new Point(450, 461);
   static final Point THIRD_TURN_END = new Point(504, 404);
   static final Point FOURTH_TURN_START = new Point(504, 127);
   
   /** Creates a new instance of TrackView */
   public TrackView() {
       try {
           car = ImageIO.read(TrackView.class.getResource("beetle_red.gif"));
           track = ImageIO.read(TrackView.class.getResource("track.jpg"));
       } catch (Exception e) {
           System.out.println("Problem loading track/car images: " + e);
       }
       carPosition = new Point(START_POS.x, START_POS.y);
       carW = car.getWidth();
       carH = car.getHeight();
       carWHalf = carW / 2;
       carHHalf = carH / 2;
       trackW = track.getWidth();
       trackH = track.getHeight();
   }
   
   public Dimension getPreferredSize() {
       return new Dimension(trackW, trackH);
   }
   
   /**
    * Render the track and car
    */
   public void paintComponent(Graphics g) {
       // First draw the race track
       g.drawImage(track, 0, 0, null);
       
       // Now draw the car.  The translate/rotate/translate settings account
       // for any nonzero carRotation values
       Graphics2D g2d = (Graphics2D)g.create();
       g2d.translate(carPosition.x, carPosition.y);
       g2d.rotate(Math.toRadians(carRotation));
       g2d.translate(-(carPosition.x), -(carPosition.y));
       
       // Now the graphics has been set up appropriately; draw the
       // car in position
       g2d.drawImage(car, carPosition.x - carWHalf, carPosition.y - carHHalf, null);
   }
   
   /**
    * Set the new position and schedule a repaint
    */
   public void setCarPosition(Point newPosition) {
       repaint(0, carPosition.x - carWHalf, carPosition.y - carHHalf,
               carW, carH);
       carPosition.x = newPosition.x;
       carPosition.y = newPosition.y;
       repaint(0, carPosition.x - carWHalf, carPosition.y - carHHalf,
               carW, carH);
   }
   
   /**
    * Set the new rotation and schedule a repaint
    */
   public void setCarRotation(double newDegrees) {
       carRotation = newDegrees;
       // repaint area accounts for larger rectangular are because rotate
       // car will exceed normal rectangular bounds
       repaint(0, carPosition.x - carW, carPosition.y - carH,
               2 * carW, 2 * carH);
   }
       

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* Go/Stop buttons to control the animation
*
* @author Chet
*/

class RaceControlPanel extends JPanel {

   /** Make these static so that outside classes can easily
    *  add themselves as listeners */
   JButton goButton = new JButton("Go");
   JButton stopButton = new JButton("Stop");
   /**
    * Creates a new instance of RaceControlPanel
    */
   public RaceControlPanel() {
       add(goButton);
       add(stopButton);
   }
   
   public JButton getGoButton() {
       return goButton;
   }
   
   public JButton getStopButton() {
       return stopButton;
   }
   
   public void addListener(ActionListener listener) {
       goButton.addActionListener(listener);
       stopButton.addActionListener(listener);
   }
   

}


      </source>
   
  
 
  



Spline Interpolator Test

   <source lang="java">

import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.TimingTargetAdapter; import org.jdesktop.animation.timing.interpolation.SplineInterpolator; /*

* SplineInterpolatorTest.java
*
* Created on May 3, 2007, 8:24 AM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

*
* @author Chet
*/

public class SplineInterpolatorTest extends TimingTargetAdapter {

   private long startTime;
   private final static int DURATION = 5000;
   
   public void begin() {
       startTime = System.nanoTime() / 1000000;
       System.out.println("Real\tInterpolated");
       System.out.println("----\t------------");
   }
   
   /**
    * TimingTarget implementation: Calculate the real fraction elapsed and
    * output that along with the fraction parameter, which has been 
    * non-linearly interpolated.
    */
   public void timingEvent(float fraction) {
       long currentTime = System.nanoTime() / 1000000;
       long elapsedTime = currentTime - startTime;
       float realFraction = (float)elapsedTime / DURATION;
       System.out.println(realFraction + "\t" + fraction);
   }
   
   public static void main(String args[]) {
       Animator animator = 
               new Animator(DURATION, new SplineInterpolatorTest());
       SplineInterpolator interpolator = new SplineInterpolator(1f, 0f, 0f, 1f);
       animator.setInterpolator(interpolator);
       // Note that you could get similar behavior by setting the following
       // acceleration/deceleration properties instead of the interpolator
       // above:
       //animator.setAcceleration(.5f);
       //animator.setDeceleration(.5f);
       animator.setStartDelay(1000);
       animator.setResolution(DURATION / 10);
       animator.start();
   }

}


      </source>
   
  
 
  



Trigger Race Animation

   <source lang="java">

import javax.swing.SwingUtilities; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.SwingUtilities; import org.jdesktop.animation.timing.Animator; import org.jdesktop.animation.timing.TimingTargetAdapter; import java.awt.event.ActionEvent; import javax.swing.JButton; import javax.swing.SwingUtilities; import org.jdesktop.animation.timing.triggers.ActionTrigger; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.UIManager; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.Point; import javax.swing.JComponent; import javax.imageio.ImageIO; import javax.swing.JFrame; /*

* TriggerRace.java
*
* Created on May 3, 2007, 1:43 PM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* Exactly like SetterRace, only this version uses Triggers to
* start/stop the animation automatically based on the user
* clicking the Go/Stop buttons (no need for an ActionListener here)
*
* @author Chet
*/

public class TriggerRace extends NonLinearRace {

   /** Creates a new instance of TriggerRace */
   public TriggerRace(String appName) {
       super(appName);
       // Clicks on the Go button will atuomatically start the animator
       JButton goButton = controlPanel.getGoButton();
       ActionTrigger trigger = ActionTrigger.addTrigger(goButton, animator);
   }
   
   /**
    * Handle clicks on the Stop button. Clicks on Go are handled through
    * the ActionTrigger above.
    */
   public void actionPerformed(ActionEvent ae) {
       if (ae.getActionCommand().equals("Stop")) {
           animator.stop();
       }
   }
       
   
   public static void main(String args[]) {
       Runnable doCreateAndShowGUI = new Runnable() {
           public void run() {
               TriggerRace race = new TriggerRace("Trigger Race");
           }
       };
       SwingUtilities.invokeLater(doCreateAndShowGUI);
   }

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* This class does the work of rendering the current view of the
* racetrack.  It holds the car position and rotation and displays
* the car accordingly.  The track itself is merely a background image
* that is copied the same on every repaint.
* Note that carPosition and carRotation are both JavaBean properties, which
* is exploited in the SetterRace and MultiStepRace variations.
*
* @author Chet
*/

class TrackView extends JComponent {

   BufferedImage car;
   BufferedImage track;
   Point carPosition;
   double carRotation = 0;
   int trackW, trackH;
   int carW, carH, carWHalf, carHHalf;
   /** Hard-coded positions of interest on the track */
   static final Point START_POS = new Point(450, 70);
   static final Point FIRST_TURN_START = new Point(130, 70);
   static final Point FIRST_TURN_END = new Point(76, 127);
   static final Point SECOND_TURN_START = new Point(76, 404);
   static final Point SECOND_TURN_END = new Point(130, 461);
   static final Point THIRD_TURN_START = new Point(450, 461);
   static final Point THIRD_TURN_END = new Point(504, 404);
   static final Point FOURTH_TURN_START = new Point(504, 127);
   
   /** Creates a new instance of TrackView */
   public TrackView() {
       try {
           car = ImageIO.read(TrackView.class.getResource("beetle_red.gif"));
           track = ImageIO.read(TrackView.class.getResource("track.jpg"));
       } catch (Exception e) {
           System.out.println("Problem loading track/car images: " + e);
       }
       carPosition = new Point(START_POS.x, START_POS.y);
       carW = car.getWidth();
       carH = car.getHeight();
       carWHalf = carW / 2;
       carHHalf = carH / 2;
       trackW = track.getWidth();
       trackH = track.getHeight();
   }
   
   public Dimension getPreferredSize() {
       return new Dimension(trackW, trackH);
   }
   
   /**
    * Render the track and car
    */
   public void paintComponent(Graphics g) {
       // First draw the race track
       g.drawImage(track, 0, 0, null);
       
       // Now draw the car.  The translate/rotate/translate settings account
       // for any nonzero carRotation values
       Graphics2D g2d = (Graphics2D)g.create();
       g2d.translate(carPosition.x, carPosition.y);
       g2d.rotate(Math.toRadians(carRotation));
       g2d.translate(-(carPosition.x), -(carPosition.y));
       
       // Now the graphics has been set up appropriately; draw the
       // car in position
       g2d.drawImage(car, carPosition.x - carWHalf, carPosition.y - carHHalf, null);
   }
   
   /**
    * Set the new position and schedule a repaint
    */
   public void setCarPosition(Point newPosition) {
       repaint(0, carPosition.x - carWHalf, carPosition.y - carHHalf,
               carW, carH);
       carPosition.x = newPosition.x;
       carPosition.y = newPosition.y;
       repaint(0, carPosition.x - carWHalf, carPosition.y - carHHalf,
               carW, carH);
   }
   
   /**
    * Set the new rotation and schedule a repaint
    */
   public void setCarRotation(double newDegrees) {
       carRotation = newDegrees;
       // repaint area accounts for larger rectangular are because rotate
       // car will exceed normal rectangular bounds
       repaint(0, carPosition.x - carW, carPosition.y - carH,
               2 * carW, 2 * carH);
   }
       

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* The GUI used by all of the different race demos.
* It contains a control panel (for the Go/Stop buttons) and a
* TrackView (where the race is rendered)
*
* @author Chet
*/

class RaceGUI {

   private TrackView track;
   private RaceControlPanel controlPanel;
   /**
    * Creates a new instance of RaceGUI
    */
   public RaceGUI(String appName) {
       UIManager.put("swing.boldMetal", Boolean.FALSE);
       JFrame f = new JFrame(appName);
       f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       f.setLayout(new BorderLayout());
       
       // Add Track view
       track = new TrackView();
       f.add(track, BorderLayout.CENTER);
       
       // Add control panel
       controlPanel = new RaceControlPanel();
       f.add(controlPanel, BorderLayout.SOUTH);
       
       f.pack();
       f.setVisible(true);
   }
   
   public TrackView getTrack() {
       return track;
   }
   
   public RaceControlPanel getControlPanel() {
       return controlPanel;
   }

} /**

* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* Go/Stop buttons to control the animation
*
* @author Chet
*/

class RaceControlPanel extends JPanel {

   /** Make these static so that outside classes can easily
    *  add themselves as listeners */
   JButton goButton = new JButton("Go");
   JButton stopButton = new JButton("Stop");
   /**
    * Creates a new instance of RaceControlPanel
    */
   public RaceControlPanel() {
       add(goButton);
       add(stopButton);
   }
   
   public JButton getGoButton() {
       return goButton;
   }
   
   public JButton getStopButton() {
       return stopButton;
   }
   
   public void addListener(ActionListener listener) {
       goButton.addActionListener(listener);
       stopButton.addActionListener(listener);
   }
   

} /*

* NonLinearRace.java
*
* Created on May 3, 2007, 7:49 AM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* Simple subclass of SetterRace that merely sets acceleration/deceleration 
* factors to give a non-linear motion effect to the car"s movement.
*
* @author Chet
*/

class NonLinearRace extends BasicRace {

   /** Creates a new instance of NonLinearRace */
   public NonLinearRace(String appName) {
       super(appName);
       // Accelerate for first half of duration, decelerate for final 10%
       animator.setAcceleration(.5f);
       animator.setDeceleration(.1f);
   }
   
   public static void main(String args[]) {
       Runnable doCreateAndShowGUI = new Runnable() {
           public void run() {
               NonLinearRace race = new NonLinearRace("Non-Linear Race");
           }
       };
       SwingUtilities.invokeLater(doCreateAndShowGUI);
   }

} /*

* BasicRace.java
*
* Created on May 3, 2007, 7:37 AM
*
* Copyright (c) 2007, Sun Microsystems, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the TimingFramework project nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/**

* The simplest version of the animation; set up a Animator to
* move the car from one position to another over a given time period.
* 
* 
* @author Chet
*/

class BasicRace extends TimingTargetAdapter implements ActionListener {

   public static final int RACE_TIME = 2000;    
   Point start = TrackView.START_POS;
   Point end = TrackView.FIRST_TURN_START;
   Point current = new Point();
   protected Animator animator;
   TrackView track;
   RaceControlPanel controlPanel;
   
   /** Creates a new instance of BasicRace */
   public BasicRace(String appName) {
       RaceGUI basicGUI = new RaceGUI(appName);
       controlPanel = basicGUI.getControlPanel();
       controlPanel.addListener(this);
       track = basicGUI.getTrack();
       animator = new Animator(RACE_TIME, this);
   }
   
   //
   // Events
   //
   
   /**
    * This receives the Go/Stop events that start/stop the animation
    */
   public void actionPerformed(ActionEvent ae) {
       if (ae.getActionCommand().equals("Go")) {
           animator.stop();
           animator.start();
       } else if (ae.getActionCommand().equals("Stop")) {
           animator.stop();
       }
   }
   
   /**
    * TimingTarget implementation: calculate and set the current
    * car position based on the animation fraction
    */
   public void timingEvent(float fraction) {
       // Simple linear interpolation to find current position
       current.x = (int)(start.x + (end.x - start.x) * fraction);
       current.y = (int)(start.y + (end.y - start.y) * fraction);
       
       // set the new position; this will force a repaint in TrackView
       // and will display the car in the new position
       track.setCarPosition(current);
   }
   public static void main(String args[]) {
       Runnable doCreateAndShowGUI = new Runnable() {
           public void run() {
               BasicRace race = new BasicRace("BasicRace");
           }
       };
       SwingUtilities.invokeLater(doCreateAndShowGUI);
   }
   

}


      </source>
   
  
 
  



Tumble Item Project

   <source lang="java">

/*

* Copyright (c) 1995 - 2008 Sun Microsystems, Inc.  All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   - Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   - Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in the
*     documentation and/or other materials provided with the distribution.
*
*   - Neither the name of Sun Microsystems nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ 

package components; import javax.swing.ImageIcon; import javax.swing.JApplet; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.Timer; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedInputStream; /**

* @author jag
* @author mem
* @author kwalrath
* @author ir71389
*/

/*

* TumbleItem.java requires these files:
*   all the images in the images/tumble directory
*     (or, if specified in the applet tag, another directory [dir]
*     with images named T1.gif ... Tx.gif, where x is the total
*     number of images [nimgs])
*   the appropriate code to specify that the applet be executed,
*     such as the HTML code in TumbleItem.html or TumbleItem.atag,
*     or the JNLP code in TumbleItem.jnlp
*
*/

public class TumbleItem extends JApplet

                       implements ActionListener {
   int loopslot = -1;  //the current frame number
   String dir;         //the directory relative to the codebase
                       //from which the images are loaded
   Timer timer;
                       //the timer animating the images
   int pause;          //the length of the pause between revs
   int offset;         //how much to offset between loops
   int off;            //the current offset
   int speed;          //animation speed
   int nimgs;          //number of images to animate
   int width;          //width of the applet"s content pane
   Animator animator;  //the applet"s content pane
   ImageIcon imgs[];   //the images
   int maxWidth;       //width of widest image
   JLabel statusLabel;
   //Called by init.
   protected void loadAppletParameters() {
       //Get the applet parameters.
       String at = getParameter("img");
       dir = (at != null) ? at : "images/tumble";
       at = getParameter("pause");
       pause = (at != null) ? Integer.valueOf(at).intValue() : 1900;
       at = getParameter("offset");
       offset = (at != null) ? Integer.valueOf(at).intValue() : 0;
       at = getParameter("speed");
       speed = (at != null) ? (1000 / Integer.valueOf(at).intValue()) : 100;
       at = getParameter("nimgs");
       nimgs = (at != null) ? Integer.valueOf(at).intValue() : 16;
       at = getParameter("maxwidth");
       maxWidth = (at != null) ? Integer.valueOf(at).intValue() : 0;
   }
   /**
    * Create the GUI. For thread safety, this method should
    * be invoked from the event-dispatching thread.
    */
   private void createGUI() {
       //Animate from right to left if offset is negative.
       width = getSize().width;
       if (offset < 0) {
           off = width - maxWidth;
       }
       //Custom component to draw the current image
       //at a particular offset.
       animator = new Animator();
       animator.setOpaque(true);
       animator.setBackground(Color.white);
       setContentPane(animator);
       //Put a "Loading Images..." label in the middle of
       //the content pane.  To center the label"s text in
       //the applet, put it in the center part of a
       //BorderLayout-controlled container, and center-align
       //the label"s text.
       statusLabel = new JLabel("Loading Images...",
                                JLabel.CENTER);
       animator.add(statusLabel, BorderLayout.CENTER);
   }
   //Background task for loading images.
   SwingWorker worker = new SwingWorker<ImageIcon[], Void>() {
       @Override
       public ImageIcon[] doInBackground() {
           final ImageIcon[] innerImgs = new ImageIcon[nimgs];
           for (int i = 0; i < nimgs; i++) {
               innerImgs[i] = loadImage(i + 1);
           }
           return innerImgs;
       }
       @Override
       public void done() {
           //Remove the "Loading images" label.
           animator.removeAll();
           loopslot = -1;
           try {
               imgs = get();
           } catch (InterruptedException ignore) {}
           catch (java.util.concurrent.ExecutionException e) {
               String why = null;
               Throwable cause = e.getCause();
               if (cause != null) {
                   why = cause.getMessage();
               } else {
                   why = e.getMessage();
               }
               System.err.println("Error retrieving file: " + why);
           }
       }
   };
   //Called when this applet is loaded into the browser.
   public void init() {
       loadAppletParameters();
       //Execute a job on the event-dispatching thread:
       //creating this applet"s GUI.
       try {
           SwingUtilities.invokeAndWait(new Runnable() {
               public void run() {
                   createGUI();
               }
           });
       } catch (Exception e) { 
           System.err.println("createGUI didn"t successfully complete");
       }
       //Set up timer to drive animation events.
       timer = new Timer(speed, this);
       timer.setInitialDelay(pause);
       timer.start(); 
       //Start loading the images in the background.
       worker.execute();
   }
   //The component that actually presents the GUI.
   public class Animator extends JPanel {
       public Animator() {
           super(new BorderLayout());
       }
       protected void paintComponent(Graphics g) {
           super.paintComponent(g);
           if (worker.isDone() &&
               (loopslot > -1) && (loopslot < nimgs)) {
               if (imgs != null && imgs[loopslot] != null) {
                   imgs[loopslot].paintIcon(this, g, off, 0);
               }
           }
       }
   }
   //Handle timer event. Update the loopslot (frame number) and the
   //offset.  If it"s the last frame, restart the timer to get a long
   //pause between loops.
   public void actionPerformed(ActionEvent e) {
       //If still loading, can"t animate.
       if (!worker.isDone()) {
           return;
       }
       loopslot++;
       if (loopslot >= nimgs) {
           loopslot = 0;
           off += offset;
           if (off < 0) {
               off = width - maxWidth;
           } else if (off + maxWidth > width) {
               off = 0;
           }
       }
       animator.repaint();
       if (loopslot == nimgs - 1) {
           timer.restart();
       }
   }
   public void start() {
       if (worker.isDone() && (nimgs > 1)) {
           timer.restart();
       }
   }
   public void stop() {
       timer.stop();
   }
   /**
    * Load the image for the specified frame of animation. Since
    * this runs as an applet, we use getResourceAsStream for 
    * efficiency and so it"ll work in older versions of Java Plug-in.
    */
   protected ImageIcon loadImage(int imageNum) {
       String path = dir + "/T" + imageNum + ".gif";
       int MAX_IMAGE_SIZE = 2400;  //Change this to the size of
                                    //your biggest image, in bytes.
       int count = 0;
       BufferedInputStream imgStream = new BufferedInputStream(
          this.getClass().getResourceAsStream(path));
       if (imgStream != null) {
           byte buf[] = new byte[MAX_IMAGE_SIZE];
           try {
               count = imgStream.read(buf);
               imgStream.close();
           } catch (java.io.IOException ioe) {
               System.err.println("Couldn"t read stream from file: " + path);
               return null;
           }
           if (count <= 0) {
               System.err.println("Empty file: " + path);
               return null;
           }
           return new ImageIcon(Toolkit.getDefaultToolkit().createImage(buf));
       } else {
           System.err.println("Couldn"t find file: " + path);
           return null;
       }
   }
   public String getAppletInfo() {
       return "Title: TumbleItem v1.2, 23 Jul 1997\n"
              + "Author: James Gosling\n"
              + "A simple Item class to play an image loop.";
   }
   public String[][] getParameterInfo() {
       String[][] info = {
         {"img", "string", "the directory containing the images to loop"},
         {"pause", "int", "pause between complete loops; default is 3900"},
         {"offset", "int", "offset of each image to simulate left (-) or "
                           + "right (+) motion; default is 0 (no motion)"},
         {"speed", "int", "the speed at which the frames are looped; "
                          + "default is 100"},
         {"nimgs", "int", "the number of images to be looped; default is 16"},
         {"maxwidth", "int", "the maximum width of any image in the loop; "
                             + "default is 0"}
       };
       return info;
   }

}


      </source>