在 Android 上產生 BMP 格式的圖檔

熟悉 Android 上 Bitmap 的讀者一定都知道在 Android 上面要存影像檔案只有三種格式可以選擇:JPEG / PNG / WEBP,其中 WebP 是在 Android4.0 之後才加入,所以原本的存檔選擇很單純,想要快速並且檔案小的就選擇 JPEG,有需要盡量保留圖片品質或是需要保留圖片 alpha channel 的話就選擇 PNG,但缺點就是壓縮效能不好。WebP  則是由 Google 所提出號稱同畫質狀況下檔案比 JPEG 或  PNG 還要小,而且是支援 Alpha 的圖檔格式(但實際在 Android 上測試似乎對於帶有透明的 WebP 圖檔解碼之後並不如預期)。

因此如果你想要在 Android 上將 Bitmap 存檔為其他影像格式的話大概只能自己來了。

Skia Solution

Android 平台上底層對於影像格式的處理倚賴的是外部的 Skia 函氏庫,Skia 原本是一家獨立的公司所擁有的,但 2005 年 Google 併購它之後就把 Skia 開源了!目前 Skia 被使用在著名的 Mozilla FireFox, Chrome 系列,當然還有 Android。而 Skia 的 Encoder / Decoder 架構上的設計簡單的來說是一種 Plug-In 的架構,它已經把 Encoder / Decoder 界面定義好了,如果需要新增一個 Encoder 的話就是繼承 SKImageEncoder 然後透過 SkTRegistry 把自己定義的 encoder 掛進  codec list 中,之後就能夠透過 Bitmap.Compress 的 API 來作 encode 了。但這個方法只適用會自己編譯 Android Framework 然後還要會把 Skia 換掉的人。

Java Solution

我門還是回到一般 Android Application 開發者比較有可能使用的方法,直接以 Java 實作 BMP encode。首先我們必須了解 BMP 的檔案格式是很單純的由一個 File Header + DIB Header + Pixel Data,詳細的格式在 Wiki 上有很詳盡的介紹。在 File / DIB header 的部分我從網路上有找到有人實作的 Pure Java solution,稍微修改為如下:
很簡單,沒什麼問題,唯一需要注意的是 BMP 的資料儲存格式是 Little Endian,所以在寫出 header 中的各欄位時必須做一次 Byte Order 的轉換。 之後就可以輸出 Pixel Data了,Android 的 Bitmap object 的 pixel 部份預設是以 ARGB8888 的格式存在記憶體中,但是很不幸的  BMP Spec 中定義的 Pixel format 是 RGBA,也就是說我們必須針對每一個 pixel 做一次 Byte Re-order。當你全部都做完存檔之後,把 BMP 打開,很可能會出現的一個問題是圖檔可能上下顛倒了,原因是因為 BMP 的 Scan Line 是從下到上,因此除了做 pixel re-order 之外還要記得要從圖檔最底下往上做 Pixel Scan  然後再輸出就會是對的了。輸出 Pixel Data 的片段如下:
或者是一個更簡單的方法是把 DIB header 中的高度設為 -height 之後,就不需要做 scan line reverse 了。

結論

以上完整的 Android Sample Code 小弟放了一份在 Github 上,有興趣的話可以參考一下。BMP 的格式由於需要做許多 Byte Re-order 而且檔案又很大,所以產生 BMP 圖檔的效能並不好(難怪 Android 沒支援),而且現在也沒有什麼情境是非要使用 BMP 的,所以沒有必較應該就別用了,唯一的用途大概就是拿來做圖檔應用的 bench mark 的吧 🙂

 

 



No Responses

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *