欧美性猛交黑人xxxx,成人毛片一区二区三区,久久综合九色综合精品,男的把j放进女人下面视频免费

利用Java 3D技術(shù)播放動畫之QTJ技術(shù)

  • 發(fā)布于:2023-08-16
  • 194 人圍觀
  在第一部分內(nèi)容中,我描述了怎樣借助Java 媒體框架(JMF)的幫助實(shí)現(xiàn)在一個Java 3D場景中播放動畫片斷。其實(shí)現(xiàn)主要使用了模型-視圖-控制器設(shè)計(jì)模式:

  · 動畫屏幕是視圖元素,由JMFMovieScreen類描述。

  · 動畫是模型部分,由JMFSnapper類所管理。


  · 一個Java 3D Behavior類TimeBehavior是控制器,由它完成從動畫中的周期性幀檢索,然后在屏幕上畫出。

  在這篇文章中,我將revisit該動畫組件,用 QuickTime for Java (QTJ)來重新實(shí)現(xiàn)之。QTJ在QuickTime API之上提供了一個面向?qū)ο蟮腏ava 層,使之有可能實(shí)現(xiàn)播放,編輯和創(chuàng)建QuickTime 動畫;捕獲音頻與視頻;執(zhí)行2D和3D動畫。QuickTime在Mac 和Windows上平臺都可以使用。關(guān)于QTJ的安裝,文檔和舉例的細(xì)節(jié)信息請參見developer.apple.com/quicktime/qtjava。

  由QTJ 來取代JMF 設(shè)計(jì)模式的結(jié)果對應(yīng)用程序影響很小-只有動畫類JMFSnapper分離出來,由QuickTime for Java版本的QTSnapper所代替。

  圖 1展示了QTJ 版本的Movie3D應(yīng)用程序中的兩幅屏幕快照,其中右邊的那幅是屏幕從后面看上去的視圖。


圖 1.QTJ Movie3D應(yīng)用程序中的兩幅視圖

  如果快點(diǎn)回顧一下第一部分中的圖1,你會發(fā)現(xiàn)基于QTJ的應(yīng)用程序和JMF 版本的實(shí)現(xiàn)沒有太明顯的區(qū)別。

  但是,如果細(xì)致比較一下這兩處執(zhí)行程序會發(fā)現(xiàn)有兩個變化:QTJ版本的動畫像素化pixelation 更明顯,且播放速度更慢些。像素化的出現(xiàn)是由于原始的動畫被從MPEG 格式翻譯成QuickTime的MOV格式所致,可以借助于一個更好的轉(zhuǎn)換工具來實(shí)現(xiàn)修補(bǔ)。速度問題更為根本性:它與QTSnapper的基本實(shí)現(xiàn)有關(guān)。

  該文中主要涉及到:

  ·討論QTSnapper實(shí)現(xiàn)中的兩種主要方法。一種方法是把動畫的每一幀都著色到屏幕上去,另一種方法是基于當(dāng)前時(shí)間選擇一幀。 后一種方法意味著,可以跳過一些幀,使得動畫顫動一點(diǎn),但是卻使動畫播放速度加快。

  ·介紹幾種簡單的幀/秒(FPS)計(jì)算方法,我將用之來判斷不同實(shí)現(xiàn)方式的相對速度的不同,并用來檢測跳過的幀。

  我不會再細(xì)致地介紹動畫屏幕和動畫更新行為,因?yàn)檫@些與第一部分中是一致的。

  下面我將詳細(xì)介紹的是我應(yīng)用在QTSnapper中的用于從動畫中提取幀的QTJ技術(shù)。

  1. 程序?qū)崿F(xiàn)中的兩幅輪廓圖

  下面的圖2描述了該應(yīng)用程序中的場景圖。


圖 2. Movie3D場景圖

  該圖幾乎和第一部分中的一模一樣。

  QuickTime動畫由QTSnapper類負(fù)責(zé)裝載。動畫屏幕由QTMovieScreen創(chuàng)建,它管理一個放置在跳棋盤地板上的Java 3D四邊形。每隔40毫秒,TimeBehavior 對象調(diào)用QTMovieScreen中的nextFrame()方法一次,該方法調(diào)用中QTSnapper 的getFrame()方法來取得動畫中的一幀,該幀最后被放置到由QTMovieScreen管理的四邊形上。

  JMFSnapper和QTSnapper之間有一個重要的不同。JMFSnapper返回當(dāng)前正播放動畫的當(dāng)前幀,而QTSnapper依賴于一個遞增的索引值返回動畫中的當(dāng)前幀。

  例如,當(dāng)getFrame()在JMFSnapper中被反復(fù)調(diào)用時(shí),它可以檢索幀1,3,6,9,11,等等,具體依賴于調(diào)用的方法和動畫的播放速度。當(dāng)在QTSnapper中調(diào)用getFrame()時(shí),它將返回幀1,2,3,4,等等。

  下面的圖3描述了該應(yīng)用程序的UML類圖,其中僅顯示了類的公共方法。


圖 3. Movie3D類圖

  除了動畫屏幕QTMovieScreen類和動畫QTSnapper類名字的區(qū)別外,這里的應(yīng)用程序類繼承圖與第一部分中的沒有區(qū)別。事實(shí)上,只有Snapper類的內(nèi)部實(shí)現(xiàn)變更了一些。

  把應(yīng)用程序從JMF Movie3D版本遷移到QTJ版本要求代替Snapper類。另外,還需要把動畫屏幕類中的兩行代碼改變一下,其中聲明了Snapper 類并被實(shí)例化:

//全局變量定義
private QTSnapper snapper; //以前是JMFSnapper
//在構(gòu)造器中以fnm方式裝入動畫
snapper = new QTSnapper(fnm);

  這兩處變化是把JMFMovieScreen 改名為QTMovieScreen的唯一原因。

  所有該示例的代碼以及本文的一個早期版本,都能在KGPJ website處找到。

  2. 一部幀到幀F(xiàn)rame-By-Frame 的動畫

  理解了QTSnapper的內(nèi)部工作機(jī)理,有助于對QuickTime動畫的結(jié)構(gòu)有一個基本了解。每個動畫可能由多個音頻和視頻軌道合成,而在時(shí)間上相重疊。下圖4展示了這種基本思想。


圖 4.一個QuickTime動畫的內(nèi)部結(jié)構(gòu)

  每個軌道管理自己的數(shù)據(jù),如它包含的媒體的類型和媒體本身。該媒體容器具有它自己的數(shù)據(jù)結(jié)構(gòu),包括持續(xù)時(shí)間和播放速率(也就是,每秒要顯示多少個樣本)。這里的媒體實(shí)際上是一系列的樣本(或者幀),第一個樣本從時(shí)間0開始(with respect to media time)。樣本被索引化了,如第一個樣本在位置1處(而不是0)。

  簡化后的QuickTime軌道和媒體結(jié)構(gòu)如下圖5所示。


圖 5. 一個QuickTime軌道和媒體的內(nèi)部結(jié)構(gòu)


  要想更全面的了解,請參考QuickTime教程的動畫部分。

存取動畫的視頻媒體

  在QTSnapper類構(gòu)造器中打開動畫:

// 全局變量
private boolean isSessionOpen = false;
private OpenMovieFile movieFile;
private Movie movie;
//在構(gòu)造器中啟動一個QuickTime會話
QTSession.open();
isSessionOpen = true;
//打開動畫
movieFile =OpenMovieFile.asRead( new QTFile(fnm) );
movie = Movie.fromFile(movieFile);

  QTSession.open()的執(zhí)行來實(shí)現(xiàn)使用QuickTime 之前對其初始化。在程序最后的終止時(shí)刻應(yīng)該有一個相應(yīng)的QTSession.close()調(diào)用。

  視頻軌道的定位(如果存在的話)及其媒體的存取如下:

//更多的全局變量
private Track videoTrack;
private Media vidMedia;
//在構(gòu)造器中,從動畫中提取視頻軌道
videoTrack =
movie.getIndTrackType(1,
StdQTConstants.videoMediaType,
StdQTConstants.movieTrackMediaType);
if (videoTrack == null) {
 System.out.println("Sorry, not a video");
 System.exit(0);
}
//取得由視頻軌道使用的媒體
vidMedia = videoTrack.getMedia();

  一旦媒體被暴露出來exposed,就可以從中提取各種信息:

//更多的全局變量
private MediaSample mediaSample;
private int numSamples; //樣本數(shù)
private int sampIdx; //當(dāng)前樣本索引
private int width; // 幀寬度
private int height; //幀高度
//在構(gòu)造器中
numSamples = vidMedia.getSampleCount();
sampIdx = 1; //取得軌道中的第一個樣本
mediaSample = vidMedia.getSample(0,
vidMedia.sampleNumToMediaTime(sampIdx).time,1);
//把圖像的寬度和高度值存儲在該樣本中
ImageDescription imgDesc =
ImageDescription) mediaSample.description;
width = imgDesc.getWidth();
height = imgDesc.getHeight();

  sampIdx用作計(jì)數(shù)器,實(shí)現(xiàn)樣本(第一個樣本從位置1開始)的反復(fù)提取。

  動畫圖像的寬度和高度信息通過分析第一個樣本得到,不過假設(shè)前提是所有的樣本使用相同的尺寸。

  計(jì)算FPS

  由QTSnapper返回的每秒幀數(shù)將用于后面的對該類不同實(shí)現(xiàn)策略的比較。在構(gòu)造器中實(shí)現(xiàn)必需元素的初始化的代碼如下:

//幀速率全局變量
private long startTime;
private long numFramesMade;
//在構(gòu)造器中初始化它們
startTime = System.currentTimeMillis();
numFramesMade = 0;

  收尾處理

  當(dāng)應(yīng)用程序即將終止時(shí),QTSnapper中的stopMovie()方法被調(diào)用,它給出FPS值并關(guān)閉QuickTime。

// 全局變量
private DecimalFormat frameDf=new DecimalFormat("0.#");//1dp
synchronized public void stopMovie()
{
 if (isSessionOpen) {
  //報(bào)告幀速率
  long duration =System.currentTimeMillis() - startTime;
  double frameRate = ((double) numFramesMade*1000.0)/duration;
  System.out.println("FPS: " +frameDf.format(frameRate));
  QTSession.close(); //關(guān)閉QuickTime
  isSessionOpen = false;
 }
}

  stopMovie()和getFrame()均被同步化處理,以確保正在從動畫中進(jìn)行幀復(fù)制時(shí)不會出現(xiàn)QuickTime會話終止的情形。

  抓幀

  getFrame()方法從動畫中以BufferedImage 對象的形式返回一個樣本(一幀)。該幀的選取是通過存儲在變量sampIdx(其取值范圍從1到numSamples,然后重復(fù))中的索引號實(shí)現(xiàn)的。

// 全局變量
private BufferedImage img, formatImg;
synchronized public BufferedImage getFrame()
{
 if (!isSessionOpen)
  return null;
 if (sampIdx > numSamples)
  //往回從第一個樣本重新開始
  sampIdx = 1;

  try {
   /*取得從指定的索引時(shí)間開始的樣本*/
   TimeInfo ti = vidMedia.sampleNumToMediaTime(sampIdx);
   mediaSample=vidMedia.getSample(0,ti.time,1);
   sampIdx++;

  writeToBufferedImage(mediaSample, img);

  //調(diào)整img的大小,把它寫入formatImg
  Graphics g = formatImg.getGraphics();
  g.drawImage(img,0,0,FORMAT_SIZE,FORMAT_SIZE,null);

  //覆蓋掉圖像上的當(dāng)前時(shí)間
  g.setColor(Color.RED);
  g.setFont(new Font("Helvetica", Font.BOLD, 12));
  g.drawString(timeNow(), 5, 14);
  g.dispose();
  numFramesMade++; //幀計(jì)數(shù)
 }
 catch (Exception e) {
  System.out.println(e);
  formatImg = null;
 }
 return formatImg;
} //getFrame()方法結(jié)束BufferedImage

  通過調(diào)用QTJ媒體類中的getSample()方法可以很容易地得到樣本。不幸的是,把樣本轉(zhuǎn)化成BufferedImage仍然存在一定的困難--我把這些都藏在了我的writeToBufferedImage()方法的實(shí)現(xiàn)之中了。

  這個方法的實(shí)現(xiàn)使用一些奇特的辦法,這是從Chris W.Johnson的MovieFrameExtractor.java例子(你可以在quicktime-java 郵件列表中找到)中遷移過來的。

  比較奇特的實(shí)現(xiàn)思路和細(xì)節(jié)!但都加上了良好的注釋,你可以詳細(xì)地研究這些代碼。一個"原始的"圖像從樣本中提取出來,然后當(dāng)被寫入一個QuickTime 版本的Graphics 對象時(shí)解壓。Graphics對象中未壓縮的數(shù)據(jù)被復(fù)制到另一個"原始"圖像中去,然后又被復(fù)制到一個像素?cái)?shù)組(一個PixMap)中去。最后,該數(shù)組被寫進(jìn)一個空的BufferedImage的DataBuffer部分。

  應(yīng)用程序能運(yùn)行嗎?是否運(yùn)行良好?

  的確,Movie3D可以播放動畫,但是大型的動畫播放起來速度很慢。這是由于getFrame()方法提供幀的速度太慢了,此可以通過觀察FPS來定量地計(jì)算出。

  對于圖1中的動畫,程序在一臺較慢的Windows 98機(jī)器上報(bào)出的FPS范圍在每秒15到17幀。然而,TimeBehavior對象要求每40毫秒作一次更新,這轉(zhuǎn)換成幀速的話,看上去大約接近25 FPS。

  由于耗時(shí)的樣本到BufferedImage的轉(zhuǎn)換,getFrame()方法運(yùn)行緩慢。由于當(dāng)前對getFrame()的調(diào)用在作幀轉(zhuǎn)化時(shí)陷于困境,所以下一步的請求因等待當(dāng)前請求的完成而被耽誤。

  下面,我將分析克服這一問題的兩種方法:其一是,當(dāng)最終開始處理某一請求時(shí),允許getFrame()進(jìn)行幀跳躍處理;其二是,在getFrame()中嘗試用一種不同的轉(zhuǎn)換策略。下面我將輪流分析這兩種方法,先看幀跳躍處理。

  3. 幀跳躍處理的動畫

  新的Snapper類,QTSnapper1,在調(diào)用方法getFrame()時(shí)仍然返回一幀。該類與QTSnapper的不同在于,它相應(yīng)于動畫的當(dāng)前運(yùn)行時(shí)間而提供一幀。

  例如,getFrame()方法可能檢索第1,2,5,8,12幀,等等,具體依賴于什么時(shí)候調(diào)用該方法。這樣以來,動畫以良好的速度播放,但是漏掉的幀可能使得動畫出現(xiàn)顫動現(xiàn)象。

  相比較之下,QTSnapper將返回所有的幀(1,2,3,4,等等),但是在getFrame()方法調(diào)用之間的延誤將使得動畫運(yùn)行緩慢。只是由于沒有砍掉幀,所以不會出現(xiàn)一些顫動現(xiàn)象。

  QTSnapper1中的一個關(guān)鍵元素是動畫的"當(dāng)前運(yùn)行時(shí)間"的概念。我采用的解決途徑是,當(dāng)調(diào)用getFrame()方法時(shí)為QTSnapper對象計(jì)算當(dāng)前運(yùn)行時(shí)間,并把它轉(zhuǎn)換成一個動畫時(shí)間,最后轉(zhuǎn)換成一個樣本索引值。

  QTSnapper1與QTSnapper一樣有相同的公共方法,所以只要最少的修改即可應(yīng)用在QTMovieScreen中。只有在動畫播放時(shí)它們的區(qū)別才變得明顯起來。在后面將詳述的定量計(jì)算表明,現(xiàn)在的QTSnapper1方法顯示圖1中"表面上"的幀速率達(dá)21 FPS,而QTSnapper方法只能達(dá)到幀速率16 FPS。

  存取動畫的視頻媒體

  QTSnapper1遵循了與QTSnapper相同的步驟來實(shí)現(xiàn)動畫視頻的存取。一旦視頻可用,好幾個媒體值作為全局變量存儲,以備后面getFrame()方法所用:

// 全局變量
private Media vidMedia;
private int numSamples;
private int timeScale; //媒體的時(shí)間刻度
private int duration; //媒體的持續(xù)時(shí)間

//在構(gòu)造器中,取得由視頻軌道使用的媒體
vidMedia = videoTrack.getMedia();
//存儲視頻細(xì)節(jié)以備后用
numSamples = vidMedia.getSampleCount();
timeScale = vidMedia.getTimeScale();
duration = vidMedia.getDuration();

  獲取幀

  getFrame()方法中的新的編碼部分在于如何計(jì)算用來存取一個特別樣本的索引值;而該方法中的其余部分,對于writeToBufferedImage()的調(diào)用,以及把當(dāng)前時(shí)間寫到圖像上等等,都和QTSnapper中一樣。

// 全局變量
private MediaSample mediaSample;
private BufferedImage img, formatImg;
private int prevSampNum;
private int sampNum = 0;
private int numCycles = 0;
private int numSkips = 0;
// 在getFrame()函數(shù)中,
//以秒為單位取得從QTSnapper1開始以來的時(shí)間
double currTime =((double)(System.currentTimeMillis()-startTime))/1000.0;
// 使用視頻的時(shí)間刻度
int videoCurrTime=((int)(currTime*timeScale)) % duration;

try {
 // 備份前一個樣本號
 prevSampNum = sampNum;
 //計(jì)算新的樣本號
 sampNum = vidMedia.timeToSampleNum(videoCurrTime).sampleNum;
 //如果沒有樣本變化,則不生成一幅新的圖像
 if (sampNum == prevSampNum)
  return formatImg;
 if (sampNum < prevSampNum)
  numCycles++; //動畫剛剛開始播放

 // 記下跳過的幀號
 int skipSize = sampNum - (prevSampNum+1);
 if (skipSize > 0) //跳過的幀
  numSkips += skipSize;

 //取得從樣本號的時(shí)間開始的一個樣本
 TimeInfo ti =vidMedia.sampleNumToMediaTime(sampNum);
 mediaSample = vidMedia.getSample(0,ti.time,1);
 getFrame()以秒為單位計(jì)算出當(dāng)前的時(shí)間,從QTSnapper1的啟動開始計(jì)算:
 double currTime =((double)(System.currentTimeMillis()-startTime))/1000.0;

  每一段QuickTime媒體都有它自己的時(shí)間刻度-ts,這樣,一個時(shí)間單位就是1/ts秒。用時(shí)間刻度常數(shù)乘以currTime的值即得到在動畫時(shí)間單位中的當(dāng)前時(shí)間值:

int videoCurrTime=((int)(currTime*timeScale)) % duration;

  該時(shí)間值被用媒體持續(xù)時(shí)間為模作模運(yùn)算加以修正,允許動畫在當(dāng)前時(shí)間到達(dá)動畫末尾時(shí)重復(fù)播放。

  這個時(shí)間值被通過調(diào)用Media的timeToSampleNum()方法映射到一個樣本索引號:

sampNum=vidMedia.timeToSampleNum(videoCurrTime).sampleNum;

  前面使用的樣本號存儲在變量prevSampNum中,這就允許進(jìn)行一些測試和計(jì)算。

  如果"新的"樣本號與前一個相同,那么就沒有必要費(fèi)勁地把這個樣本轉(zhuǎn)化成一個BufferedImage;getFrame()方法能夠返回存在的formatImg參照。

  如果新的樣本號小于前一個相同,這意味著動畫已經(jīng)循環(huán)播放了,動畫最開始的一幀將被播出。該循環(huán)通過增加變量numCycles的值來登記。

  如果新的樣本號大于前一個幀號加1,那就記下跳過的樣本號。

  收尾處理

  stopMovie()打印出FPS值并關(guān)閉QuickTime會話,其實(shí)現(xiàn)方式同QTSnapper中的stopMovie()方法很相近。該函數(shù)還給出一些額外信息:

long totalFrames =(numCycles * numSamples) + sampNum;
//報(bào)告跳過幀的百分比數(shù)
double skipPerCent=(double)(numSkips*100)/totalFrames;
System.out.println("Percentage frames skipped: "+
frameDf.format(skipPerCent) + "%");

//"表面上的"FPS (AFPS)
double appFrameRate =
((double) totalFrames * 1000.0) / duration;
System.out.println("AFPS: "+frameDf.format(appFrameRate));//1dp

  appFrameRate代表了"表面上的"幀速率,該值是從QTSnapper1的創(chuàng)建開始算起,直到迭代結(jié)束得到的總幀數(shù)。所謂"表面上的",其意指并非所有的樣本必須被著色到屏幕上。

  應(yīng)用程序能運(yùn)行嗎?是否運(yùn)行良好?

  以QTSnapper1取代QTSnapper,原來慢速的動畫(如圖1所示)現(xiàn)在的播放加快了。在播放快結(jié)束時(shí),程序報(bào)出的表面上的幀速率數(shù)字達(dá)到31 FPS,實(shí)際的幀速率大約在16 FPS,跳過的幀數(shù)將近達(dá)到總幀數(shù)的50%。驚人的是,這么巨大數(shù)目的幀丟失在屏幕上的影響看上去并不明顯。

  對于另外一些小型動畫來說,速度的提升并不那么引人注目;跳過的幀數(shù)所占百分比大約在5-10%。

  不幸的是,存在兩個問題:當(dāng)跳躍幀時(shí)可能出現(xiàn)混雜的像素;對可以跳過的幀號缺乏有效的控制。

  混雜的像素

  這種效果如圖6所示,不準(zhǔn)確的像素使用了來自于視頻中前一階段的值。任何時(shí)候當(dāng)QTSnapper1跳過一個動畫幀時(shí),下一幅被檢索到的幀將會包含一些混雜的像素。其效果可以在圖6中看到。這些不準(zhǔn)確的像素使用了來自于視頻中前一階段的值。


圖 6. 部分混雜的圖像

  借助于來自quicktime-java 郵件列表的人們(特別感謝George Birbilis和Dean Perry)的幫助,我找到了一個解決辦法。

  該問題是,我原先所有的動畫樣本都使用了temporal compression壓縮算法,該壓縮算法主要是利用了相鄰視頻幀的相似性。如果兩個連續(xù)的幀有相同的背景,那么就沒有必要再次存儲背景了,而是僅存起這兩幀的不同處即可。

  這項(xiàng)技術(shù),幾乎為所有流行的視頻格式所應(yīng)用,意味著從幀中提取的一幅圖像將依賴于該幀,也潛在地依賴于前面幾個幀。

  Temporal decompression解壓算法在一個QuickTime DSequence對象中完成,該對象又為我自己的writeToBufferedImage()方法中所用。在Dsequence的構(gòu)造器指定,在解壓過程中QuickTime應(yīng)該使用脫屏圖像緩沖區(qū)進(jìn)行操作。

  幀中的圖像被寫向緩沖區(qū),在此其又與早期的幀數(shù)據(jù)相結(jié)合。結(jié)果圖像被傳遞到轉(zhuǎn)換過程的下一步。

  這種方式在QTSnapper1以順序方式(不進(jìn)行幀跳過,例如:1,2,3,4)解壓樣本時(shí)運(yùn)行良好,但是在有跳幀的情況下報(bào)出錯誤。例如,當(dāng)QTSnapper1跳過第5、6幀,然后解壓第7幀,情況會怎樣呢?該幀被寫向一個QuickTime 圖像緩沖區(qū),然后又與早期的幀數(shù)據(jù)相結(jié)合。不幸的是,第5、6幀中的數(shù)據(jù)丟失了,因此結(jié)果圖像是不正確的。

  簡單地說,圖像中進(jìn)來的混雜像素是由于動畫中使用了temporal compression壓縮算法的結(jié)果。一種選擇是使用spatial compression壓縮算法,這種算法獨(dú)立地壓縮每一幀數(shù)據(jù),就象對單一圖像進(jìn)行JPEG或者GIF壓縮的方式很相似。這種算法意味著,用于解壓一幀的所有信息均來自于該幀本身;沒有必要對前面的幀進(jìn)行檢查。

  QuickTime MOV動畫格式支持一種稱作Motion-JPEG (M-JPEG)的a spatial compression壓縮方式。我使用 QuickTime 6 Pro中的輸出工具,選取"M-JPEG A codec"編碼方式后,把圖1中的樣本保存成一種MOV文件。當(dāng)Movie3D應(yīng)用程序播放該動畫時(shí),沒有混雜現(xiàn)象發(fā)生。
限制幀跳躍

  關(guān)于QTSnapper1的另外一個問題是,getFrame()方法并不限制能夠跳過的幀數(shù)。在我的測試過程中,跳過幀數(shù)的上限是3時(shí),其效果并不顯著。但是,如果getFrame()方法中使用一個較大的樣本(例如,較大的尺寸和分辨率)加以轉(zhuǎn)換,那么,代碼運(yùn)行速度的明顯變慢將會由更多的幀跳越數(shù)所補(bǔ)償,只是動畫質(zhì)量非常明顯地變差。

  4. 盡量加快圖像的生成

  上面在QTSnapper和QTSnapper1中使用的sample-to-BufferedImage轉(zhuǎn)換方法(writeToBufferedImage())來源于Chris W. Johnson的一個例程。是否還存在更快的從樣本中提取圖像的方法?

  關(guān)于QTJ的標(biāo)準(zhǔn)參考書是《QuickTime for Java: A Developer’s Notebook》(作者Chris Adamson,O’Reilly,出版時(shí)間2005.1)。本書的第五章介紹了QuickDraw內(nèi)容,其中包含一個例子ConvertToJavaImageBetter.java,該例說明了怎樣把一個樣本抓取成一個PICT圖像,然后把它轉(zhuǎn)化成一個Java Image對象。你也可以在quicktime-java郵件列表處發(fā)現(xiàn)這個例子。

  其中的轉(zhuǎn)化方法并不直截易懂,這依賴于加在PICT對象前面的一個虛構(gòu)的512字節(jié)的頭部,這樣該對象就可以被QuickTime 版本的ImageProducer當(dāng)作一個PICT文件對待。

  我借用了Adamson的代碼作為我的另一個稱作QTSnapper2的Snapper類的基本代碼組成。該類負(fù)責(zé)一個沒有幀跳躍的幀系列的著色,其工作方式同QTSnapper,但是使用了PICT-to-Image轉(zhuǎn)換方法。

  對于一些小的動畫,QTSnapper2的性能與QTSnapper相差無幾,但是對于一些稍微大些的如圖1中的動畫,與QTSnapper的16 FPS幀速率相比其平均幀速率下降到大約9 FPS。也就是說,基于PICT的轉(zhuǎn)換技術(shù)慢于Johnson所用的技術(shù)。

 


 

萬企互聯(lián)
標(biāo)簽: