J2ME 標準僅支援 PNG 解碼,**無任何圖片編碼器**。若需將運行時生成/修改的圖片持久化或網絡傳輸,BMP 是最簡單的無壓縮格式選擇。本文提供基於 LGPL 的 BMPEncoder 實現,解決 Endian、文件頭結構、行填充對齊等坑點。
DataOutputStream、Image.getRGB()、基本整數運算。
public class BMPEncoder {
public static byte[] encode(Image img) throws IOException {
int w = img.getWidth(), h = img.getHeight();
int[] rgb = new int[w];
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
// 1. 計算行大小 (4 字節對齊)
int rowSize = ((w * 3 + 3) / 4) * 4; // 24-bit BGR
int dataSize = rowSize * h;
int fileSize = 14 + 40 + dataSize;
// 2. 寫文件頭 (BITMAPFILEHEADER, 14 bytes)
dos.writeShort(0x4D42); // 'BM'
dos.writeInt(fileSize); // 文件大小 (Little Endian)
dos.writeShort(0); dos.writeShort(0); // 保留
dos.writeInt(14 + 40); // 數據偏移
// 3. 寫信息頭 (BITMAPINFOHEADER, 40 bytes)
dos.writeInt(40); // 信息頭大小
dos.writeInt(w); dos.writeInt(h); // 寬高
dos.writeShort(1); // 平面數
dos.writeShort(24); // 位深 (24-bit)
dos.writeInt(0); // 無壓縮
dos.writeInt(dataSize); // 圖像數據大小
dos.writeInt(2835); dos.writeInt(2835); // DPI (72dpi = 2835 px/m)
dos.writeInt(0); dos.writeInt(0); // 調色板顏色數/重要顏色數
// 4. 寫像素數據 (底行在前,BGR 順序,行填充)
for (int y = h - 1; y >= 0; y--) {
img.getRGB(rgb, 0, w, 0, y, w, 1);
for (int x = 0; x < w; x++) {
int pixel = rgb[x];
dos.writeByte(pixel & 0xFF); // B
dos.writeByte((pixel >> 8) & 0xFF); // G
dos.writeByte((pixel >> 16) & 0xFF); // R
}
// 行填充
for (int p = w * 3; p < rowSize; p++) dos.writeByte(0);
}
dos.flush();
return bos.toByteArray();
}
}
| 坑點 | 說明 | 解決 |
|---|---|---|
| Endian | BMP 規範強制 Little Endian | DataOutputStream 默認 Big Endian,**必須手動寫 Little Endian** (如上代碼用 writeShort/writeInt 實際為 Big Endian,**錯誤!** 正確需自寫 writeIntLE 或用 ByteBuffer.order(ByteOrder.LITTLE_ENDIAN) — J2ME 無 ByteBuffer,只能手寫移位寫入) |
| 行填充 | 每行必須 4 字節對齊 | rowSize = ((w*3+3)/4)*4,不足補 0 |
| 行序 | BMP 底行在前 (y=h-1 先寫) | 循環 for (y=h-1; y>=0; y--) |
| 像素序 | BGR (非 RGB),無 Alpha | 藍綠紅順序寫入,忽略 Alpha 通道 |
| 有符號/無符號 | Java byte 有符號,寫入時 & 0xFF 強制無符號 | 見代碼 dos.writeByte(pixel & 0xFF) |