在資源受限的行動裝置上實作任意角度影像旋轉,需兼顧數學正確性、記憶體效能與執行速度。本文從 2D 旋轉數學推導、MIDP 2.0 API 實作、失真問題解決、CLDC 1.0 相容性到替代方案完整說明。
點 P(x,y) 繞原點逆時針旋轉 θ 到 P'(x',y'):
x' = x·cosθ - y·sinθ
y' = x·sinθ + y·cosθ
Q(x0,y0) 旋轉x' = x0 + (x-x0)·cosθ - (y-y0)·sinθ
y' = y0 + (y-y0)·cosθ + (x-x0)·sinθ
螢幕左上為原點,Y 軸向下。視 θ 為順時針角度,公式不變。
getRGB()。dr = max(距離四角),建立外切正方形畫布 (2·dr+1)²。(destX, destY)。destMap[destY·w + destX] = srcMap[y·w + x]。createRGBImage(),回傳位置偏移量供繪製對齊。/**
* @param imgSource 來源影像
* @param cx 旋轉中心相對來源左上 X
* @param cy 旋轉中心相對來源左上 Y
* @param theta 逆時針旋轉角度(度)
* @param offset 輸出:新影像相對來源的位移 {dx, dy}
* @return 旋轉後影像
*/
public Image rotate(Image imgSource, int cx, int cy, double theta, int[] offset) {
if (Math.abs(theta % 360) < 0.1) return imgSource; // 角度極小直接返回
int w = imgSource.getWidth();
int h = imgSource.getHeight();
int[] src = new int[w * h];
imgSource.getRGB(src, 0, w, 0, 0, w, h);
// 計算旋轉半徑(到四角最大距離)
int dx = Math.max(cx, w - cx);
int dy = Math.max(cy, h - cy);
double dr = Math.sqrt(dx*dx + dy*dy);
int size = (int)(2 * dr) + 1; // 正方形邊長 +1 防越界
int[] dest = new int[size * size];
double rad = Math.toRadians(theta);
double cos = Math.cos(rad), sin = Math.sin(rad);
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int pixel = src[y * w + x];
if ((pixel >> 24) != 0) { // 忽略全透明像素
double destX = dr + (x - cx) * cos - (y - cy) * sin;
double destY = dr + (y - cy) * cos + (x - cx) * sin;
int ix = (int) destX;
int iy = (int) destY;
if (ix >= 0 && ix < size && iy >= 0 && iy < size) {
dest[iy * size + ix] = pixel;
}
}
}
}
offset[0] = cx - (int)dr; // 繪製位移補償
offset[1] = cy - (int)dr;
return Image.createRGBImage(dest, size, size, true);
}
前向映射(來源→目標)導致多個來源像素映射到同一目標座標(覆蓋),或目標座標無來源像素(空洞)。
| 方法 | 原理 | 成本 |
|---|---|---|
| 超採樣 | 來源放大 N 倍旋轉再縮小 | 記憶體/運算 ×N² |
| 反向映射 | 遍歷目標座標,反推來源座標雙線性插值 | 品質最佳,運算較重 |
| 馬賽克/雙線性補償 | 邊界像素平均/插值 | 低成本,品質中等 |
for (int dy = 0; dy < size; dy++) {
for (int dx = 0; dx < size; dx++) {
// 目標→來源反向變換
double sx = cx + (dx - dr) * cos + (dy - dr) * sin;
double sy = cy - (dx - dr) * sin + (dy - dr) * cos;
// 雙線性插值取樣...
dest[dy * size + dx] = bilinearSample(src, w, h, sx, sy);
}
}
sin[360]、cos[360](定點數)。Sprite 內建 90° 倍數旋轉/鏡射。collidesWith 配合像素遮罩)。| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 即時運算旋轉 | 任意角度、動態中心、無預存成本 | CPU 密集、失真處理複雜 | 指針、旋轉物件、動態角度 |
| 預存多角度幀 | 極快、無運算、品質可控 | 儲存空間大、角度離散 | 角色轉向、固定角度集合 |
| TinyLine 2D 引擎 | 向量圖形、旋轉縮放原生支援、<35KB | 學習曲線、光柵影像支援有限 | 複雜 2D 繪圖、遊戲 UI |