読者です 読者をやめる 読者になる 読者になる

なんとなく

誰得感満載な記事が多いかも。Mono関係とLinuxのサーバ関係、レビューとか。

Mono for AndroidでTGAをBitmapに変換するもの実装したメモ

もしかするとMono for Androidについて、今は、Xamarin.Androidという名称かもしれない。

はじめに

作ろうと思っているアプリで、初期データとしてTGAがあり、それを読み込みAndroidで表示するためにはどうすればいいかという問題に対して、作ればいいじゃないということで、TGAからBitmapに変換するライブラリを書きました。
その時の実装を思い出しての個人的なメモです。

概要

実装したものは、以下のTGAファイルをBitmapファイルとして返すものです。
色深度8,16,24,32bitおよびRLE圧縮、非圧縮およびデータの格納方式(上→下、下→上)に対応しました。
openmetaverse.orgのOpenMetaverse.Imaging.TGALoaderを元にした実装しました。

https://github.com/openmetaversefoundation/libopenmetaverse/blob/master/OpenMetaverse/Imaging/TGALoader.cs

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として返すようにしました。

最後に

変換速度について計測してません。もしかするともっと早く効率よくできるかもしれません。
時間があれば見直したいです。