SWT(Standard Widget Toolkit)提供 Image、ImageData、ImageLoader、GC(Graphics Context)等核心類,支援像素級操作、Alpha 通道合成、多幀動畫解碼。本文透過兩個完整可執行範例,示範靜態 Alpha 漸變遮罩與GIF 多幀動畫播放的實作細節。
ImageData,建立等大小的 byte[] alphaValues。255 - 255 * x / width,左邊不透明、右邊全透明。alphaValues 寫入 imageData.alphaData,建立帶 Alpha 的 Image。Canvas + PaintListener 繪製,事件迴圈保持視窗開啟。package swt_jface.demo10;
import org.eclipse.swt.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.*;
public class AlphaFadeIn {
Display display = new Display();
Shell shell = new Shell(display);
public AlphaFadeIn() {
shell.setLayout(new FillLayout());
final Canvas canvas = new Canvas(shell, SWT.NONE);
// 1. 載入圖片並建立 Alpha 遮罩
ImageData imageData = new ImageData("C:/icons/eclipse.jpg");
byte[] alphaValues = new byte[imageData.height * imageData.width];
for (int y = 0; y < imageData.height; y++) {
for (int x = 0; x < imageData.width; x++) {
alphaValues[y * imageData.width + x] = (byte) (255 - 255 * x / imageData.width);
}
}
imageData.alphaData = alphaValues;
final Image image = new Image(display, imageData);
// 2. 繪製監聽器
canvas.addPaintListener(e -> e.gc.drawImage(image, 10, 10));
shell.setSize(400, 300);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
public static void main(String[] args) { new AlphaFadeIn(); }
}
ImageData.alphaData 長度必須等於 width * height,否則拋 SWTError。Class.getResourceAsStream 載入資源檔。ImageData[],每幀含 delayTime(單位:百分之一秒)、x/y(幀偏移)、disposalMethod。Image(畫布大小),GC 繪製每幀到該 Image。display.asyncExec 切換到 UI 執行緒繪製幀 → canvas.redraw() 觸發重繪 → Thread.sleep(delayTime * 10)。ShellListener 中斷執行緒,釋放資源。package swt_jface.demo10;
import org.eclipse.swt.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.*;
public class Animations {
Display display = new Display();
Shell shell = new Shell(display);
public Animations() {
shell.setLayout(new FillLayout());
// 1. 載入 GIF 所有幀
ImageLoader loader = new ImageLoader();
final ImageData[] frames = loader.load("C:/icons/eclipse-ani.gif");
final Image canvasImage = new Image(display, frames[0].width, frames[0].height);
final GC gc = new GC(canvasImage);
final Canvas canvas = new Canvas(shell, SWT.NONE);
canvas.addPaintListener(e -> e.gc.drawImage(canvasImage, 0, 0));
// 2. 動畫執行緒
final Thread animThread = new Thread(() -> {
int idx = 0;
while (!Thread.currentThread().isInterrupted()) {
idx %= frames.length;
final ImageData frame = frames[idx];
display.asyncExec(() -> {
if (canvas.isDisposed()) return;
Image frameImg = new Image(display, frame);
gc.drawImage(frameImg, frame.x, frame.y);
frameImg.dispose();
canvas.redraw();
});
try { Thread.sleep(frame.delayTime * 10); }
catch (InterruptedException e) { return; }
idx++;
}
});
// 3. 視窗關閉清理
shell.addShellListener(new ShellAdapter() {
@Override public void shellClosed(ShellEvent e) { animThread.interrupt(); }
});
shell.setSize(400, 300);
shell.open();
animThread.start();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
gc.dispose();
canvasImage.dispose();
display.dispose();
}
public static void main(String[] args) { new Animations(); }
}
GIF 幀之間可能有「不處理、恢復背景、恢復上一幀」三種 disposal 方式。上例為簡化未處理,生產級播放器需根據 frame.disposalMethod 在繪製前清除畫布或還原前一幀,否則會出現殘影。
| 資源 | 建立處 | 釋放處 | 備註 |
|---|---|---|---|
| Display | main 開頭 | main 結尾 dispose() | 進程僅需一個 |
| Image (靜態) | 載入後 | 視窗關閉前 | 未釋放會洩漏原生記憶體 |
| Image (動畫幀) | asyncExec 內 | 繪製後立即 dispose() | 短命物件,必須即時釋放 |
| GC | 建立目標 Image 後 | 程式結束前 | 綁定單一 Image |
| Thread | 啟動動畫前 | interrupt() + join() | 避免視窗關閉後執行緒殘留 |
SWT 底層直通作業系統圖形子系統,雖 API 相對底層,但靈活度極高。掌握 ImageData 像素操作與 ImageLoader 多幀解碼,配合 asyncExec 跨執行緒 UI 更新,即可實現從簡單遮罩到複雜動畫的各類視覺特效。建議在真實專案中封裝 AnimatedImage、AlphaMaskFilter 等可復用元件,提升開發效率。