首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> J2ME

J2ME/MIDP 圖像任意角度旋轉算法完整實現:數學原理、定點數移植、Sprite 整合與替代方案

     編輯:J2ME

MIDP 2.0 無內建任意角度旋轉 API,僅 Sprite 支援 90° 倍數。本文從 2D 旋轉數學推導、掃描線填充算法、CLDC 1.0 定點數移植 (MathFP)、預計算三角函數表、Sprite 整合、預置圖片/TinyLine 2D 替代方案,給出完整工程級解決方案。

1. 數學基礎:繞任意點旋轉

點 P(x,y) 繞 Q(x0,y0) 順時針旋轉 θ:


x' = x0 + (x-x0)cosθ - (y-y0)sinθ
y' = y0 + (y-y0)cosθ + (x-x0)sinθ

設備座標系:原點左上,Y 向下,θ 視為順時針角度,公式不變。

2. 掃描線填充算法(前向映射)

  • 取得源圖 getRGB() 像素陣列 srcMap[w*h]。
  • 計算旋轉後外接正方形邊長:dr = max(dist(Q, 四頂點)),新圖尺寸 wh2 = 2*dr+1。
  • 分配目標陣列 destMap[wh2*wh2],初始化透明 (0x00000000)。
  • 遍歷源圖每像素 (i,j),計算目標座標 (destX, destY),寫入 destMap。
  • 偏移量 dd[0]=cx-dr, dd[1]=cy-dr,繪製時補償。

3. MIDP 2.0 參考實現 (CLDC 1.1)


public Image rotate(Image src, int cx, int cy, double theta, int[] dd) {
    if (Math.abs(theta % 360) < 0.1) return src;
    int w = src.getWidth(), h = src.getHeight();
    int[] srcMap = new int[w*h];
    src.getRGB(srcMap, 0, w, 0, 0, w, h);

    int dx = Math.max(cx, w-cx), dy = Math.max(cy, h-cy);
    int dr = (int)Math.sqrt(dx*dx + dy*dy);
    int wh2 = 2*dr + 1;
    int[] destMap = new int[wh2*wh2];

    double rad = theta * Math.PI / 180;
    double cos = Math.cos(rad), sin = Math.sin(rad);

    for (int i=0; i> 24) != 0) { // 非透明
                int destX = (int)(dr + (i-cx)*cos - (j-cy)*sin);
                int destY = (int)(dr + (j-cy)*cos + (i-cx)*sin);
                destMap[destY*wh2 + destX] = rgb;
            }
        }
    }
    dd[0] = cx - dr; dd[1] = cy - dr;
    return Image.createRGBImage(destMap, wh2, wh2, true);
}

4. 失真問題與對策

前向映射導致多源像素映射同一目標、或目標空洞。對策:

  • 源圖放大 N 倍 → 旋轉 → 縮小 (超採樣)
  • 邊界馬賽克填充 / 雙線性插值(計算量大)
  • 反向映射(目標遍歷反算源座標,雙線性插值)品質最佳但計算重

5. CLDC 1.0 移植:定點數

引入 MathFP (Onne Hommes) 庫,int 模擬定點數:


int xFP = MathFP.toFP("0.10");
int yFP = MathFP.toFP("0.2");
int zFP = MathFP.mul(xFP, yFP);
System.out.println(MathFP.toString(zFP)); // 0.02

三角函數預計算表 (0-359°):


static int[] cosFP = new int[360], sinFP = new int[360];
for (int i=0; i<360; i++) {
    int radFP = MathFP.div(MathFP.mul(MathFP.toFP(i), MathFP.PI), MathFP.toFP(180));
    cosFP[i] = MathFP.cos(radFP);
    sinFP[i] = MathFP.sin(radFP);
}
// 使用時:cosFP[theta], sinFP[theta]

精度優化:僅存 0-90°,利用對稱性推導;或存 sin*4096 整數,運算後右移 12 位。

6. Sprite 整合旋轉

  • Sprite 單幀圖需先 getFrame() 截取單幀 Image。
  • 對單幀應用上述旋轉算法,產生新 Image 替換該幀或動態繪製。
  • 碰撞檢測必須用 pixelLevel=true,因旋轉後邊界框變大。

應用例:「淘金者」鉤子 0-60° 連續旋轉(圖 5)。

7. 替代方案比較

方案優點缺點適用場景
實時算法旋轉任意角度、動態、無額外存儲CPU 重、失真處理麻煩、大圖記憶體峰值高連續旋轉動畫(指針、鉤子、轉盤)
預置圖片 (含 Sprite 90° 變換)零運行時運算、品質可控、Sprite 90° 免費角度多時存儲大、美術成本高固定角度集(坦克 12 方向、角色 8 方向)
TinyLine 2D (純 Java, <35KB)支援向量/光柵旋轉、CLDC 1.0、API 豐富第三方依賴、學習成本需複雜 2D 圖形渲染、不想造輪子

8. 局限性總結

  • 生成大圖 (外接正方形),固定旋轉點連續旋轉適合,頻繁換點/單次旋轉浪費大。
  • 需 MIDP 2.0 (getRGB/createRGBImage) 與 Alpha 通道支援。
  • MIDP 1.0 需廠商擴展包 (如 Nokia DirectUtils)。
  • Sprite 碰撞必須像素級。

結語

無銀彈,視專案選型:連續旋轉用實時算法+超採樣;固定角度用預置圖+Sprite 90° 優化;複雜 2D 場景引入 TinyLine。掌握數學本質與平台限制,才能在資源受限設備上做出流暢旋轉效果。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved