JAVA多媒体编程入门(音频部分)

来源:互联网 发布:淘宝评论被判定为广告 编辑:程序博客网 时间:2024/06/05 13:07

Multimedia Programming

多媒体程序设计

591 Playing an AudioClip

播放一段音频剪辑

Kaj verify applet class method

Audio can be a very powerful tool in a multimediaproduct. In fact, a few sound clips can really jazz up a program and draw theusers attention. The Java Developers Kit supports the playing of Suns AU formatsound files. To play an AU sound file in an applet, you can use one of theApplet class play methods, as shown in the following statements:

音频在多媒体中是一个强有力的工具. 事实上,少量的声音片段就可以使一个程序活跃起来,吸引住用户的注意力. Java 的Developers Kit 支持 Sun 的 AU 格式声音文件的播放. 为了在一小应用程序中播放一个 AU 声音文件, 你可以利用Applet类的play方法, 如以下语句所示:

void play(URL url);

void play(URL url,String name);

 

The first play methods url parameter should be aUniform Reference Locator (URL) pointing to the audio file on the Web. Forexample, the following statement plays the audio file sample.au that the appletretrieves from the specified Web site:

Play方法的第一个参数url必须是指向Web上的音频文件的一个URL(统一资源定位器).例如, 以下的语句播放了由小应用程序从指定的Web位置抽取的音频文件sample.au:

play (newURL(http://www.myserver.com/audio/sample.au));

 

Noticethe hardcoded pathname in the previous example. It is good programming practicenot to hardcode the servers name within your program. Should you move theprogram to a different server, or to a different directory, you will have to changethe source code to match the new URL. Instead, you should use the getCodeBasemethod to locate the Java program, and then store the audio file in a directoryrelated to the program file.Using the second form of the play method, the urlparameter points to the audio files location (which you can get using thegetCodeBase method) and the name parameter specifies the name of the audiofile. For example, the following statement plays the audio file sample.au,which resides in the same directory as the Java program:

注意以上例子中的硬编码的路径名. 在你的程序中对服务器名不采用硬编码是一种良好的编程习惯. 如果你要把程序移到另一不同种类的服务器中, 或者另一目录中,你就必须改变源程序的代码以便和新的 URL 匹配. 反之, 你应利用getCodeBase方法来定位Java程序, 然后将音频文件存入到和程序相关的一个目录中. 利用第二种形式的play 方法, 参数url应指向音频文件的位置(你可利用getCodeBase方法来取得这一位置), 而参数name用来指示音频文件的名称. 例如, 以下的语句播放了音频文件sample.au,它放在和Java程序同一目录中:

play(getCodeBase(),"sample.au");

 

592 Using theAudioClip Class

使用AudioClip (音频剪辑)类

The Applet class play method that you examined inthe previous tip provides a convenient way to get and play an audio clip.However, if you want more control over how the audio clip plays (such aswhether the clip plays one time or loops), you can create an AudioClipreference by calling the AudioClip class getAudioClip methods, as shown in thefollowing statements:

你在上一TIP中考察的 Applet 类的play 方法为你提供了获取和播放音频剪辑的一种便当的方法. 但是, 如果你想能更多地控制音频剪辑的播放形式(例如控制播放是否一次性的,或者还是翻复播放), 你可以创建一个AudioClip类, 并引用AudioClip类的getAudioClip方法, 如以下的语句所示:

AudioClipgetAudioClip(URL url);

AudioClipgetAudioClip(URL url, String name);

 

The getAudioClip methods let you create a referenceto the audio file without actually playing it. Later, you can play the audiofile by calling the AudioClip class play method, as shown:

GetAudioClip 方法使你创建了一个音频文件的引用而不实际播放它. 已后, 你可以借助于调用AudioClip 类的 play 方法来播放这一文件, 如:

AudioClip sample_audio= getAudioClip(getCodeBase(), sample.au);

sample_audio.play();  // Play the clip

 

593 Stopping anAudio Clip

   停止播放音频剪辑

In the previous tip, you learned that you can playan audio clip by creating an AudioClip object and calling the AudioClip classplay method. You may be wondering why you need to create an AudioClip objectwhen you can use the Applet class play method. One reason is because you canuse the AudioClip class stop method to stop an audio files playback.

By default, when you start playing an audio clip,Java will play the clip until it reaches the end. There may be times, however,when you want to stop playing at some midpoint. In such cases, you call thestop method. For example, if you are playing a long audio clip in your applet,you may want it to stop playing when the user switches to another Web page. Thefollowing code illustrates how to stop an audio clip when the user switches Webpages:

在上一 TIP 中你已知道, 你可以借助于创建一个AudioClip对象, 然后再调用这一AudioClip对象, 来播放一段音频剪辑. 你可能会奇怪,为什么当你利用Applet类的play方法时要创建一个AudioClip对象. 这里的一个理由是你可利用AudioClip类的stop方法来终止音频文件的播放.

在缺省情况下, 当你开始播放一段音频剪辑时, Java将播放该段音频剪辑,直到结束.但是, 你有的时候想在某个中间点来终止播放. 在这种情况下, 你可调用stop方法.例如, 如果你的小应用程序正在播放一个很长的一段音频剪辑, 你可能要求,当用户切换到其他Web页面时,就终止这一播放. 以下的代码说明了怎样在用户切换Web页面时终止播放一个音频剪辑:

 

 

public class myappletextends Applet {

 

   // Other applet statements here

 

   public void stop()

     {

       if (long_audio_clip != null) // makesure audio clip

         {                          // reference is valid

            long_audio_clip.stop(); // stopplaying the clip

         }

     }

  }

As you have learned, when a user switches Webpages, Java calls the stop method. Within the stop method, you can use theAudioClip class stop method to stop the audio clip. As you can see, the codefirst checks the long_audio_clip object reference to make sure it is not null,which means the variable contains an AudioClip object. Next, the function usesthe stop method to turn off the audio clip.

正如你所了解的那样, 当一用户切换Web页面时,  Java 调用了stop方法. 你可利用AudioClip类的 stop 方法来停止播放音频剪切. 你能看到, 程序首先检测long_audioclip 对象的引用, 要确保它是非空的, 也即变量包含着一个音频剪切对象. 然后,函数利用stop方法来关闭音频剪切的播放.

 

594 Looping an AudioClip

 循环播放一段音频剪切

When your programs play audio clips, there may betimes when you want to repeatedly play an audio clip. In such cases, you callthe AudioClip class loop method to play the audio clip. When you call the loopmethod, Java will play the clip continuously until you call the AudioClip classstop method, or until you start to play another clip. The following applet,splayer.java, plays the audio clip specified by the HTML AUDIOCLIP parameter.To play the clip once, click the play button; to play the clip

repeatedly, click the loop button.

当你的程序播放一段音频剪切时, 有时你想要重复地播放某一段音频剪切.在这种情况下, 你可调用AudioClip 类的 loop 方法. 当你调用 loop 方法时, Java将连续播放这一段音频剪切, 直到你调用了 AudioClip 类的 stop 方法, 或者你开始播放另一个段剪切. 以下的小应用程序splayer.java, 播放了用参数 HTML AUDIOCLIP 指定的一个音频剪切. 为了播放一次音频剪切, 可点击play 按钮; 而要重复播放这一音频剪切, 则应在 loop 按钮点击.

import java.applet.*;

import java.awt.*;

 

public class splayerextends Applet {

 

   AudioClip aclip;

 

   public void init()

     {

       aclip = getAudioClip(getCodeBase(),

                getParameter("AUDIOCLIP"));

 

       add(new Button ("Play"));

       add(new Button ("Stop"));

       add(new Button ("Loop"));

     }

 

   public void stop()

     {

       if (aclip != null)

         aclip.stop();

     }

 

   public boolean action(Event evt, Object arg)

     {

       if (evt.target instanceof Button)

         {

            if (arg.equals("Play"))

              aclip.play();

            else if(arg.equals("Stop"))

              aclip.stop();

            else if(arg.equals("Loop"))

 

            aclip.loop();

            return true;  // events processed

        }

 

      return false; // events not processed

   }

 }

 

595 Building a PianoApplet

 建立一个模拟钢琴的小应用程序

In previous tips, you learned how to play an audioclip. The following program, piano.java, uses a graphical user interface builtfrom Button objects to create a simple piano, as shown in Figure 595. Each timethe user presses a button, the applet plays the corresponding audio clip:

在上一 TIP 中, 你已知道了怎样播放一段音频剪切. 以下的程序,piano.java, 利用一个由Button对象建成的图形用户接口来创建一架 如图955 所示的简单"钢琴". 每当用户按下一个按键时, 小应用程序就播送与此键对应的音频剪切:

import java.applet.*;

import java.awt.*;

 

public class pianoextends Applet {

 

   String items[] ={"C","D","E","F","G","A","B","C'"};

 

   public void init()

     {

       setLayout (new GridLayout(1, 8));

 

       for (int i = 0; i < items.length;i++)

          add (new Button(items[i]));

     }

 

   public boolean action(Event evt, Object arg)

     {

        if (evt.target instanceof Button)

          {

            if (arg.equals("C"))

              play (getCodeBase(),"cnote.au");

            else if (arg.equals("D"))

              play (getCodeBase(),"dnote.au");

            else if (arg.equals("E"))  

              play (getCodeBase(),"enote.au");

            else if (arg.equals("F"))

              play (getCodeBase(),"fnote.au");

            else if (arg.equals("G"))

              play (getCodeBase(),"gnote.au");

            else if (arg.equals("A"))

              play (getCodeBase(),"anote.au");

            else if (arg.equals("B"))

              play (getCodeBase(),"bnote.au");

            else if(arg.equals("C'"))

              play (getCodeBase(),"c1note.au");

 

            return true;  // event processed

          }

        return false; // events not processed

     }

  }

 

 

Figure 595 A Java-based piano.

图595 用Java实现的模拟钢琴

 

596 Converting a.WAV File to a .AU File Format

将 .WAV 文件转换为 .AU文件

As you have learned, Java provides built-insupport for playing Suns AU audio-file format, also known as -law format.However, the current release of Javas JDK does not support other formats, suchas Windows WAV format or the Sound Blaster VOC format. Until JDK supports othersound file formats, you can use one of the many sound utilities available toconvert other formats to Suns AU format.

One such utility is SOund eXchange (SOX) createdby Lance Norskog. This versatile utility lets you change between many differenttypes of sound files. For example, to convert the WAV file, sample.wav, to SunsAU format on a Windows platform, you can type the following command at theMS-DOS prompt:

正如你所了解的那样, Java为 SUN 的 AU 音频文件格式提供了内建的支持; 这种文件格式称作 -law 格式. 但是, Java 目前的JDK版本不支持其它的格式, 诸如Windows的WAV 格式或 Sound Blaster 的 VOC 格式. 在 JDK 将来能支持其他声音文件格式之前, 你可利用目前已有的许多声音实用程序中的任意一种将其它格式转换为 SUN 的 AU 格式.

属于这种实用程序的一个是 Lance Norskog 开发的SOund eXchange (SOX). 这一适用范围很广的实用程序可让你在许多不同类型的声音文件之间相互进行转换. 例如, 在Windows 平台上把WAV格式文件sample.wav转换为AU格式. 你可在 MS-DOS 提示符下打入以下的命令:

C:\> sox sample.wav   sample.au <Enter>

 

Rick  Where did they get Sox?Do we have a license agreement that says we

can distribute it? Is there a Sox web site? Have Phil check if theygave us

the software

 

A copy of SOX is included on this books companionCD-ROM. You can read SOXs readme file and documentation for the supportedformats and availableoptions.

在本书所附的CD-ROM上有SOX的一份COPY. 有关SOX支持的格式与可用选项你可看一下SOX的readme文件及文档.

 

597 Creating aSimple Animation

创建一个简单动画

Animation is the process of displaying images(sometimes called frames) in a sequence to simulate motion. When you createyour first Java animation, you may be tempted to loop through the images,displaying one image after another. For example, the following code fragmentuses a for loop to display a specific number of frame images. As you can see,the for loop delays one-tenth of a second between frames:

动画是显示一连串的图象(有时叫帧)来模拟运动. 当你在创建第一个Java动画时, 你可能想到要一个图象接一个图象地进行循环显示.  例如,以下的代码段利用一个FOR循环来显示指定帧数的图象. 你可以看到, FOR循环间隔十分之一秒显示一帧图象:

public classmy_animation extends Applet {

 

   public void start()

     {

       for (animeframe = 0; animeframe <no_of_frames;

            animeframe ++)

          {

             // set up a frame

             setup_animation_frame(animeframe);

 

             // redraw screen for new frame

             repaint ();

 

             // pause for 0.1 second (100milliseconds)

             // between successive frames

 

             try {

               Thread.sleep (100);

             } catch (InterruptedException e) {};

          }

     }

 

  // Other applet statements here

}

The problem with this approach to animation isthat the call to the repaint method does not immediately draw the screen. As itturns out, Java is an event-driven system which collects requests to paint thescreen so Java can perform them when there are no other events to process. Thefor loop in the previous start method locks up Javas event message deliverysystem and Java cannot repaint the screen until the for loop completes. Thesolution to this problem is to create a thread, which the following codeillustrates:

用这种方法产生动画的问题是:调用repaint方法不能立即在屏幕上画图. 这是因为,Java是一个事件驱动系统,它收集向屏幕上画图的各种请求, 当没有其它的事件要处理时,Java 才来实现它们. 以上start方法的for循环封锁了Java的事件消息提交系统,使Java在for循环结束之前不能在屏幕上画图. 这一问题的解决方法是创建一个按以下代码说明的线程(thread):

 

public classmy_animation extends Applet implements Runnable {

 

  Thread anime = null;

 

  public void start()

    {

      anime = new Thread(this);

      anime.start();

    }

 

  public void run()

    {                                                                          

      for (animeframe = 0; animeframe <no_of_frames;

           animeframe ++)

         {

           // set up a frame

           setup_animation_fram (animeframe);

 

           // redraw screen for new frame

           repaint ();

 

           // pause for 0.1 second (100milliseconds)

           // between successive frames

 

           try {

             Thread.sleep (100);

           } catch (InterruptedException e) {};

         }

    }

 

   // Other applet statements here

 

  }

 

The above example implements the Runnableinterface which lets the applet run a separate thread. The program starts theanime thread object within the applets start method. When the animation threadstarts, Java automatically calls the run method which, in turn, uses the forloop that creates the animation. The section of this book that examines threadswill provide a detailed discussion on thread operations.

以上例子实现了 Runnable接口, 它使小应用程序能运行一个独立的线程. 程序在小应用程序的start方法中起动动画线程对象.  当动画线程起动后, Java自动调用run方法,后者又进一步利用for循环来建立动画. 在本书考察线程的那一部分中将提供有关线程操作的详细讨论.

 

598 Scrolling TextAnimation

滚动文本的动画

In the previous tip, you learned how to createanimation using threads. In this tip, you will create a complete example of aJava animation. The following applet, scroll_hello.java, uses a thread objectwhich repeatedly scrolls the string Hello, world from left to right across theapplet window. As you will see, to move the string across the screen, theapplet simply redraws the string using a different x coordinate each time:

在以上的 TIP 中, 你已知道了怎样利用线程来创建动画. 在本 TIP 里, 你将创建一个完整的Java动画例子. 以下的小应用程序,scroll_hello.java, 利用一个 thread 对象在小应用程序窗口中从左到右地来重复地滚动"Hello, would". 你可以看到, 为了在屏幕上移动字符串, 小应用程序只需在每个时刻在不同的位置上重画这一字符串:

import java.applet.*;

import java.awt.*;

 

public classscroll_hello extends Applet implements Runnable {

 

   int xpos = 0;

   Thread anime = null;

   Image file_img;

 

   public void start()

     {

       if (anime == null)

         {

           anime = new Thread(this);

           anime.start();

         }

     }

 

   public void paint (Graphics g)

     {

       g.drawString ("hello, world",xpos, 70);

     }

 

   public void run()

     {

       while (anime != null)

         {

           xpos += 10;

 

           if (xpos > size().width)

             xpos = 0;

           repaint();

           try {

               Thread.sleep (100);

             }

           catch (InterruptedException e) {};

         }

     }

  }

 

599 Creating aSimple Animation with Images

创建一个简单的图象动画

In the previous tip, you learned how to animate atext string by redrawing the string at different locations within the appletwindow. As it turns out, you can use this same technique to animate images. Thefollowing applet, gif_anime.java, displays a 3-D rotating Java logo, as shownin Figure 599, by cycling through six GIF images:

在以上的 TIP 中, 你已知道了怎样利用在小应用程序窗口的不同位置上重画字符串的办法使一字符串运动起来, 事实上,你可利用同样的技术使图象动起来. 以下的小应用程序,gif_anime.java, 借助于对6幅GIF图象的循环显示, 产生了如图599所示的一个Java logo 的3-D 动画:

 

import java.applet.*;

import java.awt.*;

 

public class gif_animeextends Applet implements Runnable {

 

   intimg_index = 0;

   Thread anime = null;

 

   String img_names[] = {"java1.gif","java2.gif", "java3.gif",

                         "java4.gif","java5.gif", "java6.gif"};

 

   Image java_img[] = new Image[6];

 

   public void init()

     {

       for (int i = 0; i < 6; i++)

         java_img[i] = getImage (getCodeBase(),img_names[i]);

     }

 

   public void start()

     {

       if (anime == null)

         {

           anime = new Thread(this);

           anime.start();

         }

     }

 

   public void paint (Graphics g)

     {

       g.drawImage (java_img[img_index], 0, 0,this);

     }  

 

   public void run()

     {

       while (anime != null)

         {

           img_index++;

 

           if (img_index > 5)

             img_index = 0;

 

           repaint();

 

           try {

               Thread.sleep (200);

             }

           catch (InterruptedException e) {};

         }

     }

  }

The difference between this applet and thescroll_hello.java applet you created in the previous tip is that instead ofadjusting the string position within the run method, the applet uses the runmethod to select which image to display. When you run this applet, you maynotice a lot of screen flickering. The next tip, however, will show you how toreduce the flickering.

本小应用程序与你上一TIP中所创建的小应用程序scroll_hello.java 的差别在于,上一程序是在run方法中调正字符串的位置, 而在本TIP中, 小应用程序利用run方法来选择要显示哪一个程序. 当你运行本小应用程序时, 你可能注意到屏幕有相当多的闪烁. 但在下一TIP中,将告诉你怎研样来减少这种闪烁. 

Production: Insert figure from jtp0009.bmp [MCC]

 

Figure 599 Creating an animation using images.

图99 利用图象创建一个动画

 

600 Overriding theupdate Method to Reduce Flickering

重用update方法来减少闪烁 

In the previous tip, you found that the animationin the applet gif_anime.java suffers from screen flickering. The reason for theflickering is that when the code calls the repaint method to redraw the screen,the repaint method in turn calls the update method. The default update methodclears the drawing area with the background color and then calls the paintmethod to redraw the window contents. However, in the case of thegif_anime.java applet, clearing the background is unnecessary because the codein the paint method will draw the entire display area. Therefore, in this case,you can override the update method with the following code to remove the eraseoperation which, in turn, will reduce the flickering:

在以上的TIP中, 你发现小应用程序 gif_anime.java 在产生动画时, 屏幕会遭受到闪烁. 闪烁的原因在于: 当程序调用repaint方法在屏幕上作图时, repaint方法进一步去调用了update方法; 而缺省的update方法要先用背景的颜色来清除屏幕的作图区,然后再调用paint方法来重画窗口的内容. 但是, 在小应用程序gif_anime.java的情况中, 用背景颜色清除屏幕是不必要的, 因为paint方法中的代码将在整个显示区域中作图. 因此, 在这种情况下, 你可以用以下的代码形式来调用update方法以消除清屏操作, 从而又消除屏幕的闪烁:

public void update(Graphics g)

  {

    paint(g);

  }

 

Overriding the update method in this way will onlywork in cases where the paint method redraws the entire display area. An alternativeto overriding the update method is not to call the repaint method to redraw thescreen. Instead, the applet can call the paint method directly, as shown:

利用这种形式调用update方法只有在paint方法重画整个显示区域时才可以使用. 另外有一种update方法的用法是不调用repaint方法来重画屏幕. 而是由小应用程序直接调用painr方法,如:

Graphics g =getGraphics(); // get graphics object

paint(g);                   // replaces the repaint()call

 

601 ImprovingAnimation Using Clipping Areas

利用剪切区来改进动画

As your animations become more complex, one wayyou can improve the animations efficiency is by redrawing only the screen areathat you need to update. Consider, for example, a case where you move a smallimage across the screen. You could redraw the entire background and then drawthe image at the new location. However, this technique is very inefficientbecause it redraws the screen area that does not need updating. A better way toupdate the screen is to define a clipping region for the area that needsupdating, and then only redraw that part of the screen. The following applet, sfish.java,uses a clipping region to display the animation shown in Figure 601:

当你的动画变得很复杂时, 你能有效地改善动画的一种办法是仅仅重画你必须进行刷新的那一部分屏幕区域. 例如, 考察你在屏幕上移动一幅小图象的情况. 你可以众画整个背景而然后在新位置上画此图象. 但是,这一方法效率很不高, 因为它在不需要刷新的屏幕区域上也重画了. 一种刷新屏幕的较好的办法是为必须刷新的区域定义一个剪切区,然后仅对屏幕的这一部分进行重画. 以下的小应用程序, sfish.java, 利用了剪切区来显示如图 601 所示的动画:

 

import java.applet.*;

import java.awt.*;

 

public class sfishextends Applet implements Runnable {

 

   int xpos = 0;

   Thread anime = null;

   Image fish_img, bg_img;

   int fish_width, fish_height;

 

   public void init()

     {

        fish_img = getImage (getCodeBase(),"sfish.gif");

        fish_width = 100;

        fish_height = 139;

 

        bg_img = getImage (getCodeBase(),"fishback.gif");

     }

 

   public void start()

     {

       if (anime == null)

         {

           anime = new Thread(this);

           anime.start();

         }

     }

 

   public void paint(Graphics g)

     {

       g.drawImage (bg_img, 0, 0, this);

       g.drawImage (fish_img, xpos, 50, this);

     }

 

   public void run()

     {

        while (anime != null)

          {

             Rectangle old_area = new Rectangle(xpos, 50,

                fish_width, fish_height);

 

             xpos += 10;

 

             if (xpos > size().width)

               xpos = 0;

 

             Rectangle new_area = newRectangle(xpos, 50,

                fish_width, fish_height);

 

             Rectangle update_area=new_area.union(old_area);

 

             Graphics g = getGraphics();

 

             g.clipRect (update_area.x,update_area.y,

                update_area.width, update_area.height);

 

             paint (g);

 

             try {

                Thread.sleep (300);

               }

             catch (InterruptedException e) {};

          }

     }

  }

 

Figure 601 An animation that uses a clipping region.

图 601 利用剪切区显示一个动画

 

602 UsingTransparent GIF Files

利用透明的GIF文件

Quite often the objects that you want to drawwithin an applet window have unique shapes. However, image files can only berectangular. When you display an image that contains a non-rectangular object,the images background will cover up the display background. If you examine thesfish.java applet that you created in the previous tip, you will find that theimage covers part of the background. One solution to this problem is to matchthe images background color with that of your applets background. Thistechnique, however, is inappropriate for cases in which the image is drawn overdifferent backgrounds. A better solution is to define an image with atransparent background. Java has built-in support for the use of transparentGIF files. As it turns out, loading a transparent GIF is no different fromloading any other image files. Many graphics utilities and paint programs, suchas Adobe Photoshop, let you create GIF images with a transparent background.The following applet, trans_gif.java, draws a flower image with a transparentbackground, as shown in Figure 602:

在你的小应用程序窗口中想画的图形有许多物体都有独特的形状. 但是, 图象文件只能是矩形的. 当你显示一个非矩形的图象对象时,图象背景将掩盖了显示器背景. 如果你考察你在前一TIP中创建的小应用程序 sfish.java, 你将会发现图象掩盖了部分的显示背景. 这个问题的一种解决办法是将图象背景和显示器背景设成一致. 但这种办法对于图象画在不同背景上时是不合适的. 一种更好的解决办法是在一个透明的背景上定义一个图象. Java已内建了使用透明GIF文件的方法. 实实上,安装透明的GIF文件与安装任何其它图象文件是没有什么区别的. 很多图形和图象软件, 例如Photoshop,都能让你建立带有透明背景的GIF文件. 以下的小应用程序, trans_gif.java, 画了一个如图602所示的带有透明背景的花朵图象:

 

import java.applet.*;

import java.awt.*;

 

public class trans_gifextends Applet {

 

   Image bg_img, fg_img;

 

   public void init()

     {

       bg_img = getImage (getCodeBase(),"bgimg.jpg");

       fg_img = getImage (getCodeBase(),"tranflwr.gif");

     }

 

   public void paint(Graphics g)

     {

       g.drawImage (bg_img, 0, 0, this);

       g.drawImage (fg_img, 50, 40, this);

     }  

 }

 

Figure 602 A flower image with a transparentbackground.

图602 一个带有透明背景的花朵图象

 

603 Enhancing AnAnimation with Double Buffering

 利用双缓冲器来改进动画

fix xxx

When you run the sfish.java animation that youcreated in Tip xxx, you may still notice some flickering. The reason for theflickering is that when the paint method draws each new frame, it draws thebackground first to erase the fish, and then draws the fish again in the newlocation. Therefore, for a brief moment, there is no fish on the screen.Unfortunately, users can detect this intermediate step. The solution to thisproblem is to use double buffering. With double buffering, you first draw yourimage on an off-screen buffer in memory. Then, you copy the buffers contents tothe on-screen display. In this way, the user will not see the erase process. Tocreate the memory-image buffer, you call the Graphics class creatImage method,as shown:

当你运行TIP 602中创建的sfish.java动画时, 你可能注意到屏幕仍有一些闪动.闪动的原因是,paint方法画每一新帧时,它首先是重画背景来清除鱼,然后在新的位置再重画鱼. 所以, 在一短暂瞬间里, 屏幕上是没有鱼的, 而用户是能感觉得出这一中间阶段的. 这一问题的解决是采用双缓冲器. 用双缓冲器时, 你首先把你的图象画在内存的一个缓冲区中, 然后, 再把它拷贝到显示缓冲器. 中间没有一个清屏过程. 为了创建内存的图象缓冲器, 你可按以下的方式调用Graphics类的creatImage方法:

 

Image createImage(intwidth, int height);

 

The width and height parameters specify thedimensions of the image buffer. To draw to the image buffer, you must get areference to a Graphics object by calling the Image class getGraphics method.After you have the Graphics object reference, drawing to the off-screen bufferis no different from drawing to the screen. The following applet,dubf_anime.java, improves the animation of the sfish.java applet by usingdouble buffering:

参数width 和 height 指定了图象缓冲区的大小. 为了把图形画在图象缓冲区中, 你必须调用 Image 类的 getGraphics 方法以获得一个图形对象的引用. 当你得到了图形对象的引用后, 在内存缓冲器中作图和在屏幕上作图就没有什么差别了. 以下的小应用程序dubf_anime.java 利用双缓冲器改进了小应用程序sfish.java 的动画:

*** kaj better explain the processing

import java.applet.*;

import java.awt.*;

 

public classdbuf_anime extends Applet implements Runnable {

 

   int xpos = 0;

   Thread anime = null;

   Image fish_img, bg_img;

   int fish_width, fish_height;

   Image offscreen_buf = null;

 

   public void init()

     {

       fish_img = getImage (getCodeBase(),"tranfish.gif");

       fish_width = 100;

       fish_height = 139;

 

       bg_img = getImage (getCodeBase(),"fishback.gif");

     }

 

   public void start()

     {

       if (anime == null)

         {

           anime = new Thread(this);

           anime.start();

         }

     }

 

   public void paint(Graphics g)

     {

       g.drawImage (bg_img, 0, 0, this);

       g.drawImage (fish_img, xpos, 50, this);

     }  

   public void run()

     {

       while (anime != null)

         {

           if (offscreen_buf == null)

             {

                // create an offscreen bufferwith the same

                // size as the display

 

                offscreen_buf = createImage(size().width,

                   size().height);

             }

 

           Rectangle old_area = new  Rectangle(xpos, 50, fish_width,

                   fish_height);

 

           xpos += 10;

 

           if (xpos > size().width)

             xpos = 0;

 

           Rectangle new_area = new  Rectangle(xpos, 50, fish_width,

                   fish_height);

 

           Rectangle update_area=new_area.union(old_area);

 

           // paint to offscreen buffer

           Graphics offg =offscreen_buf.getGraphics();

           offg.clipRect(update_area.x,update_area.y,

              update_area.width, update_area.height);

 

           paint(offg);

 

           // copy image to onscreen display

           Graphics ong = getGraphics();

           ong.clipRect (update_area.x,update_area.y,

              update_area.width, update_area.height);

           ong.drawImage (offscreen_buf, 0, 0,this);

 

           try {

              Thread.sleep (300);

             }

           catch (InterruptedException e) {};

         }

     }

  }

 

604 Understandingthe ImageObserver

了解ImageObserver(图象观察器)

kaj better define the imageobserver

When your program calls the drawImage method todisplay an image, Java first checks to see if the image is available (if theimage has been loaded and is ready for display). If the image is unavailable,the drawImage method returns without drawing the image, but will notify theImageObserver of its progress. Java sends the notification message to theImageObserver by calling the observers imageUpdate method, as shown in thefollowing statement:

当你的程序调用drawImage 方法在屏幕上显示一图象时,Java首先要检查图象是否可用(图象是否已安装,是否已准备好显示). 如果图象还不能使用, 则调用drawImage方法时将不画图象就返回, 但会通知ImageObserver它已结束.Java通过调用imageUpdate方法把这个通知消息送给ImageObserver,如下所示: 

booleanimageUpdate(Image img, int infoflags, int x, int y,

  int width, int height);

 

The img parameter specifies for which image Javais sending the notification. The infoflags argument specifies the type ofinformation the message contains. The infoflags argument is a bit-mask variablewhose bits can represent a combination of the values listed in Table 604.

其中参数img指定了java为哪一个图象发送通知. 变元infoflags 定义了消息包含的信息类型. 变元infoflags是一个用二进制位表示的变量, 它的各个位可表示如表604所示的各种值的组合:

Bit Field      Meaning

------------------------------------------------------------------------

ABORT          Image production was aborted

ALLBITS        Image is completed

ERROR          An error was encountered

FRAMEBITS      A new image frame is completed

HEIGHT         Height information is now available

PROPERTIES     Image property information is nowavailable

SOMEBITS       Part of the image is available

WIDTH          Width information is now available

------------------------------------------------------------------------

Table 604Flag values specified by an imageUpdate message.

 

位 域              意 义

------------------------------------------------------------------------

ABORT          图象输出已被放弃

ALLBITS        图象已完成

ERROR          遇到一个错误

FRAMEBITS      一幅新图象已结束

HEIGHT         高度信息已可用

PROPERTIES     图象属性信息已可用

SOMEBITS       部分图象已可用

WIDTH          宽度信息已可用

------------------------------------------------------------------------

表 604  由一个 imageUpdate 消息所规定的标志值

 

By default, the imageUpdate method calls therepaint method to redraw the screen area, even in the case when the image isonly partially available. As you will learn in Tip 605, by overriding theimageUpdate method, your programs can detect when the image is loaded and readyfor display.

按缺省, imageUpdate调用repaint方法来重画屏幕区, 即使图象只有部分可用也是这样. 在TIP605中你将会知道,

 

605Overriding imageUpdate Method to Detect Progress of Imaging Loading

利用imageUpdate方法来检测图象安装是否成功

In the previous tip, you learned that Java callsthe imageUpdate method as it loads a graphics image. By overriding theimageUpdate method, you can monitor the progress of the image-loadingoperation. The following applet, img_progress.java, displays progress messagesto the status window to indicate that image loading is in progress:

在以上TIP中, 你已了解Java在安装图象时调用了imageUpdate方法. 通过imageUpdate 的调用,你可以监示图象安装操作的进行. 以下的小应用程序img_progress.java在状态行窗口中显示了过程信息,指示图象正在安装之中:

import java.applet.*;

import java.awt.*;

 

public classimg_progress extends Applet implements Runnable {

 

   int load_index = 0;

   Thread progress = null;

   Image bg_img;

   String ld_str[]  = {"loading *   ", "loading  * ",

                       "loading   * ", "loading    *"};

 

   public void init()

     {

       bg_img = getImage (getCodeBase(),"bgimg.jpg");

     }

 

   public void start()

     {

       if (progress == null)

         {

           progress = new Thread(this);

           progress.start();

         }

     }

 

   public void paint(Graphics g)

     {

       g.drawImage (bg_img, 0, 0, this);

     }  

 

   public void run()

     {

       while (progress != null)

         {

           load_index++;

 

           if (load_index > 3)

             load_index = 0;

 

           showStatus (ld_str[load_index]);

 

           try {

               Thread.sleep (300);

             }

           catch (InterruptedException e) {};

         }

     }

 

   public boolean imageUpdate(Image img, intflags, int x, int y,

                              int w, int h)

     {

       if (img == bg_img)

         {

           if ((flags & ALLBITS) != 0)

             {

               if ( progress != null)

                 {

                    showStatus("loaded");

                    progress.stop();

                 }

             }

         }

       return super.imageUpdate(img, flags, x,y, w, h);

     }

 }

*** kaj describe the processing

 

606 Getting the Sizeof an Image

取得图象的大小

When you use images within a Java program, theremay be times you need to know the size of an image. In such cases, you use theImage class getWidth and getHeight methods. The formats of these methods are asfollows:

当你在一Java程序中使用图象时,你有时需要知道一个图象的大小. 这时, 你可以利用Image 类的getWidth 和 getHeight 方法. 这些方法的格式如下:

intgetWidth(ImageObserver observer);

intgetHeight(ImageObserver observer);

 

These methods return the image width and height inpixels. If, for some reason, the size information is not available, such as ifthe image is not yet loaded, the methods return the value -1. If theinformation is unavailable because the image is not yet loaded, Java will callthe imageUpdate method when the information becomes available. The followingapplet, img_size.java, illustrates the use of the getWidth and getHeightmethods by displaying an image and its size, as shown in Figure 606:

这些方法返回的图象的用象素数表示宽度和高度. 如果由于某种原因图象大小信息不能使用, 例如图象尚未安装, 方法将返回值-1.如果信息不能使用是因为图象尚未安装, 则待信息变成能用时java将调用imageUpdate方法. 以下的小应用程序img_size.java, 通过图象及其尺寸的显示说明了 getWidth 和 getHeight 的用法,如图606所示:

import java.applet.*;

import java.awt.*;

 

public class img_sizeextends Applet {

 

   Image file_img;

   int img_width, img_height;

 

   public void init()

     {

        file_img = getImage (getCodeBase(),"bgimg.jpg");

        img_width = file_img.getWidth(this);

        img_height = file_img.getHeight(this);

     }

 

   public void paint (Graphics g)

     {

        g.drawImage (file_img, 0, 0, this);

        g.setColor (Color.green);

        g.drawString (String.valueOf(img_width)+ "x" +

            String.valueOf(img_height),30, 70);

     }  

  

   public boolean imageUpdate(Image img, intflags, int x, int y,

                              int w, int h)

     {

       if (img == file_img)

         {

           if ((flags & HEIGHT) != 0)

             img_height = h;

            

           if ((flags & WIDTH) != 0)

             img_width = w;

         }

       return super.imageUpdate(img, flags, x,y, w, h);

     }

  }

 

Figure 606  Using the getWidth and getHeight methods.

 

607 UsingMediaTracker to Preload Images

利用MediaTracker(媒体轨道) 来预装图象

As you learned in the gif_anime.java animation youcreated in Tip xxx, image flickering is a problem during screen updateoperations. Even if your applet overrides the update method to remove thescreen erase, flickering may still occur as the image loads. To solve thisproblem, your program can wait until the applet loads all of its images beforeit starts the animation. To help you preload images in this way, Javas AWTlibrary provides the MediaTracker class. In the future, your programs may usethe MediaTracker class to track your applets loading of various media objects,such as images and audio clips. Currently, the MediaTracker only tracks theloading of images.

你在TIP 606所创建的动画 gif_anime.java 中已看到, 图象的闪动是在屏幕的刷新操作过程中引起的. 即使你在小应用程序中利用update方法去掉了清屏操作,由于图象的装入而仍然会出现闪动. 为了解决这一个问题, 你的程序可以在动画开始之前把所有的图象预先都装入(内存). 为了帮助你用这一种办法预装图象, Java的AWT库提供了MediaTracker类. 今后, 你的程序可以利用MediaTracker类为你的小应用程序预装各种多媒体对象,包括图象和音频剪切. 而现在只用MediaTracker 来预装图象.   

To use Javas MediaTracker to track images, youmust first create a MediaTracker object. Then, you must call the addImagemethod to add an image to the tracker. The formats of the addImage methods areas follows:

为了利用Java的 MediaTracker类来预装图象, 你必须首先创建一个 MediaTracker对象. 然后再调用addImage方法在轨道(traker)中装入图象. addImage方法的格式如下:

void addImage(Imageimage, int id);

void addImage(Imageimage, int id, int w, int h);

 

The image parameter specifies the loading of theimage that you want to track. The id parameter specifies an identifier whichyour program can use later to retrieve information about the images. The w andh parameters of the second addImage method specify the width and height of thearea within which you want the image rendered. When you use the addImage methodto add images to the media tracker, you are not limited to assigning one idnumber to one image. In this way, you can add multiple images to the trackerwith the same id number to form a tracking group.Two MediaTracker class methodsyou will use to synchronize your graphics operations are waitForAll andwaitForId. The formats of these two methods are as follows:

参数image指定了你想装入的图象.参数id是一个标识符, 你的程序以后可利用它来提取图象有关的信息. 在第二个addImage方法中出现的参数w和h是代表你要画的图象区域的宽度和高度.当你使用addImage方法把图象加入到多媒体轨迹中去时, 你不用限制一幅图象用一个id号. 采用这种方法后, 你就可以利用同一个id在轨迹中加入多幅图象以形成轨迹群.

MediaTracker类中用来同步你的图象操作的方法是waitForAll 和 waitForId. 这两个方法的格式如下:

 

void waitForAll();

void waitForID(intid);

fix xxx

 

The waitForID method will not return control tothe caller until the images in the tracker with the specified id are loaded.The waitForAll method will not return control to the caller until all images inthe tracker are loaded. The following applet, tracked_anime.java, provides animproved version of the gif_anime.java applet that you created in Tip xxx. Theanimation will not start until all images are loaded.

在指定了id的图象没有装入轨迹之前,waitForID方法不返回控制到调用者.而waitForAll

方法在轨迹中所有的图象没有装入之前不把控制返回给调用者. 以下的小应用程序 tracked_anime.java 提供了TIP 606小应用程序中创建的gif_anime.java 的一个改进版. 动画要在所有的图象装入后才开始执行.

import java.applet.*;

import java.awt.*;

 

public classtracked_anime extends Applet implements Runnable {

 

   int img_index = 0;

   Thread anime = null;

   String img_names[] = {"java1.gif","java2.gif", "java3.gif",

                         "java4.gif","java5.gif", "java6.gif"};

 

   Image java_img[] = new Image[6];

 

   public void init()

     {

       MediaTracker mt = newMediaTracker(this);

 

       for (int i = 0; i < 6; i++)

          {

            java_img[i] = getImage(getCodeBase(), img_names[i]);

            mt.addImage (java_img[i], i);

          }

 

       try {

           mt.waitForAll();

         }

       catch (InterruptedException e) {};

     }

 

   public void start()

     {

       if (anime == null)

         {

            anime = new Thread(this);

            anime.start();

         }

     }

 

   public void stop()

     {

       if (anime == null)

         {

            anime.stop();

         }

     }

 

   public void paint(Graphics g)

     {

       g.drawImage (java_img[img_index], 0, 0,this);

     }  

 

   public void run()

     {

       while (anime != null)

         {

           img_index++;

 

           if (img_index > 5)

             img_index = 0;

 

           paint (getGraphics());

 

           try {

               Thread.sleep (200);

             }

           catch (InterruptedException e) {};

         }

     }

 }

 

 

608Setting Time Limits in MediaTracker When Loading an Image

在装入图象时在MediaTracker中设置时间极限

In Tip xxx, you learned that by using theMediaTracker class waitForAll and waitForID methods, you can suspend your programsuntil Java has loaded the specified image files. The disadvantage of thewaitForID and waitForAll methods is that these methods will wait forever untilthe tracked images are loaded. If, for some reason, the images are never loadedbecause the image file does not exist or network problems prevent the filesaccess,

your program will lock up. Therefore, you may wantto use the other forms of the waitForID and waitForAll methods. The followingstatements illustrate the formats of the waitForID and waitForAll methods:

在 TIP 608 中, 你已知道利用了MediaTracker类waitForAll和waitForID方法, 你可以挂起你的程序, 直到Java已装好指定图象文件.waitForAll和waitForID方法的缺点是运动的图象在装入之前这些方法会一直等下去. 但因为某些原因,如图象不存在, 或网络问题妨碍了文件的存取, 图象可能永远不能装入, 你的程序就会被锁住. 因此, 你可能要用其它的方式来使用waitForAll和waitForID方法. 以下的语句说明waitForAll和waitForID方法的格式:

void waitForAll(longms);

void waitForID(int id,long ms);

The ms parameter specifies the maximum time inmilliseconds that the call will wait before returning control to the caller.When the function returns, you can then use the statusID or statusAll methodsto determine if the images successfully loaded. The formats of the statusID andstatusAll methods are as follows:

参数 ms 规定了在控制返回到调用者之前的最大调用等待时间(毫秒数). 当函数返回时, 你就可以用statusID或 statusAll 方法来确定图象是否已装入成功.statusID和 statusAll 方法的格式如下:

int statusID(int id,boolean load);

int statusAll(booleanload);

kaj check the meaning of the load parameter

 

The load parameter specifies whether you want thetracker to start loading the images or not. Both of these methods return abit-field value that can be a combination of the status bits listed in Table608.

参数load指定了你是否要跟综器开始装入图象. 这两个方法都返回一个二进制位的值,它是如表608所示的状态字的组成位:

 

Bit Field  Meaning

LOADING    Still loading images

ABORTED    Loading aborted

ERRORED     Anerror was detected

COMPLETE   Image loading completed

 

 

 位 域               意    义

--------------------------------------------------------------------------

LOADING            继续安装图象

ABORTED            放弃安装

ERRORED            检测到一个错误

COMPLETE           图象安装完成

--------------------------------------------------------------------------

Table608 Bit field values returned by the statusID and statusAll methods.

表608 由statusID 和statusAll 方法返回的位域值

 

609 How to BuildZoom-In Animation Special Effects

怎样建立Zoom-In动画特殊效果

When your programs manipulate images, there may betimes when your program

needs to switch between two images. For example,within a slide-show program, you may find that switching instantly from oneimage to another is distracting. Rather than using an instantaneous switch fromone image to the next, your programs can switch images progressively by zoominginto the next image. The key to this type of special effect is your use of thedrawImage method, which lets you stretch the image. The following applet,zoom_in.java, implements the fade-in special effect. Figure 609 illustrates theprograms output:

当你的程序操纵图象时, 有时你的程序需要在两个图象之间进行切换. 例如, 在一幻灯片显示程序中, 你能看到从一幅图象瞬即切换另一幅图象是很迷惑人的. 与其说是从一幅图象切换到另一幅图象, 不如说你的程序能借助于快速移动镜头的办法逐渐地从一幅图象切换到另一幅图象. 这一类型的特殊效果的关键是你使用了drawimage方法,它使你展现了图象.以下的小应用程序,zoom_in.java,实现了淡入(fade-in)特效. 图609说明了程序的输出:

 

import java.applet.*;

import java.awt.*;

importjava.awt.image.*;

 

public class zoom_inextends Applet implements Runnable {

 

   int img2_width, img2_height;

   Image img1, img2;

   Image offscreen;

   Thread anime = null;

 

   public void init()

     {

       img1 = getImage (getCodeBase(),"turtoise.gif");

       img2 = getImage (getCodeBase(),"sheepb.gif");

 

       MediaTracker mt = newMediaTracker(this);

       mt.addImage (img1, 1);

       mt.addImage (img2, 1);

 

       try {

          mt.waitForAll();

         }

       catch (InterruptedException e) {};

 

       img2_width = 0;

       img2_height = 0;     

     }

 

   public void update (Graphics g)

     {

       paint(g);

     }

 

   public void paint(Graphics g)

     {

       // prepare offscreen image

       Graphics goff = offscreen.getGraphics();

       goff.drawImage (img1, 0, 0, this);

 

       int x = (size().width - img2_width) / 2;

       int y = (size().height - img2_height) /2;

       goff.drawImage (img2, x, y, img2_width,img2_height, this);

 

       // copy offscreen to onscreen

       g.drawImage (offscreen, 0, 0, this);

       goff.dispose();

     }

 

   public void run()

     {

       Image tempimg;

       int width_inc, height_inc;

 

       while (anime != null)

         {

           // display the first image only

           img2_width = img2_height = 0;

 

           repaint();

 

           try {

              Thread.sleep (100);

             }

           catch (InterruptedException e) {};

 

           width_inc = img2.getWidth(null) / 16;

           height_inc = img2.getHeight(null) /16;

 

           img2_width = img2.getWidth(null) -(width_inc * 15);

           img2_height = img2.getHeight(null) -(height_inc * 15);

 

           for (int i = 0; i < 16; i++)

             {

               repaint();

               try {

                  Thread.sleep (100);

                 }

               catch (InterruptedException e){};

 

               img2_width += width_inc;

               img2_height += height_inc;

             }

 

           // swap images

           tempimg = img1;

           img1 = img2;

           img2 = tempimg;

           tempimg = null;

         }

     }

 

   public void start()

     {

       offscreen = createImage (size().width,size().height);

 

       if (anime == null)

         {

           anime = new Thread(this);

           anime.start();

         }

     }

 }

kaj explain processing

 

 

Figure 609 An applet illustrating the zoom-in special effect.

图609 说明zoom-in特效的一个小应用程序


 

Multimedia Programming.1

多媒体程序设计... 1

591 Playing anAudio Clip.1

播放一段音频剪辑...1

592 Using theAudioClip Class.2

使用AudioClip (音频剪辑)类...2

593 Stopping anAudio Clip.2

停止播放音频剪辑...2

594 Looping anAudio Clip.3

循环播放一段音频剪切...3

595 Building aPiano Applet5

建立一个模拟钢琴的小应用程序...5

596 Converting a.WAV File to a .AU File Format6

将 .WAV 文件转换为 .AU文件...6

597 Creating aSimple Animation.7

创建一个简单动画...7

598 Scrolling TextAnimation.9

滚动文本的动画...9

599 Creating aSimple Animation with Images.10

创建一个简单的图象动画...10

600 Overriding theupdate Method to Reduce Flickering.12

重用update方法来减少闪烁...12

601 ImprovingAnimation Using Clipping Areas.13

利用剪切区来改进动画...13

602 UsingTransparent GIF Files.15

利用透明的GIF文件...15

603 Enhancing AnAnimation with Double Buffering.16

利用双缓冲器来改进动画...16

604 Understandingthe ImageObserver.18

了解ImageObserver(图象观察器)18

605 OverridingimageUpdate Method to Detect Progress of Imaging Loading.20

利用imageUpdate方法来检测图象安装是否成功...20

606 Getting theSize of an Image.21

取得图象的大小...21

607 UsingMediaTracker to Preload Images.23

利用MediaTracker(媒体轨道) 来预装图象...23

608 Setting TimeLimits in MediaTracker When Loading an Image.26

在装入图象时在MediaTracker中设置时间极限...26

609 How to BuildZoom-In Animation Special Effects.27

怎样建立Zoom-In动画特殊效果...27

 


原创粉丝点击