早期 J2ME 無硬體加速,精靈旋轉需純軟體實作。本文提供一個基於查表法正弦/餘弦 + 雙線性插值的通用 Sprite.rotate(count) 實作,可離線預生成 360 度內 count 幀旋轉圖,執行時直接播放,效能穩定。
public class Sprite {
public short[][] pixels; // 每幀像素陣列 (ARGB 4444 打包於 short)
public short width, height; // 單幀尺寸
public byte numOfFrame; // 總幀數
public int curFrame; // 當前幀
// 正弦表:0~359 度 × 256 定點數 (sin × 256)
public final static int[] SIN_TABLE = {
0, 4, 8, 13, 17, 22, 26, 31, 35, 39, 44, 48, 53, 57, 61, 65, 70, 74, 78, 83,
87, 91, 95, 99, 103, 107, 111, 115, 119, 123, 127, 131, 135, 138, 142, 146,
149, 153, 156, 160, 163, 167, 170, 173, 177, 180, 183, 186, 189, 192, 195,
198, 200, 203, 206, 208, 211, 213, 216, 218, 220, 223, 225, 227, 229, 231,
232, 234, 236, 238, 239, 241, 242, 243, 245, 246, 247, 248, 249, 250, 251,
251, 252, 253, 253, 254, 254, 254, 254, 254, 255, 254, 254, 254, 254, 254,
253, 253, 252, 251, 251, 250, 249, 248, 247, 246, 245, 243, 242, 241, 239,
238, 236, 234, 232, 231, 229, 227, 225, 223, 220, 218, 216, 213, 211, 208,
206, 203, 200, 198, 195, 192, 189, 186, 183, 180, 177, 173, 170, 167, 163,
160, 156, 153, 149, 146, 142, 138, 135, 131, 127, 123, 119, 115, 111, 107,
103, 99, 95, 91, 87, 83, 78, 74, 70, 65, 61, 57, 53, 48, 44, 39, 35, 31,
26, 22, 17, 13, 8, 4, 0, -4, -8, -13, -17, -22, -26, -31, -35, -39, -44,
-48, -53, -57, -61, -65, -70, -74, -78, -83, -87, -91, -95, -99, -103, -107,
-111, -115, -119, -123, -127, -131, -135, -138, -142, -146, -149, -153, -156,
-160, -163, -167, -170, -173, -177, -180, -183, -186, -189, -192, -195, -198,
-200, -203, -206, -208, -211, -213, -216, -218, -220, -223, -225, -227, -229,
-231, -232, -234, -236, -238, -239, -241, -242, -243, -245, -246, -247, -248,
-249, -250, -251, -251, -252, -253, -253, -254, -254, -254, -254, -255, -254,
-254, -254, -254, -254, -253, -253, -252, -251, -251, -250, -249, -248, -247,
-246, -245, -243, -242, -241, -239, -238, -236, -234, -232, -231, -229, -227,
-225, -223, -220, -218, -216, -213, -211, -208, -206, -203, -200, -198, -195,
-192, -189, -186, -183, -180, -177, -173, -170, -167, -163, -160, -156, -153,
-149, -146, -142, -138, -135, -131, -127, -123, -119, -115, -111, -107, -103,
-99, -95, -91, -87, -83, -78, -74, -70, -65, -61, -57, -53, -48, -44, -39,
-35, -31, -26, -22, -17, -13, -8, -4
};
// ... 建構子、常數略
}
rotate(int count)輸入:欲預生成的幀數 count(如 16、32)。輸出:新 Sprite,包含 count 幀旋轉後圖像。
radius × 2(正方形)。frame = 0 ~ count-1:
angle = frame * (360 / count)。sin = SIN_TABLE[angle] << 8、cos = SIN_TABLE[(angle+90)%360] << 8(定點數 8.8 格式)。project()private final short project(short[] src, int srcW, int srcH,
int u, int v) {
// u, v: 定點數 16.16 (整數部 16 bits, 小數部 16 bits)
int uInt = (u >> 16) % srcW; if (uInt < 0) uInt += srcW;
int vInt = (v >> 16) % srcH; if (vInt < 0) vInt += srcH;
int uFrac = (u >> 8) & 0xFF; // 0~255
int vFrac = (v >> 8) & 0xFF;
// 取四鄰域像素 (ARGB 4444)
int p00 = src[vInt * srcW + uInt];
int p10 = src[vInt * srcW + (uInt+1)%srcW];
int p01 = src[((vInt+1)%srcH) * srcW + uInt];
int p11 = src[((vInt+1)%srcH) * srcW + (uInt+1)%srcW];
// 各通道獨立插值
int a = lerp4(p00>>12, p10>>12, p01>>12, p11>>12, uFrac, vFrac);
int r = lerp4(p00>>8&0xF, p10>>8&0xF, p01>>8&0xF, p11>>8&0xF, uFrac, vFrac);
int g = lerp4(p00>>4&0xF, p10>>4&0xF, p01>>4&0xF, p11>>4&0xF, uFrac, vFrac);
int b = lerp4(p00&0xF, p10&0xF, p01&0xF, p11&0xF, uFrac, vFrac);
return (short)((a<<12)|(r<<8)|(g<<4)|b);
}
// 簡化雙線性插值權重計算
private int lerp4(int v00, int v10, int v01, int v11, int fx, int fy) {
int wx1 = 256 - fx, wy1 = 256 - fy;
return (v00*wx1*wy1 + v10*fx*wy1 + v01*wx1*fy + v11*fx*fy) >> 16;
}
原始碼中以展開式計算四通道,此處封裝為 lerp4 便於理解。
draw(Graphics, x, y, manipulate) 內部呼叫 drawPixels,支援 Nokia 專有 manipulate 參數(翻轉、旋轉 90/180/270),並以 drawLine 掃描線繪製加速(連續相同像素合併)。
rotate(16/32),將結果快取;執行時僅播放幀,零運算負擔。radius = (short)Math.sqrt(w*w + h*h)/2 + 1。以查表法避開 Math.sin/cos 浮點運算,配合定點數雙線性插值,實現無浮點、全機型相容的精靈旋轉。此技術在早期 J2ME 射擊、競速遊戲中廣泛應用。