圖 1. BufferedImage 子圖形
這個圖像是一個以 spriteSize 為邊長的正方形。圖像其它部分的尺寸值都與這個邊長相關。實際上這裡只有兩個幾何實體,一條線和一個圓,都在不同位置和方向重復出現。如果我們創建一個 Line2D.Double 對象代表線,創建一個 Ellipse2D.Double 對象代表圓,那麼我們就可以通過移動用戶坐標系和畫這兩個對象中的一個或其它的對象而畫出整個圖像。
如果是按真正面向對象的方法,應該定義一個類代表一個子圖形,可能是作為 BufferedImage 的一個子類,但由於我們是在探索使用 BufferedImage 對象的技巧,因此用一個 createSprite() 方法來畫出 BufferedImage 對象上的子圖形會更適合我們的目的。因為該方法只是我們的 applet 類的一個成員,所以我們將為 applet 添加數據成員以存儲任何需要的數據。您可以把我們將使用的數據成員插入到 applet 類中,如下所示:
double totalAngle; // Current angular position of sprite
double spriteAngle; // Rotation angle of sprite about its center
ImagePanel imagePanel; // Panel to display animation
BufferedImage sprite; // Stores reference to the sprite
int spriteSize = 100; // Diameter of the sprite
Ellipse2D.Double circle; // A circle - part of the sprite
Line2D.Double line; // A line - part of the sprite
// Colors used in sprite
Color[] colors = {Color.red , Color.yellow, Color.green , Color.blue,
Color.cyan, Color.pink , Color.magenta, Color.orange};
java.util.Timer timer; // Timer for the animation
long interval = 50; // Time interval msec between repaints
這些成員的一般用途可以從注釋中清楚地看到。下面我們要看一看開發代碼時它們是怎樣被使用的。
createSprite() 方法需要做的第一件事就是創建 BufferedImage 對象 sprite,然後我們還需要一個 Graphics2D 對象用於在 sprite 圖像上繪畫。下面就是完成這些操作的代碼:
BufferedImage createSprite(int spriteSize)
{
// Create image with RGB and alpha channel
BufferedImage sprite = new BufferedImage(spriteSize, spriteSize,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2D = sprite.createGraphics(); // Context for buffered image
// plus the rest of the method...
}
sprite 對象的寬和高的值都是 spriteSize,圖像的類型為 TYPE_INT_ARGB,就是說每個像素的 alpha 值和顏色組件是以一個單獨的 int 值存儲的,而顏色是以 8 位的紅、綠、藍組件的形式存儲的。這意味著我們的 sprite 圖像將占用 40,000 字節,這只是浏覽一個網頁會占用的內存的很小一部分。而這並不影響網頁的下載時間,因為在執行 applet 的時候,這部分內存是在本地機器上被分配的。除了作為網頁本身的 HTML 文件的內容外,下載時間還取決於 applet 的 .class 文件的大小,以及在它執行時下載的圖像或其它文件。
創建一個透明的背景
在 sprite 圖像中,alpha 通道是很重要的,因為我們希望背景能完全透明。在繪畫過程中,只有 sprite 對象本身應該是可見的,而不是整個 100×100 的矩形圖像。我們可以很容易地實現這一目的,只要開始先使整個 sprite 圖像區域透明(即,alpha 值為 0.0f),然後把我們想要畫的圖形繪制在上面,使之不透明(alpha 值為 1.0f)。以下是使整個圖像透明的代碼:
// Clear image with transparent alpha by drawing a rectangle
g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
Rectangle2D.Double rect = new Rectangle2D.Double(0,0,spriteSize,spriteSize);
g2D.fill(rect);
我們首先使用 AlphaComposite 對象按照 CLEAR 規則設置 alpha 合成值,把顏色組件設置為零,又通過設置 alpha 值為 0.0f,使之透明。然後我們填充一個覆蓋整個圖像區域的矩形。我們不必設置顏色值,因為根據 CLEAR 規則,每個像素的前景和背景色所占成分都是零,所以這兩者都不參與像素的生成。但我們仍要填充該矩形,因為這將確定被操作的圖像像素。
這裡,我們可以稍微了解一下怎樣控制圖像的質量。
著色微調
對著色操作的許多方面而言,都有一個在質量和速度間選擇的問題。著色操作就像大多數事情一樣 — 質量是需要代價的,而這裡的代價就是處理時間。所有的著色操作都有缺省設置,其中存在一個選擇,缺省設置是特定於平台的,但您可以通過調用用於著色的 Graphics2D 對象的 setRenderingHint() 方法自己選擇。雖然只有一些微調,如果您的計算機不支持與您指定的微調相對應的著色操作選項,這些微調就無法生效。
通過添加以下對 createSprite() 方法的調用,可以確保得到由我們的 alpha 合成操作可能生成的最好效果。
BufferedImage createSprite(int spriteSize)
{
// Create image with RGB and alpha channel
BufferedImage sprite = new BufferedImage(spriteSize, spriteSize, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2D = sprite.createGraphics(); // Context for buffered image
// Set best alpha interpolation quality
g2D.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
// Clear image with transparent alpha by drawing a rectangle
g2D.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
Rectangle2D.Double rect = new Rectangle2D.Double(0,0,spriteSize,spriteSize);
g2D.fill(rect);
// plus the rest of the method...
}
RenderingHints 類定義了多種著色微調,它們存儲在一個映射集的 Graphics2D 對象裡。 setRenderingHint() 方法的參數是一個鍵以及對應的鍵值。在我們的代碼中,第一個參數是代表 alpha 合成微調的鍵,第二個參數是該微調的值。該微調的其它可能的值有 VALUE_ALPHA_INTERPOLATION_DEFAULT,代表平台缺省值;以及 VALUE_ALPHA_INTERPOLATION_SPEED,代表追求速度而不是質量。
您還可以為下面的鍵提供微調:
鍵 描述
KEY_ANTIALIASING決定是否使用抗鋸齒。當著色有傾斜角度的線時,通常會得到一組階梯式的像素排列,使這條線看上去不平滑,經常被稱為 鋸齒狀圖形。抗鋸齒是一種技術,它設置有傾斜角度的線的像素亮度,以使線看起來更平滑。因此,這個微調是用來決定在著色有傾斜角度的線時是否在減少鋸齒狀圖形上花費時間。可能的值有 VALUE_ANTIALIAS_ON, _OFF 或 _DEFAULT。
KEY_COLOR_RENDERING控制顏色著色的方式。可能的值有 VALUE_COLOR_RENDER_SPEED, _QUALITY 或 _DEFAULT。
KEY_DITHERING控制如何處理抖動。抖動是用一組有限的顏色合成出一個更大范圍的顏色的過程,方法是給相鄰像素著色以產生不在該組顏色中的新的顏色幻覺。可能的值有 VALUE_DITHER_ENABLE, _DISABLE 或 _DEFAULT。
KEY_FRACTIONALMETRICS文本的質量。可能的值有 VALUE_FRACTIONALMETRICS_ON, _OFF 或 _DEFAULT。
KEY_INTERPOLATION確定怎樣做內插。
在對一個源圖像做變形時,變形後的像素很少能夠恰好對應目標像素位置。在這種情況下,每個變形後的像素的顏色值不得不由周圍的像素決定。
內插就是實現上述過程。有許多可用的技術。可能的值,按處理時間從最多到最少,是 VALUE_INTERPOLATION_BICUBIC, _BILINEAR 或 _NEAREST_NEIGHBOR。
KEY_RENDERING 確定著色技術,在速度和質量之間進行權衡。可能的值有 VALUE_RENDERING_SPEED, _QUALITY 或 _DEFAULT。
KEY_TEXT_ANTIALIASING 確定對文本著色時是否抗鋸齒。可能的值有 VALUE_TEXT_ANTIALIASING_ON, _OFF 或 _DEFAULT。
我們繞得已經夠遠了。讓我們回到繪制 sprite 上……