早期 J2ME 手機(MIDP 1.0/2.0)原生不支援 GIF 動畫,僅支援靜態 PNG。將動畫拆解為多張 PNG 會劇增 JAR 體積。本類別實現純 Java GIF 解碼,直接讀取 .gif 檔案逐幀渲染,大幅節省資源空間。
private GIFDecode gifDecoder;
private int frameIndex = 0;
private int totalFrames;
private Image currentFrame;
void initGIF() {
gifDecoder = new GIFDecode();
gifDecoder.read(getClass().getResourceAsStream("/animation.gif"));
totalFrames = gifDecoder.getFrameCount();
System.out.println("Total frames: " + totalFrames);
}
void renderFrame(Graphics g, int x, int y, int anchor) {
currentFrame = gifDecoder.getFrame(frameIndex);
frameIndex = (frameIndex + 1) % totalFrames; // 循環播放
g.drawImage(currentFrame, x, y, anchor);
}
完整實現了 GIF89a 規範核心子集:
prefix/suffix/pixelStack),處理 clear code、end of information、動態碼長增長。Graphic Control Extension 獲取 delay time、disposal method(無動作/保留/還原背景/還原前幀)、transparency。act(active color table)。static class GifFrame {
public Image image; // 解碼後的 MIDP Image
public int delay; // 顯示延遲(毫秒)
}
Vector frames; // 所有幀緩存
byte[] pixels; // LZW 解碼後的像素索引流
short[] prefix; byte[] suffix; byte[] pixelStack; // LZW 解碼工作陣列
read(InputStream) → readHeader() 驗證 GIF 簽名 → readLSD() 讀取邏輯螢幕描述符(寬高、全域色表旗標)。readContents() 循環解析區塊:0x2C 影像描述符 → readImage() → decodeImageData() (LZW) → setPixels() 合成幀 → 存入 frames。setPixels() 依 disposal method 處理前幀殘留(保留/清背景/還原前前幀),再將當前像素按交織序寫入目標 RGB 陣列,最後 Image.createRGBImage() 生成 MIDP Image。| 指標 | 典型值 | 優化建議 |
|---|---|---|
| 解碼速度 | ~50-200ms/幀 (視手機 CPU) | 預解碼緩存所有幀至 Vector,播放時直接取用 |
| 記憶體佔用 | 幀數 × (寬×高×2 位元組) | 大動畫考慮邊解碼邊播放,或降低幀率/解析度 |
| JAR 體積增加 | ~8-12 KB (編譯後) | 遠小於等量 PNG 序列 |
| 方案 | 優點 | 缺點 |
|---|---|---|
| 本解碼器 | 單檔、無依賴、支援循環/透明/延遲 | 純軟解碼較慢、佔用 Heap |
| PNG 序列 | 原生繪製最快 | JAR 體積大、管理麻煩 |
| MIDP 3.0 / JavaFX Mobile | 原生 GIF 支援 | 普及率極低(歷史環境不適用) |