在 J2ME 中實作影像縮放,核心挑戰在於:MIDP 1.0 無標準像素存取 API、目標座標非整數需插值、透明背景處理受限。本文以 Nokia S40 平台為例,示範利用 Nokia UI API 實作雙向線性插值表的最近鄰縮放演算法。
// 讀取像素到 short[] 陣列 (444 格式:R/G/B 各 4 bits)
DirectGraphics dg = DirectUtils.getDirectGraphics(image.getGraphics());
dg.getPixels(pixels, 0, width, 0, 0, width, height, 0x444);
// 從 short[] 建立可變影像
Image img = Image.createImage(w, h);
DirectGraphics dg2 = DirectUtils.getDirectGraphics(img.getGraphics());
dg2.drawPixels(pixels, true, 0, w, 0, 0, w, h, 0, 0x444);
注意:getPixels 僅支援可變影像;不可變影像需先繪製到可變影像再讀取。
傳統逐像素計算源座標效率低。預先建立水平/垂直查找表 tabX[], tabY[],將目標索引映射至源索引:
// 垂直方向插值表
int distance = Math.max(srcH, desH);
for (int i = 0, sb = 0, db = 0, tems = 0, temd = 0; i <= distance; i++) {
tabY[db] = (short) sb;
tems += srcH; temd += desH;
if (tems > distance) { tems -= distance; sb++; }
if (temd > distance) { temd -= distance; db++; }
}
// 水平方向同理建立 tabX[]
此演算法等同於「以最大邊長為基準,按比例分配源像素到目標像素」,屬於整數版 Bresenham 風格分配,無浮點運算。
short[] desBuf = new short[desW * desH];
int dy = 0, sy = 0, oldy = -1;
for (int i = 0; i < desH; i++) {
if (oldy == tabY[i]) {
// 該行與前一行源列相同 → 直接複製前一行像素
System.arraycopy(desBuf, dy - desW, desBuf, dy, desW);
} else {
// 依水平插值表逐像素取樣
for (int j = 0, dx = 0; j < desW; j++, dx++) {
desBuf[dy + dx] = srcBuf[sy + tabX[j]];
}
sy += (tabY[i] - oldy) * srcW; // 更新源列偏移
}
oldy = tabY[i];
dy += desW;
}
行複製優化大幅減少重複取樣,特別適合放大操作。
private Image ZoomImage(Image srcImg, int desW, int desH) {
int srcW = srcImg.getWidth(), srcH = srcImg.getHeight();
short[] srcBuf = new short[srcW * srcH];
Image temp = Image.createImage(srcW, srcH);
// 統一處理可變/不可變影像
if (srcImg.isMutable()) {
DirectUtils.getDirectGraphics(srcImg.getGraphics())
.getPixels(srcBuf, 0, srcW, 0, 0, srcW, srcH, 0x444);
} else {
temp.getGraphics().drawImage(srcImg, 0, 0, 0);
DirectUtils.getDirectGraphics(temp.getGraphics())
.getPixels(srcBuf, 0, srcW, 0, 0, srcW, srcH, 0x444);
}
// 建立插值表 tabX, tabY (略)
// 生成 desBuf (略)
// 輸出結果
Image desImg = Image.createImage(desW, desH);
DirectUtils.getDirectGraphics(desImg.getGraphics())
.drawPixels(desBuf, true, 0, desW, 0, 0, desW, desH, 0, 0x444);
return desImg;
}
Image.createImage(w,h) 預設白底。對策:全程用 short[] 保存像素,繪製時用 drawPixels,不建立中間 Image。此實作在 Nokia 6230/6600 等 S40 裝置實測流暢,為 J2ME 影像處理的經典參考。