Section 14.1. Generating Frame-Based Animations


[Page 485 (continued)]

14.1. Generating Frame-Based Animations

To make movies, we're going to create a series of JPEG frames and display them. We have included a class FrameSequencer which will help you generate the frames. The class FrameSequencer also creates an object of the class MoviePlayer to display the frames. The MediaTools application on the CD can reassemble JPEG frames into a JMV movie. You can also use tools such as Apple's QuickTime Pro (http://www.apple.com/quicktime) or ImageMagick (http://www.imagemagick.org/), which can also create QuickTime, MPEG, or AVI movies from individual frames (and go in reverseburst a movie into a bunch of frames).

We'll place all of our frames in a single directory, and number them so that the tools know how to reassemble them into a movie in the right order. We'll literally name our files frame0001.jpg, frame0002.jpg, and so on with leading zeros, so that the files are in order when placed in alphabetical order.

Here's our first movie-generating program, which simply moves a red rectangle down diagonally (Figure 14.1). We have created a new class MovieMaker to hold the methods that make movies.

Figure 14.1. A few frames from the first movie: Moving a rectangle.
(This item is displayed on page 487 in the print version)



[Page 486]

Program 130. Simple Motion of a Rectangle

import java.awt.*; /**  * Class to create frames for a movie  * @author Barb Ericson  */ public class MovieMaker {   /**    * Method to make a movie that has a rectangle moving    * around    * @param directory the directory to put the movie    * frames    */   public void makeRectangleMovie(String directory)   {     int framesPerSec = 30;     Picture p = null;     Graphics g = null;     FrameSequencer frameSequencer =       new FrameSequencer(directory);     // loop through the first second     for (int i = 0; i < framesPerSec; i++)     {       // draw a filled rectangle       p = new Picture(640,480);       g = p.getGraphics();       g.setColor(Color.RED);       g.fillRect(i * 10, i * 5, 50,50);       // add frame to sequencer       frameSequencer.addFrame(p);     }     // replay the movie     frameSequencer.show();     frameSequencer.replay(framesPerSec);   }   // main for testing   public static void main(String[] args)   {     MovieMaker movieMaker = new MovieMaker();     String dir = "c:/intro-prog-java/movies/rectangle/";     movieMaker.makeRectangleMovie(dir);   } }



[Page 487]

You can change the directory that will hold the created movie frames from what is specified as the dir variable in the main method. Compile and run the main method for the class MovieMaker. You should see the rectangle move as shown in (Figure 14.1).

Debugging Tip: Out of Memory Error

Working with the number of pictures you have in a movie can cause Java to run out of memory and you can get an out of memory error (java.lang.OutOfMemoryError). When the Java Virtual Machine starts it sets aside some memory to work in and it has a limit of how much total it can use. You can change this by adding some arguments for the Java Virtual Machine when you start it. In DrJava you do this by clicking on EDIT, then PREFERENCES, which will display the PREFERENCES WINDOW. Click on MISCELLANEOUS under CATEGORIES on the left, and then enter -Xmx512m -Xms128m to set the maximum amount of memory to 512 megabytes and the starting memory to 128 megabytes. Then click on OK. It may ask if you really are sure that you want to do this, and you should say OK. Click on RESET to have the new settings affect the INTERACTIONS PANE.

You can actually set the maximum to more RAM memory than you have. It will save some of the items in memory to disk and bring them back in when they are needed. This is called virtual memory.

You can also do this using the command-line tools from Sun:

java -Xmx512m -Xms128m MovieMaker



The key part of this recipe is the g.fillRect(i * 10, i * 5, 50,50);. Each time through the loop we create a new Picture object and then draw the rectangle at a new position in the Picture based on the value of the loop index. When we add the Picture object to the FrameSequencer object, it will write out the frame to the directory. It will also display the current frame using a MoviePlayer object.

Here are the first five values in the call to the fillRect method:

g.fillRect(0,0,50,50);   // i is 0 g.fillRect(10,5,50,50);  // i is 1 g.fillRect(20,10,50,50); // i is 2 g.fillRect(30,15,50,50); // i is 3 g.fillRect(40,20,50,50); // i is 4


While setPixel() gets upset if you try to set a pixel outside of the bounds of the picture, the graphics methods of the Graphics class drawString and fillRect don't generate errors. They'll simply clip the image for the picture, so you can create simple code to make animations and not worry about going out of bounds. This makes creating a tickertape movie fairly simple.


[Page 488]

You can add the following method to the MovieMaker class to generate a movie with the text appearing on the right side of the picture and moving across to the left.

Program 131. Generate a Tickertape Movie

/**  * Method to create a tickertape movie  * @param directory the directory to write to  * @param message the string to display  */ public void makeTickerTapeMovie(String directory,                                 String message) {   int framesPerSec = 30;   Picture p = null;   Graphics g = null;   FrameSequencer frameSequencer =     new FrameSequencer(directory);   Font font = new Font("Arial",Font.BOLD,24);   // loop for 2 seconds of animation   for (int i = 0; i < framesPerSec * 2; i++)   {     // draw the string     p = new Picture(300,100);     g = p.getGraphics();     g.setColor(Color.BLACK);     g.setFont(font);     g.drawString(message,300 - (i * 10), 50);     // add frame to sequencer     frameSequencer.addFrame(p);   }   // replay the movie   frameSequencer.show();   frameSequencer.replay(framesPerSec); }


You can test this with the following main. You can change the directory that will hold the created movie frames from what is specified as the dir variable in the main method. The result of running this main method is shown in Figure 14.2.

public static void main(String[] args)  {    MovieMaker movieMaker = new MovieMaker();    String dir = "c:/intro-prog-java/movies/tickertape/";    movieMaker.makeTickerTapeMovie(dir,"Buy more widgets");   }



[Page 489]

Figure 14.2. Frames from the tickertape method.


Can we move more than one thing at once? Sure! Our drawing code just gets a little more complicated. Here's a recipe that uses sine and cosine to create circular motion to match our linear motion of Program 130 (page 486) (Figure 14.3). You can add this method to the class MovieMaker.

Figure 14.3. Moving two rectangles at once.
(This item is displayed on page 490 in the print version)


Program 132. Move Two Objects at Once

/**  * Method to make a movie that has a two rectangles moving  * around  * @param directory the directory to put the movie  * frames  */ public void makeTwoRectangleMovie(String directory) {   int framesPerSec = 30;   Picture p = null;   Graphics g = null;   FrameSequencer frameSequencer =     new FrameSequencer(directory);   // loop through the first second   for (int i = 0; i < framesPerSec; i++)   {     // draw a filled rectangle     p = new Picture(640,480);     g = p.getGraphics();     g.setColor(Color.RED);     g.fillRect(i * 10, i * 5, 50,50);     g.setColor(Color.BLUE);     g.fillRect(100 + (int) (10 * Math.sin(i)),                4 * i + (int) (10 * Math.cos(i)),                50,50);     // add frame to sequencer     frameSequencer.addFrame(p);   }   // replay the movie   frameSequencer.show();   frameSequencer.replay(framesPerSec); }



[Page 490]

You can test this with the following main. You can change the directory that will hold the created movie frames from what is specified as the dir variable in the main method. The result of executing the following main method is shown in Figure 14.3.

public static void main(String[] args)  {    MovieMaker movieMaker = new MovieMaker();    String dir = "c:/intro-prog-java/movies/rectangle2/";    movieMaker.makeTwoRectangleMovie(dir);  }


We don't have to create our animations out of things that we can draw, like rectangles. We can copy Picture objects to different locations. This kind of code runs pretty slowly.

The recipe below moves Mark's head around on the screen. This method took over a minute to complete on a fast computer. You can add this method to the MovieMaker class.

Program 133. Move Mark's Head
(This item is displayed on pages 490 - 491 in the print version)

/**  * Method to move Mark's head around  */ public void moveMarksHead(String directory) {   // load the picture of Mark   String fName = FileChooser.getMediaPath("blue-Mark.jpg");   Picture markP = new Picture(fName);   // declare other variables   Picture target = null;   FrameSequencer frameSequencer =     new FrameSequencer(directory);   int framesPerSec = 30;   // loop creating the frames   for (int i = 0; i < framesPerSec; i++)   {     target = new Picture(640,480);     target.copy(markP,281,164,382,301,i * 10, i * 5);     frameSequencer.addFrame(target);   } 
[Page 491]
// replay the movie frameSequencer.show(); frameSequencer.replay(framesPerSec); }


You can test this with the following main. You can change the directory that will hold the created movie frames from what is specified as the dir variable in the main method. The result of executing the following main method is shown in Figure 14.4.

public static void main(String[] args)   {     MovieMaker movieMaker = new MovieMaker();     String dir = "c:/intro-prog-java/movies/mark/";     movieMaker.moveMarksHead(dir);   }


Figure 14.4. Frames from moving Mark's head around.


We can use image manipulations that we created in Chapters 47, over multiple frames, to create quite interesting movies. Remember the sunset generating program Program 8 (page 109) in class Picture? First let's add another method that takes the amount to reduce the blue and green by in the picture.

Program 134. Make Sunset with a Parameter
(This item is displayed on pages 491 - 492 in the print version)

/**  * Method to simulate a sunset by decreasing the green  * and blue  * @param the amount to multiply the original values by  */ public void makeSunset(double reduction) {   Pixel[] pixelArray = this.getPixels();   Pixel pixel = null;   int value = 0;   int i = 0; 
[Page 492]
// loop through all the pixels while (i < pixelArray.length) { // get the current pixel pixel = pixelArray[i]; // change the blue value value = pixel.getBlue(); pixel.setBlue((int) (value * reduction)); // change the green value value = pixel.getGreen(); pixel.setGreen((int) (value * reduction)); // increment the index i++; } }


Now let's create a new method in the class MovieMaker to make the sunset happen across many frames (Figure 14.5). To do this we will create a picture of a beach one time and then repeatedly call the method makeSunset to keep reducing the blue and green color.

Figure 14.5. Frames from the make-sunset movie.


Program 135. Make a Slow Sunset Movie
(This item is displayed on pages 492 - 493 in the print version)

/**  * Method to slowly create a sunset  * @param directory the directory to write to  */ public void makeSunsetMovie(String directory) {   // load the picture of the beach   String fName = FileChooser.getMediaPath("beach-smaller.jpg");   Picture beachP = new Picture(fName);   // declare other variables   Picture target = null;   FrameSequencer frameSequencer =     new FrameSequencer(directory); 
[Page 493]
int framesPerSec = 30; frameSequencer.show(); // loop creating the frames for (int i = 0; i < framesPerSec; i++) { beachP.makeSunset(0.95); frameSequencer.addFrame(beachP); } // replay the movie frameSequencer.replay(framesPerSec); }


You can test this method with the following main method:

public static void main(String[] args)   {     MovieMaker movieMaker = new MovieMaker();     String dir = "c:/intro-prog-java/movies/sunset/";     movieMaker.makeSunsetMovie(dir);   }


The swapBackground recipe (Program 43 (page 198)) that we made a while ago can also be used to good effect for generating movies. We can pass in the frame number as the threshold. The effect is a slow fade into the background image (Figure 14.6). Add the following method to the MovieMaker class.

Figure 14.6. Frames from the slow-fade-out movie.


Program 136. Fade Out Slowly
(This item is displayed on pages 493 - 494 in the print version)

/**  * Method to create a movie that fades out the person from  * one background to another.  * @param directory the directory to write to  */ public void makeFadeOutMovie(String directory) {   // load the pictures   String kidF = FileChooser.getMediaPath("kid-in-frame.jpg"); 
[Page 494]
Picture kidP = null; String wallF = FileChooser.getMediaPath("bgframe.jpg"); Picture wallP = new Picture(wallF); String beachF = FileChooser.getMediaPath("beach.jpg"); Picture beachP = new Picture(beachF); // declare other variables FrameSequencer frameSequencer = new FrameSequencer(directory); int framesPerSec = 30; // loop creating the frames for (int i = 0; i < framesPerSec * 2; i++) { kidP = new Picture(kidF); kidP.swapBackground(wallP,beachP,i); frameSequencer.addFrame(kidP); } // replay the movie frameSequencer.show(); frameSequencer.replay(framesPerSec); }


You can test this method with the following main method:

public static void main(String[] args)   {     MovieMaker movieMaker = new MovieMaker();     String dir = "c:/intro-prog-java/movies/fade/";     movieMaker.makeFadeOutMovie(dir);   }




Introduction to Computing & Programming Algebra in Java(c) A Multimedia Approach
Introduction to Computing & Programming Algebra in Java(c) A Multimedia Approach
ISBN: N/A
EAN: N/A
Year: 2007
Pages: 191

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net