もしかするとMono for Androidについて、今は、Xamarin.Androidという名称かもしれない。
はじめに
作ろうと思っているアプリで、初期データとしてTGAがあり、それを読み込みAndroidで表示するためにはどうすればいいかという問題に対して、作ればいいじゃないということで、TGAからBitmapに変換するライブラリを書きました。
その時の実装を思い出しての個人的なメモです。
概要
実装したものは、以下のTGAファイルをBitmapファイルとして返すものです。
色深度8,16,24,32bitおよびRLE圧縮、非圧縮およびデータの格納方式(上→下、下→上)に対応しました。
openmetaverse.orgのOpenMetaverse.Imaging.TGALoaderを元にした実装しました。
Mono for AndroidではSystem.Drawing.Imaging.BitmapDataがないため、ほぼ書き換えになりました。
処理の流れ
処理内容としては、
- TGAファイルの読み込み
- TGAのヘッダを読み込み
- データ部の読み込み
- TGAのヘッダより判定し、データ部をBitmapのデータ形式に変更する
- カラーマップがある場合は、Bitmapのカラーパレットに変換
- BitmapのヘッダとBitmapの形式に変更したデータ部を結合
- Bitmapファイルとして返す
を行いました。
TGAヘッダの読み込み
まず、TGA形式として定義されているものを確認します。
http://www.openspc2.org/format/TGA/
を参考にしました。
TGALoader.csのヘッダの取得をほぼ使用しました。
形式の判定
取得したヘッダの値より、以下の場合分けをします。
色深度 | PixelDepth | 8,16,24,32 |
RLE圧縮の有無 | RleEncoded | boolean |
データの格納方式 | BottomDown | boolean |
カラーマップの有無(PixelDepth==8の場合のみ) | colorMap | boolean |
カラーマップ
色深度が8bitの場合のみの対応となります。
256色(8bit)の場合、カラーマップの有無をチェックします。
もしカラーマップがあったら、そのままbitmapのカラーマップに入れてあげます。
この場合、BitmapのカラーパレットはRGBQUADなので、RとGとBと0を入れる必要があります。
そのため、4で割れない24bitには割れるように4番目に0を入れてあげます。
また、カラーマップがない場合は、グレースケールなので、Bitmapカラーマップ向けにグレースケールの
カラーパレットを作成してあげます。
ソースは以下になりました。
static byte[] getColorMap (tgaHeader hdr, System.IO.BinaryReader br) { byte size = hdr.ColorMap.EntrySize; byte[] colorMap = new byte[256*4]; byte[] tgacolorMap = new byte[256*4]; //colormapも持っているか? if (hdr.ColorMapType == 0x01) { //ヘッダより先のカラーマップの読み取り br.Read (tgacolorMap, 0, (int)size*32); switch (size) { case 0x20: //32 colorMap = tgacolorMap; break; case 0x18: //24 for (int i= 0; i< 256; i++) { colorMap [i * 4] = tgacolorMap [i * 3]; colorMap [i * 4 + 1] = tgacolorMap [i * 3 + 1]; colorMap [i * 4 + 2] = tgacolorMap [i * 3 + 2]; colorMap [i * 4 + 3] = 0; } break; case 0x10: colorMap = tgacolorMap; break; case 0x09: //not implimented break; default: break; } } else { //grayscale if (size == 0x00) { for (int j=0; j<256; j++) { colorMap [j * 4] = (byte)(j + (j << 8) + (j << 16) + (j << 24)); colorMap [j * 4 + 1] = (byte)(j + (j << 8) + (j << 16) + (j << 24)); colorMap [j * 4 + 2] = (byte)(j + (j << 8) + (j << 16) + (j << 24)); colorMap [j * 4 + 3] = 0; } } } return colorMap ; }
RLE圧縮されている場合
http://ja.wikipedia.org/wiki/%E9%80%A3%E9%95%B7%E5%9C%A7%E7%B8%AE
の
Switched Run Length Encodingを参考にし、理解しました。
実装は、オリジナルのTGALoaderでされていたので、改修したところとの整合性を取りました。
格納方向について
Bitmapは下から上へが標準なので、それを基準にして、上から下の場合を行を逆順に読み込むようにしました。
System.Drawing.Imaging.BitmapDataがない対応
BitmapDataの振る舞いに似たSystem.IO.MemoryStreamを継承したクラスを作成しました。
Bitmap化
http://ja.wikipedia.org/wiki/Windows_bitmap
にならって、取得しているTGAのヘッダデータを利用しbitmap形式のヘッダを作成しました。
カラーマップがある場合は、カラーパレットを作成しました。
それらと変換したデータ部を結合し、
BitmapFactory.DecodeByteArrayを使用してbitmapとして返すようにしました。
最後に
変換速度について計測してません。もしかするともっと早く効率よくできるかもしれません。
時間があれば見直したいです。