Morphing one image into another in Swing

Morphing is short for metamorphosing, it refers to an animation technique in which one image is gradually turned into another.

This example morphs one image into another (with same width and height). It first precalculates R G and B array that contains for each pixel the change that should be applied to a (x, y) pixel in image 1 to get to the (x, y) pixel in image 2 after iterating for nFrames steps.
Then it loops through the pixelvalues of the source image and adds the RGB change to it. After nFrames steps, the resulting image is image 2.

import java.awt.event.*;
import java.awt.image.*;
import java.awt.color.*;
import javax.swing.*;
import java.awt.*;
                           
public class Main extends JFrame
{
   int width, height; 
 
   int source[];
   int dest[];
 
   double dalpha[], dred[], dgreen[], dblue[];
 
   int nFrames;
 
   JLabel jlabel;
 
   public Main(String image1, String image2, int nFrames) throws Exception {
      BufferedImage bi1 = createBufferedImage(image1);
      BufferedImage bi2 = createBufferedImage(image2);
 
      width = bi1.getWidth();
      height = bi1.getHeight();
 
      source = new int[width*height];
      dest = new int[width*height];
 
      dalpha = new double[width*height];
      dred = new double[width*height];
      dgreen = new double[width*height];
      dblue = new double[width*height];
 
      this.nFrames = nFrames;
 
      PixelGrabber grabber1 = new PixelGrabber(bi1,0,0,width,height,source,0,width);
      grabber1.grabPixels();
 
      PixelGrabber grabber2 = new PixelGrabber(bi2,0,0,width,height,dest,0,width);
      grabber2.grabPixels();
 
      calcIntervals();
 
      // add the image inside a JLabel component to the window
      jlabel = new JLabel(new ImageIcon(bi1));
      getContentPane().add(jlabel);
      pack();
                          
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
   }
 
   public void calcIntervals() {
      for (int i=0; i<source.length; i++) {
         int sourcePixel = source[i];
         int sourceAlpha = (sourcePixel >> 24) & 0xff;
         int sourceRed   = (sourcePixel >> 16) & 0xff;
         int sourceGreen = (sourcePixel >>  8) & 0xff;
         int sourceBlue  = (sourcePixel      ) & 0xff;
          
         int destPixel = dest[i];
         int destAlpha = (destPixel >> 24) & 0xff;
         int destRed   = (destPixel >> 16) & 0xff;
         int destGreen = (destPixel >>  8) & 0xff;
         int destBlue  = (destPixel      ) & 0xff;
 
         dalpha[i] = (double) (destAlpha - sourceAlpha) / nFrames;
         dred[i] = (double) (destRed - sourceRed) / nFrames;
         dgreen[i] = (double) (destGreen - sourceGreen) / nFrames;
         dblue[i] = (double) (destBlue - sourceBlue) / nFrames;
      }
   }
 
   public int[] calcFrame(int frame) {
      int result[] = new int1;
      for (int i=0; i<source.length; i++) {
         int sourcePixel = source[i];
         int sourceAlpha = (int) (((sourcePixel >> 24) & 0xff) + (dalpha[i] * frame));
         int sourceRed   = (int) (((sourcePixel >> 16) & 0xff) + (dred[i] * frame));
         int sourceGreen = (int) (((sourcePixel >>  8) & 0xff) + (dgreen[i] * frame));
         int sourceBlue  = (int) (((sourcePixel      ) & 0xff) + (dblue[i] * frame));
          
         result[i] = argb2p(sourceAlpha, sourceRed, sourceGreen, sourceBlue);
      }
 
      return result;
   }
 
   public int argb2p(int alpha, int red, int green, int blue) {
      int t = 0;
 
      t |= (alpha << 24);
      t |= (red << 16);
      t |= (green << 8);
      t |= blue;
 
      return t;
   }         
 
   public void startMorphing(int delay) {
      for (int i=0; i<nFrames+1; i++) {
         int result[] = calcFrame(i);
         Image img = createImage(new MemoryImageSource(width, height, result, 0, width));
         jlabel.setIcon(new ImageIcon(img));
 
         try {
            Thread.sleep(delay);
         }
         catch(InterruptedException e) { }
      }
   }
 
   public BufferedImage createBufferedImage(String filename) {
      Image img = Toolkit.getDefaultToolkit().getImage(filename);
      try {
         MediaTracker tracker = new MediaTracker(this);
         tracker.addImage(img, 0);
         tracker.waitForID(0);
      }
      catch ( Exception e ) {} 
  
      BufferedImage bi = 
         new BufferedImage(img.getWidth(this), img.getHeight(this),
                        BufferedImage.TYPE_INT_RGB);
      Graphics2D g2d = bi.createGraphics();
      g2d.drawImage(img, 0, 0, this);
  
      return bi;
   }
  
   public static void main(String args[]) throws Exception {
      if (args.length != 4) {
         System.out.println("usage: Java Main image1 image2 framecount delay");
         System.exit(1);
      }
 
      Main main = new Main(args[0], args[1], Integer.parseInt(args[2]));
      main.show();
      main.startMorphing(Integer.parseInt(args[3]));
   }
}

Test it out with:

java Main ally.jpg lion.jpg 200 10

Where
ally.jpg is the source image
lion.jpg is the destination image
200 is the number of frames in between
10 the delay in milliseconds between showing each frame

ally.jpg:

lion.jpg: